darryncampbell / darryncampbell-cordova-plugin-intent

General purpose intent shim layer for cordova appliations on Android. Handles various techniques for sending and receiving intents.
MIT License
86 stars 136 forks source link

plugin should set onload=true #53

Closed robwatt closed 6 years ago

robwatt commented 6 years ago

Hi,

I ran into another issue.

One of the phones I am testing on (Samsung S7) appears to have a very aggressive memory manager. Basically, when an app goes in the background (and a different app is brought to the foreground), it's process is destroyed (even though it appears to remain in the background).

When I share a picture to this app, the intent provided to the app is correct, but it is shared via onNewIntent, instead of getIntent. To me this means the OS remembered that the app is in the background, even though process is dead.

Here are some logs of what I see.

When launching a different app (and my app is in the background)

CordovaActivity: Paused the activity. CordovaActivity: Stopped the activity. CordovaActivity: CordovaActivity.onDestroy()

Then when I share an image with my application I see the following. This is the correct intent I should see.

ActivityManager: START u0 {act=android.intent.action.SEND typ=image/* flg=0xb080001 cmp=ComponentInfo{io.ionic.starter/io.ionic.starter.MainActivity}}

Unfortunately, this intent does not appear to be available to my application (the plugin never receives it). I see this in the logs

CordovaActivity: Apache Cordova native platform version 7.0.0 is starting CordovaActivity: CordovaActivity.onCreate() CordovaActivity: Started the activity. CordovaActivity: Resumed the activity. ... Cordova Intents Shim: Action: getIntent Cordova Intents Shim: getIntentIntent { act=android.intent.action.MAIN flg=0x10000000 cmp=io.ionic.starter/.MainActivity launchParam=MultiScreenLaunchParams { mDisplayId=0 mFlags=0 } }

After doing some debugging in Cordova, I can see that Cordova does indeed get the correct Intent (android.intent.action.SEND), but Cordova receives this intent through onNewIntent(), and as such tries to send this to the plugin BEFORE the plugin has been created by Cordova, through onNewIntent(). This is because the plugin is set to only be created when it is invoked in my app (this is how the plugin was configured).

To fix this I added

<param name="onload" value="true"/>

To the plugin.xml. This will allow Cordova to create the plugin when Cordova is started.

In addition to this, a bit more code (in the same vein as issue #51) is also required. Because Cordova calls onNewIntent before calling execute() we won't have a context. And on top of that, Cordova will call the 'getIntent' action, not 'onNewIntent'.

Now the logs show:

... CordovaActivity: Started the activity. Cordova Intents Shim: onNewIntent: Intent { act=android.intent.action.SEND typ=image/ flg=0x13400001 cmp=io.ionic.starter/.MainActivity launchParam=MultiScreenLaunchParams { mDisplayId=0 mFlags=0 } clip={image/ U:content://0@media/external/images/media/624} (has extras) } CordovaActivity: Resumed the activity. ... Cordova Intents Shim: Action: getIntent Cordova Intents Shim: getIntent => deferredIntent: Intent { act=android.intent.action.SEND typ=image/ flg=0x13400001 cmp=io.ionic.starter/.MainActivity launchParam=MultiScreenLaunchParams { mDisplayId=0 mFlags=0 } clip={image/ U:content://0@media/external/images/media/624} (has extras) }

I have run this against Cordova 6.4, 7.0, and 7.1. There does not appear to be any adverse side effects of having the plugin created when Cordova is started vs creating it on demand.

You can see my changes here (https://github.com/robwatt/darryncampbell-cordova-plugin-intent/commit/7584a11318ec3fdb50f3a73b39de7d317513f8c2). If you agree to them, I will create a pull request.

darryncampbell commented 6 years ago

Hi Rob, when I first looked at the code I was concerned that you had used the same intent (this.deferredIntent) for both getIntent() and onNewIntent() but they are not exactly the same thing as far as Android is concerned. The documentation for the Activity OnNewIntent method states "Note that getIntent() still returns the original Intent". Re-reading the above issue report however, I believe you are saying that this is just how the Cordova framework works anyway? That there is some issue with how the framework handles onNewIntent() / getIntent() and you are just working round it?

robwatt commented 6 years ago

I believe there are 2 issues,

  1. The plugin is not loaded immediately when Cordova is started.

    • This is why the new Intent isn't being received by the plugin.
    • Cordova receives the new Intent from Android via onNewIntent() and then iterating through it's own list of plugins before the application has been initialized, the plugin hasn't been created, and as such, the plugin will never receive the most recent Intent.
  2. The other issue this is working around, is how Cordova handles the events from Android. Although, I am not sure this is Cordova's fault. It could be just how Android sends its events.

    • Android sends the new Intent via onNewIntent, and THEN Cordova calls execute() using the action 'getIntent'. This is a huge problem.
    • We get the new Intent but without a call back context, there is nothing we can do with it. This is similar to the issue I fixed earlier, except it happens on getIntent as well.

This issue only shows up when the application is in the background, but has actually been destroyed, and you try to share something to it. As far as I can tell, this isn't typical of most phones (or at least the ones I have been testing with).

To your points above

darryncampbell commented 6 years ago

Thank you. If you share the pull request, I will merge the change.

darryncampbell commented 6 years ago

Changes have been merged and published to npm as version 1.0.4. Thank you.