Intent receiving in deep-linked-activity - android

I have some problems with proper task stack and bringing to front during deep-linking implementations... I have an Activity declared like this:
<activity
android:name=".activity.ArticleSingleActivity"
android:label="#string/app_name"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http"
android:host="site.com"
android:pathPrefix="/article" />
</intent-filter>
</activity>
It has launchMode="standard" and no parent, because this Activity can be launched from many places in app: native buttons or from WebView's. Even on bottom of almost every instance there are placed related articles, so it might call itself (new instance) and every article should appear in new "window"
And now: when my app is running in background and is visible in app manager deep-linking fires ArticleSingleActivity on top of other activities without a problem. Intent contains these flags:
FLAG_ACTIVITY_BROUGHT_TO_FRONT
FLAG_ACTIVITY_NEW_TASK
FLAG_RECEIVER_FOREGROUND
When app isn't in back stack (lets say removed by user) ArticleSingleActivity is launched without brought_to_front flag and everything starts like usual, but I've found small improper behaviour. When I navigate to another ArticleSingleActivity (e.g. from related on bottom or to any other Activity from side menu), then press home and try to open same deep-linked url then apps is just bring to front. It doesn't show linked article, but Activity which was on top when app was "homed". Also breakpoints aren't firing in onCreate, onActivityReenter or even onNewIntent. In my opinion system "remebers" that it have in back stack launched Acticity matching this url so it just brings to front whole app, but not proper Activity from the stack (no bring_to_front flag like above).
What can I do with this behaviour when I can't even set custom flags for this Intent delivered by system or do any Intent check in onCreate because it isn't called at all in any alive Activity on the stack?

Related

SingleTask activity is created multiple times within the same process

I have a launcher app which has a singleTask activity as the main entry point. When the user navigates away to another activity or to a 3rd party app and then hits the home button, then this activity should be brought to the front. However what I experience is that for the first home button press only, another instance is created instead (a new task is created, onCreate() is called). In the meantime the old task is still alive, containing the original instance of this activity, but it is impossible to navigate back to that task/activity or to bring it to the foreground.
After the first home button press, the next home button press brings the 2nd instance of this activity to the foreground. Not sure why not the very first instance's onNewIntent() method is called for the first time... So this only happens once, after that always the 2nd instance's onNewIntent() method is called. This means that the original activity will be not accessible..
I tried to bring the task to the foreground, nothing was happening... Like if it never existed (but the task is there with the activity, it is not killed at any point). I can find the task from code and also using a shell script. It contains the original activity
This is happening on Android TV (Os: Pie). Any idea what can be the reason for this? I do not really understand how this is happening... BTW the result is the same if I set the activity to singleInstance.
The activity looks like this:
<activity
android:name=".activities.MainActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:theme="#style/AppTheme">
<intent-filter android:priority="2">
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.ALL_APPS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
I tried alternating the above attributes (priority, excludeFromRecents, taskAffinity), also removed them completely, they had no effect...
The home button press sends the following intent:
action: "android.intent.action.MAIN"
category:
"android.intent.category.HOME"
component: the above activity
it has also some extras but I do not think it is relevant
You are experiencing this nasty long-standing Android bug:
Re-launch of Activity on Home button, but...only the first time
You say in a comment that it doesn't happen with real users. That is not actually true. If a real user would install your app from the Play store and then launch it immediately (click the OPEN APP button after installation) then the problem would be reproduced exactly as you describe it.
I was struggling with the exactly same issue for the last couple of hours and I've read many similar Q&A about this topic/bug on StackOverflow. No solution really worked for me until, out of mere curiosity, I did the following.
If you're building a launcher and using onNewIntent() in your MainActivity (although OP doesn't mention that he's using this method), then simply comment out this line:
super.onNewIntent(intent);
Uninstall your app and install it again.
I don't know how this works, but EVEN when you then uncomment the very same line, it still behaves in a proper manner, that is: the app has really only single instance of its main activity all the time. And how I know that? My MainActivity is doing some database operations, and previously I saw in my Logcat that these operations were done twice every time, but now they are done only once.
And BTW my MainActivity tag in AndroidManifest.xml is nothing special:
<activity
android:name=".activities.MainActivity"
android:exported="true"
android:excludeFromRecents="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

How to launch (when the app is not running) or switch (when the app is running) to an android app when clicking a link

My app (a game) has two activities: a launcher activity which shows logo, does initializations and is the main activity of my app, and a client activity which does the 3D rendering and is started by the launcher activity. Thus when the app is running, the back stack is: launcher | client.
What I want with a link is:
if the app has not been started, clicking the link should first start the launcher activity, then the launcher activity start the client activity. Finally the game runs in the client activity.
if the app is running (running in the client activity), clicking the link should switch to and notify the app that the link is clicked (either in launcher or client is ok, better in client), instead of starting another launcher activity.
What I have tried:
I added an intent filter in the declaration of the launcher acitivity:
<activity android:name=".Launcher" android:label="TestApp">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="myapp" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
<activity android:name=".Client" android:label="TestApp">
</activity>
And the intent filter works when I click the link: myapp://, the launcher activity is started. But the problem is the launcher activity is started in the browser's task. If I already have an app running, there will be a new instance of my app started in the browers back stack, instead of switching to the already running app.
Then I tried add the attribute android:launchMode="singleTask" into the activity declaration. Then clicking the link will start a new task and the Launcher activity is started in that task. And if the app has already running, the running task is switched to foreground, that's what I expect. But the problem is, the back stack of the app task is changed from "launcher | client" to "launcher", the client activity is gone.
I hope that there is only one instance of my app. Cliking a link should start the app if the app is not running, otherwise switch to the app without affecting the app's current running states.
I searched a lot but don't find a solution, please help me, thanks.
Click here for app links assistant

Android handling click on notification

I have this situation in my app: I have these activities
<activity
android:name=".presentation.view.start.view.activity.StartActivity"
android:screenOrientation="sensorPortrait"
android:theme="#style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".presentation.view.main.view.activity.MainActivity"
android:configChanges="locale"
android:screenOrientation="sensorPortrait"
android:theme="#style/AppThemeMultiStep"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".presentation.view.firstScreen.view.activity.FirstScreenActivity"
android:screenOrientation="sensorPortrait"
android:theme="#style/AppThemeMultiStep"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".presentation.view.signup.view.activity.LoginViaPinActivity"
android:screenOrientation="sensorPortrait"
android:theme="#style/AppThemeMultiStep" />
StartActivity acts as a sort of "router" based on the application state:
If I am not logged in, it will launch FirstScreenActivity
If I am logged in, it will launch LoginViaPinActivity, which will login the user based on some logic and then launch MainActivity. At the end of all, MainActivity will be at the root of the activities stack.
At some point the app will receive a notification, and when I tap it I want this:
if the app is running and MainActivity is running, open MainActivity (this is easy, there are planty of ways I can to that with various flags) but if it's not running launch StartActivity (so that I can open the app based on all the startup logics implemented there).
So the options I can think of are:
know if an activity is running in order to fire an intent or another (I don't like static fields solutions like you read in many SO post related to this)
make StartActivity the root of the task and find a combination of intent flags which will act like "launch StartActivity, but if it is running at the root of a task, bring that task to front" (this would be my preferred option if possible)
any suggestion is very appreciated
How do you usually handle this kind of situations? (I don't think I'm the first in the world with this problem :) )
Here is my approach -
Make StartActivity as your router as you've said. Just make launchmode to singleTop in your manifest in order to user onNewIntent() method of an Activity.
You'll generate notification, sending some data with contentIntent - as a result clicking on notification you'll be redirected to StartActivity.
Two cases here -
If StartActivity is in stack
onNewIntent gets called with your new Intent - Choose your activity required to be open - If it is already in stack Bring it to front using FLAG_ACTIVITY_REORDER_TO_FRONT flag
If StartActivity is not running or not in stack
Receive bundle of intent that is being got from the intent via notification, parse data and open an activity accordingly.

Start main activity when opening backgrounded app from other app by url scheme

I've just implemented deep linking to my app. I've added intent filter to my main activity. When user starts my url scheme I'm parsing it and handling in the activity to display proper information depending on parameters. It works well when my app is started from scratch.
<activity
android:launchMode="singleTask"
android:name="com.my.app.ui.activities.MainActivity"
android:screenOrientation="portrait"
android:configChanges="orientation|screenSize|">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="myapp-scheme"/>
</intent-filter>
</activity>
The problem occurs when user opens some other activities on stack and goes background. Now if this app is resumed from background by calling my scheme url, all activities are still on the stack and the main activity doesn't show up.
How can I resolve this?
I think about navigating back to the main activity, but I don't have info if app was started from other app or in the normal way.
You need to make your MainActivity as singleTask by adding following line in the manifest(for ManiActivity):
android:launchMode="singleTask"
Then, override the onNewIntent() in the MainActivity and handle the navigation based on deep link data.Hope this answers your question.

Start External Activity for Result and Return to Requesting Activity

I have two applications, call them app 1 and app 2, and am starting an activity in app 2 from app 1 for a result.
The activity in app 2 has an activity-alias applied to it so I can add an action (used to start it), and requires a signature-level permission (shared with app 1).
The code used to start the app 2 activity from app 1 is simple:
Intent startActivity = new Intent(MY_CUSTOM_ACTION);
startActivity.setPackage(APP2_PACKAGE);
startActivity.putExtra(EXTRA_SOME_PARAM, ...);
startActivityForResult(startActivity, 1234);
This results in the activity appearing as expected. I then get some user input, and clean up app 2's activity like so:
setResult(Activity.RESULT_OK, someData);
finish();
What I am expecting to happen, is for the calling activity in app 1 to be restored to the foreground and so onActivityResult to be called, but what is actually happening, is that app 2's main/launch activity is started instead.
Sometimes I get the onActivityResult callback with the right response code and data, but app 1's activity is still stopped.
My understanding of Android led me to believe that app 2's activity would have been part of app 1's task stack (and opening the task switcher bears this out), and so finishing it would just pop back to the caller.
I've tried:
enabling task reparenting on the app 2 activity
zeroing out the flags on the startActivity Intent
ensuring that app 2's activity doesn't try to start any other activities
tracing through the Activity lifecycle to see what's happening, but all trails go cold at NativeActivityManager...
clearing any existing tasks that app 2 has open
finishing app 2's activity with finishAffinity (doesn't work when returning a result)
None of this seems to have an effect. Many of the Intent flags and launch modes aren't allowed when starting for a result, so I'm at a loss for what to try next.
Thanks!
Here's the <activity> definitions from the manifest in case it matters:
<activity
android:name=".activity.App2Activity"
android:windowSoftInputMode="adjustResize"
android:configChanges="locale"
android:theme="#style/Some.Theme"
android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity-alias
android:name=".activity.App2ActivityAlias"
android:permission="x.y.z.MY_CUSTOM_PERMISSION"
android:targetActivity=".activity.App2Activity"
android:exported="true">
<intent-filter>
<action android:name="x.y.z.MY_CUSTOM_ACTION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity-alias>

Categories

Resources