carsten-klaffke / send-intent

Repository for send-intent Capacitor plugin
MIT License
106 stars 12 forks source link

App crash after SendIntent.finish() #69

Closed Ryner47 closed 1 year ago

Ryner47 commented 1 year ago

Describe the bug Hello, on ionic/android I'm implementing this plugin but when I execute the recommended 'SendIntent.finish()' after intent and routing to the desired page are executed successfully, the app crashes closing itself. What could this be due to?

carsten-klaffke commented 1 year ago

Hello! Did you configure your intent filter in your MainActivity or in a separate activity? "finish()" closes the current activity. So if you call that, you should be in a separate activity. I recommend using a separate activity for handling intents, because otherwise it can lead to app state problems (intents might be re-executed after your activity returns after idle time).

Ryner47 commented 1 year ago

Hello, on ionic app side there is no 'activity' concept, so it would be useful understand in which phase of the lifecycle of the ionic components execute 'SendIntent.finish()'. Maybe in 'ngOnDestroy' function of the main component? Thanks

carsten-klaffke commented 1 year ago

You should call it after your share workflow is finished, whenever that is. E.g. in my app this is after a user edited the content and the save operation completed. But be aware that "finish" does close the current instance (and if it wouldn't, you would have two app instances running in parallel).

luisleagues commented 1 year ago

Is there a way to share a file to the already running App and not start a new instance? I want the user to stay in the App after they shared the file.

carsten-klaffke commented 1 year ago

Yes, you could just configure the intent-filters in your MainActivity instead of the SendIntentActivity. I think you would need to call registerPlugin(SendIntent.class); in your create-Method, but that should be it. But as I said, this can lead to some strange app state issues that can occur after idle mode (Android might re-execute the share-intent to restart the app).

luisleagues commented 1 year ago

That works! Can you also share a file with the App when it is already running? SendIntent.checkSendIntentReceived() works when the App is closed, but when the App is already running it doesn't received anything. This issue seems to indicate that that doesn't work: https://github.com/carsten-klaffke/send-intent/issues/61#issuecomment-1316625453.

carsten-klaffke commented 1 year ago

You can do this with an event/listener pattern. This is the "old way" this plugin used to work (you can see it in the history of readme). But I switched to the separate activity implementation due to the state problems which weren't solvable for me (and which appears cleaner to me, because you don't steal another apps context).

Dispatch an event in your MainActivity like this:

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    String action = intent.getAction();
    String type = intent.getType();
    if ((Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) && type != null) {
        bridge.getActivity().setIntent(intent);
        bridge.eval("window.dispatchEvent(new Event('sendIntentReceived'))", new ValueCallback<String>() {
            @Override
            public void onReceiveValue(String s) {
            }
        });
    }
}

And register a listener in your app:

window.addEventListener("sendIntentReceived", () => {
   Plugins.SendIntent.checkSendIntentReceived().then((result: any) => {
        if (result) {
            // ...
        }
    });
})
luisleagues commented 1 year ago

Thanks again, I really appreciate it :). I'll do it this way and hope I won't get any state problems for now.

daniandl commented 1 year ago

Yes, you could just configure the intent-filters in your MainActivity instead of the SendIntentActivity. I think you would need to call registerPlugin(SendIntent.class); in your create-Method, but that should be it. But as I said, this can lead to some strange app state issues that can occur after idle mode (Android might re-execute the share-intent to restart the app).

So a cleaner way would be to catch the finish event and then re-open the main activity? Maybe with a deep link so we can redirect the user with context? Is this possible?

carsten-klaffke commented 1 year ago

Hey daniandl, using a deep link could work, but maybe that will not give you a fluid user experience. I'm sorry but I didn't find a clean solution for staying in the app after a send-intent yet. This is somewhat of an Android problem. I was even able to get Whatsapp crashing this way.

chiraganand commented 1 year ago

Hey daniandl, using a deep link could work, but maybe that will not give you a fluid user experience. I'm sorry but I didn't find a clean solution for staying in the app after a send-intent yet. This is somewhat of an Android problem. I was even able to get Whatsapp crashing this way.

I was having the same problem of going back to the main activity of my app after performing the action. I tried a solution which seems to work for my use case. The UX isn't really fluid mainly because of the back animation by the OS, it should have been front.

I noticed that WhatsApp does a similar thing: creating a new activity for an inward share and then probably using a deep-link to the main activity (the sharing activity is destroyed the moment you press send button). Plus, it has been able to achieve forward animation for move to the main activity even if it was already active in the background.

This is the method I added to SendIntent.java:

@PluginMethod
public void openMainActivity (PluginCall call) {
    Activity currentActivity = bridge.getActivity();
    Intent intent = currentActivity.getParentActivityIntent();
    intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); // doesn't seem to work
    currentActivity.startActivity(intent);
    call.resolve(new JSObject()); // not sure if this is needed?
}

AndroidManifest.xml:

<activity
    android:name="de.mindlib.sendIntent.SendIntentActivity"
    android:parentActivityName="MainActivity"
    ...
</activity>

@carsten-klaffke Thanks for creating this plugin, it is very useful. If you think the above function could be of wider use to other users then let me know I will be happy to create a PR!

daniandl commented 1 year ago
@PluginMethod
public void openMainActivity (PluginCall call) {
    Activity currentActivity = bridge.getActivity();
    Intent intent = currentActivity.getParentActivityIntent();
    intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); // doesn't seem to work
    currentActivity.startActivity(intent);
    call.resolve(new JSObject()); // not sure if this is needed?
}

Thanks for sharing. I ended up just using the capacitor AppLauncher package and launching the app before calling .finish(). This uses deep-links and verified domain association, which requires some setup.

if (this.$platform === 'android') {
  await AppLauncher.openUrl({
    url: `${CONFIG.appLaunchUrl}/bookmarks`,
  }).catch(console.error)
}
chiraganand commented 1 year ago

Thanks for sharing. I ended up just using the capacitor AppLauncher package and launching the app before calling .finish(). This uses deep-links and verified domain association, which requires some setup.

if (this.$platform === 'android') {
  await AppLauncher.openUrl({
    url: `${CONFIG.appLaunchUrl}/bookmarks`,
  }).catch(console.error)
}

This actually is a simpler solution than writing native code.

carsten-klaffke commented 1 year ago

Very nice @chiraganand and @daniandl ! I mentioned your solution in the Readme. Thank you, I think this will help many developers!