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.
Related
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.
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?
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>
In my application I have three activities:
<activity
android:name="com.example.myapp.SplashScreenActivity"
android:exported="true"
android:launchMode="singleInstance"
android:noHistory="true"
android:screenOrientation="sensorLandscape" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.myapp.MainActivity"
android:exported="false"
android:launchMode="singleInstance"
android:screenOrientation="sensorLandscape" >
</activity>
<activity
android:name="com.example.myapp.ListActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="sensorLandscape" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.myapp.MainActivity" />
</activity>
The first one is the LAUNCHER, SplashScreenActivity, which is a splash screen that disappears quite soon and it's not shown in recent activities, it starts MainActivity. In MainActivity users can select a category and ListActivity shows the items belonging to the given category. This is done with the following code:
Intent i = new Intent(getActivity(),ListActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
i.putExtra("category",mCategory);
startActivity(i);
In ListActivity the onResume method checks for the "category" extra and shows data accordingly. Since Activity launchMode is singleTop, I've also overridden the onNewIntent method to set the new Intent of the Activity.
This works properly if the app doesn't go in background: in this case, when I restart MainActivity and select a category, ListActivity resumes the old Activity showing data belonging to the previously chosen category.
How should I fix flags/launchMode in such way that my app doesn't resume ListActivity with the old data loaded?
You should not use the special launch modes. These are rarely required and only in very specific circumstances. Remove all the launchMode specifiers from your manifest. You also don't need to use these flags when launching ListActivity from MainActivity:
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
Your use of singleInstance launch mode is causing you all these problems.
Assuming you would ditch all the launchModes and Flags, then activity lifecycle gives you all the information needed to implement application as you have described - maybe there is somethings you have not added?
Once application goes to background, then ListActivity will be allowed to save its state, even if system will kill your process (lack of memory etc.), then still - launching back your app will first bring back ListActivity with previously saved instance state. So you will not have behaviour as you describe : when I restart MainActivity and select a category, ListActivity resumes the old Activity showing data belonging to the previously chosen category.
Still, if you need to keep to your current design, then you should somehow inform ListActivity of new data changes, question is if ListActivity.onNewIntent is being called at all in the case your describe? Have you tried adding Intent.FLAG_ACTIVITY_SINGLE_TOP : as per this blog entry: http://www.acnenomor.com/1094151p2/bug-onnewintent-not-called-for-singletop-activity-with-intentflagactivitynewtask ?
I have a ListActivity and a MapActivity. I would like to launch either one of these activities on application startup that has been chosen by the user in a preferences window.
So far the only way I see to launch an activity on application startup is to specify it in the application manifest file using...
<activity android:name=".MyActiivty"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
I am thinking I might have to start an activity that does nothing but looks at the user preferences and then launches either the ListActivity or MapActivity. Seems like a waste to have an activity do nothing but launch another activity. In my research I have not found any solution to this problem. Any suggestions would be greatly appreciated.
Thanks & Regards,
Dave
First, don't create some third activity. Just have the LAUNCHER Activity be either the list or the map, and have it call startActivity() on the other one (plus finish()) in onCreate() before calling setContentView() when needed. That way, ~50% of the time, you're launching the right activity.
In principle, you could have both activities have a LAUNCHER <intent-filter>, only enabling one. However, that will not work with desktop shortcuts, which will route to a specific activity (whichever one happened to be configured when they made the shortcut). If this does not concern you, you might go this route. However, try to test it with a few devices and custom home screens -- I'm not sure if everyone will pick up on your change immediately.
I just added the following code to the onCreate() method an it worked like a charm.
Intent intent;
intent = new Intent(this, MyMap.class);
startActivity( intent );
finish();
for new folks (me), following is dave's answer, plus changes i needed to make to AndroidManifest.xml.
Main activity:
Intent intent;
intent = new Intent(this, DisplayMessageActivity.class);
startActivity( intent );
changes to xml file, from -> http://developer.android.com/training/basics/firstapp/starting-activity.html
AndroidManifest.xml:
<activity
android:name="com.mycompany.myfirstapp.DisplayMessageActivity"
android:label="#string/title_activity_display_message"
android:parentActivityName="com.mycompany.myfirstapp.MyActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.mycompany.myfirstapp.MyActivity" />
</activity>