Skip to content
This repository has been archived by the owner on Oct 1, 2018. It is now read-only.

Deep linking loads multiple instances of app on android #37

Closed
mklilley opened this issue Jan 26, 2016 · 30 comments
Closed

Deep linking loads multiple instances of app on android #37

mklilley opened this issue Jan 26, 2016 · 30 comments
Labels

Comments

@mklilley
Copy link

I am creating my app using Ionic and am testing the app on Android version 5.1.1.

I find that using the plugin does indeed allow the app to be launched when I click on a particular link which is lovely, thanks.

The issue is that if I already have the app open and then I click on a link inside of e.g. whatsapp, then a new instance of the app loads inside of whatsapp instead of using the already open app. This creates a strange user experience because in order to return to whatsapp I have to navigate backwards several times , whereas I would expect to just be able to switch from my app back to whatsapp using the familiar square button that allows you to see all open apps.

Thanks for taking to time to develop this plugin, it is very useful, I am just trying to understand if this is intended behaviour or a bug.

Thanks,

Matt

@nikDemyankov
Copy link
Member

Thanks for the kind words :)

I don't have a whatsapp, but tried to reproduce it with the Inbox app and couldn't. What I did:

  1. Created a new Ionic app.

  2. In the config.xml added:

    <universal-links>
      <host name="sometest.com" scheme="https" />
    </universal-links>
  3. Launched the app on the device: ionic run android --device.

  4. Pressed "home" button, so it would stay alive.

  5. Send myself an email with the http://sometest.com/path/ url in it.

  6. Opened Inbox app and pressed on the link. As a result - application launched.

  7. Pressed back button - returned to the Inbox app.

Also, can navigate back to the Inbox app by clicking on the square button.

Can you please provide a step-by-step guide on how I can try to reproduce it?

then a new instance of the app loads inside of whatsapp instead of using the already open app

Feels like whatsapp opened url link in his inner webview instead of launching your app. Especially if you can't see it in recent apps list. Android app can't launch another application inside of himself.

@mklilley
Copy link
Author

Hi.

Thanks for getting back to me so quickly.

Yes you are right it does appear as if whatsapp is loading it as some kind of internal webview. I guess that is a separate discussion of why it does that.

If I email myself a link in the gmail app (I dont have inbox) I get different behaviour this time. The app loads as a proper app outside of gmail (not in some kind of internal webview). However, if I already have the app open it duplicates the app, so that if i look at my recent apps there are two versions of it open.

Just in case this was something strange i did for my app I did the following

  1. ionic start test tabs
  2. cd test
  3. ionic platform add android
  4. cordova plugin add cordova-universal-links-plugin
  5. Added the following to config.xml after the end of </feature>

<universal-links> <host name="test.com" scheme="https"> </host> </universal-links>
6) ionic run android --device
7) hit home button
8) Go to gmail and click on a link https://test.com
9) Hit the recent apps button to show what you see in the attached image

You see there are two copies. The one that was loaded on step 6 is apparently ignored when i click on the link in gmail.

What do you think?

Thanks again,

Matt
screenshot 26 jan 2016 7_00_00 pm

@TonivdW
Copy link

TonivdW commented Jan 26, 2016

I have also tested this and find that both Whatsapp and Gmail behave the same (Android 4.4). If I click the link then the app opens. In the open apps view, the logo of WhatsApp/Gmail is shown, but the screenshot is that of my app. So I find no difference in behavior between Gmail and WhatsApp.
(I was about to open an issue for the following, but I think it just as well fits in this issue) What I find strange/unwanted is that when I subsequently tap the WhatsApp or Gmail icon on the homescreen my app is shown. If I navigated within my app 6 pages deep, then I have to press the back button 6 times in order to get back to the app that opened it. I would like the app in which the link is clicked to spawn my app as a seperate process, instead of embedding it within the 'parent' app. Is this something Universal Links can decide, or is this an Android issue?
unnamed

@nikDemyankov
Copy link
Member

@mklilley thanks for the step-by-step guide, that helped.

After some experiments and debugging I found, that this is an Android feature, not a plugin bug. I'll explain why and how it can be changed.

At first I tried to use Inbox app. I launched my TestApp, then pressed home button, then in the Inbox app pressed the link. As a result - existing instance of the TestApp was launched, no new instance is created.

Then I used Gmail app, and suddenly it created a new instance of the TestApp. Which is strange, since application is the same, link is the same. The only thing that has changed is the app, in what the link was pressed. This means, that they are sending different types on Intents to the Android. Intent from the Inbox doesn't lead to creation of the TestApp's new instance.

To confirm that I launched the app in debug mode from the AndroidStudio and placed a break point in the MainActivity onCreate() method. It should be called every time the instance of the application is created. Then I reproduced the steps with Inbox and Gmail. As a result I saw, that for the Gmail version I get the break point hit. So yes - for some reason the Intent from this app creates new instance of the TestApp.

After looking into AndroidManifest.xml of the TestApp I found the following preference:

<activity
            android:name="MainActivity"
            android:launchMode="singleTop"
....>

From the android documentation you can find, that singleTop allows creation of several instances of the activity. And since our launching (MainActivity) is the one that get's targeted by the Intent from the other app - it can be created several times.

To prevent that from happening you can change singleTop to singleInstance. Although, in the docs:

standard is the default mode and is appropriate for most types of activities. SingleTop is also a common and useful launch mode for many types of activities. The other modes — singleTask and singleInstance — are not appropriate for most applications, since they result in an interaction model that is likely to be unfamiliar to users and is very different from most other applications.

But in the case of Ionic/Cordova apps, which consist only of one activity and all navigation is executed within the WebView - that can be fine. But make sure to test it carefully.

@nikDemyankov
Copy link
Member

@TonivdW I think in your case the problem is the same, as I described above. This is an Android behaviour, not a plugin thing. In some way - yes, plugin can be considered as responsible for that, since it add's the feature for launching the app from the link. But it can't control what Intents it receives and if new instance of the app is created.

As an experiment, you can try to add android:allowTaskReparenting="true" to the AndroidManifest.xml:

<activity
  android:name="MainActivity"
  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale"
  android:label="@string/activity_name"
  android:launchMode="singleInstance"
  android:allowTaskReparenting="true"
  android:theme="@android:style/Theme.Black.NoTitleBar"
  android:windowSoftInputMode="adjustResize">

@mklilley
Copy link
Author

@nikDemyankov This works like a charm, thank you so much! It is thanks to people like you that people like me can get anywhere with making apps.

You are a star!

Matt

@nikDemyankov
Copy link
Member

@mklilley thanks :)

@TonivdW
Copy link

TonivdW commented Jan 27, 2016

@nikDemyankov My testing shows that setting allowTaskReparenting is not needed. I switched android:launchMode from the default (singleTop) to what you mentioned above (singleInstance) and that alone did the trick. The app is now started outside the context of the calling app just like it should (for my purpose)
Like @mklilley I want to thank you for writing this fantastic plugin!

@nikDemyankov
Copy link
Member

@TonivdW Thanks :) I'll update the readme, so if someone will hit the same issue - they would know why and how to fix it. Created an issue for that so I would not forget. Leaving this one open for the newcomers, will close it when the docs are updated.

@TonivdW
Copy link

TonivdW commented Feb 3, 2016

Unfortunately I encountered and issue with changing the lauchmode to singleInstance...
I also use phonegap-plugin-push to receive push notifications in my app, and when the app has singleInstance set, it will not be brought to the foreground upon receiving a notification. The app works ok, and receives and handles the notification, but stays in the background.
I have now changed it back to SingleTop and then push notifications start working again, but then Universal Links starts misbehaving again of course.
Do you think the author of phonegap-plugin-push can change something to their plugin to change this behavior?

@nikDemyankov
Copy link
Member

Don't know, I think you should ask him... But my guess, is that it will require some experiments with PushHandleActivity or GCMIntentService.

@TonivdW
Copy link

TonivdW commented Feb 4, 2016

The reason I was asking you is that the universal links plugin does manage the app to be brought to the foreground, even if the setting is singleInstance, so I was guessing your code also has to actively make that happen. Or is it the OS that is doing this? In that case the OS apparently is handling push notifications differently than universal links.
(But I will also open an issue in the push plugin queue.)

@nikDemyankov
Copy link
Member

UL plugin doesn't open the app. This is OS doing. It only handles the intent, that was used to launch the app. And by "handles" I mean it just read's information from it and nothing more.

In the case of the push notification it is a bit different. When user clicks on the notification - plugin launches PushHandleActivity, which handles the notification, and then launches MainActivity , if needed. So if something can be done regarding all of that - then it's in there.

But if I'm wrong - please, point me where to change and what :)

@TonivdW
Copy link

TonivdW commented Feb 5, 2016

I did some further testing and it seems that if I use singleTask for lauchMode, both UL and plugin-push work like I would like it to work.
(Now I hope nobody is going to shout "No! dont't do that, because...")

@nikDemyankov
Copy link
Member

singleTask, not a singleInstance? Sooo, basically, no changes are needed in the AndroidManifest?

@TonivdW
Copy link

TonivdW commented Feb 5, 2016

The original value was singleTop.

@nikDemyankov
Copy link
Member

Ah, yes, you are right. Great :)

I think it should be fine. But some extra testing is always a good idea.

@mattwoberts
Copy link

@nikDemyankov Thanks for the awesome work on this :)

And @TonivdW, thanks for basically doing my work for me - I had the same issue with push notifications, singleTask is working well for me too!

👍

@nikDemyankov
Copy link
Member

@mattwoberts great, thanks :)

@mohamed-ahmed
Copy link
Contributor

I have this plugin ( version 1.1.2) installed on top of Ionic. I'm trying to change android:launchMode to single instance as recommended in thees comments but building always reverts launchMode to single top. How can I make it not revert back to that?

@nikDemyankov
Copy link
Member

On every build Ionic rewrites platforms/android/AndroidManifest.xml file. And according to documentation you can only add stuff to it, not replace. To fix that - you can use custom after_prepare hook.

  1. In your project's config.xml define the hook:

    <platform name="android">
    
      <hook type="after_prepare" src="scripts/afterPrepareHook.js" />
    
      // some other android preferences 
    </platform>
  2. Create scripts/afterPrepareHook.js in your project with the following content:

    var fs = require('fs');
    var path = require('path');
    
    module.exports = function(ctx) {
      var pathToManifest = path.join(ctx.opts.projectRoot, 'platforms', 'android', 'AndroidManifest.xml');
      var manifestContent = fs.readFileSync(pathToManifest, 'utf8');
      manifestContent = manifestContent.replace('singleTop', 'singleInstance');
    
      fs.writeFileSync(pathToManifest, manifestContent, 'utf8');
    };

Haven't tested the JS code above, but you should get the idea on what it does.

@headshota
Copy link

headshota commented Jun 6, 2016

@nikDemyankov is it not enough to just add the preference in config.xml?

<preference name="AndroidLaunchMode" value="singleInstance" />

Reference

@nikDemyankov
Copy link
Member

@headshota yes, you are right. Missed that option in the docs in the first place. That's why suggested a hook.

ghybs added a commit to ghybs/cordova-universal-links-plugin that referenced this issue Jul 4, 2016
Following nordnet#37 and nordnet#38, updated README Cordova config preferences section with a new sub-section to detail how to prevent Android from creating multiple instances of the App when clicking on a link from another App.
@souly1
Copy link

souly1 commented Aug 27, 2016

Word of caution, had issues with the Facebook login when setting:

Using https://github.com/jeduan/cordova-plugin-facebook4
this is also mentioned here:
Wizcorp/phonegap-facebook-plugin#915

@jacquesdev
Copy link

jacquesdev commented Mar 11, 2017

Yup, unfortunately this setting breaks other things like the cordova-plugin-camera. See this error: http://stackoverflow.com/questions/38878963/ionic-cordova-camera-plugin-error-on-android and https://issues.apache.org/jira/browse/CB-3452.

So the question is how can I prevent the issue mentioned here without using
<preference name="AndroidLaunchMode" value="singleInstance" />

@nikDemyankov
Copy link
Member

@jacquesdev I don't think you can... That preference controls exactly that.

@alexnu
Copy link

alexnu commented Jul 4, 2017

Hi @jacquesdev, so did you find a solution for that ? I am in the exact same position where I have to choose whether I want Universal Links to work properly (setting AndroidLaunchMode to singleTop/singleInstance) or Camera to work properly (setting standard).

Is there a way to get both features working at the same time ? I really don't want to have to choose one of the two...

@jacquesdev
Copy link

Unfortunately not :(

I have opted to not use the singleInstance option, on balance it's the only real solution for the moment.

@alexnu
Copy link

alexnu commented Jul 4, 2017

I just found another option singleTask which seems to satisfy both cordova-universal-links-plugin and cordova-camera-plugin.

I have tested it on an a new android phone and on an old android tablet and it works fine so far. I hope it won't cause any new problems in the future, since in the documentation it says "not recommended for general use".

@nordnet-deprecation-bot
Copy link
Contributor

👋 Hi! Thank you for your interest in this repo.

😢 We are not using nordnet/cordova-universal-links-plugin anymore, and we lack the manpower and the experience needed to maintain it. We are aware of the inconveniece that this may cause you. Feel free to use it as is, or create your own fork.

🔒 This will now be closed & locked.

ℹ️ Please see #160 for more information.

@nordnet nordnet locked and limited conversation to collaborators Sep 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

10 participants