My application is in running mode[foreground] and user clicks on home button, which puts application to background[and still running]. I have alarm functionality in my application which fires up. I want is when my alarm goes off i want to bring my background running application in foreground and from last state in which it was.
<application
android:name="com.abc.android.state.management.MainEPGApp"
android:icon="#drawable/icon"
android:label="#string/app_name"
android:largeHeap="true"
android:logo="#drawable/app_logo" >
<activity
android:name=".SplashScreen"
android:label="#string/app_name"
android:launchMode="singleTop"
android:screenOrientation="nosensor"
android:theme="#style/Theme.Sherlock" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Starter"
android:configChanges="orientation|screenSize"
android:screenOrientation="behind"
android:launchMode="singleTop"
android:uiOptions="none"
android:windowSoftInputMode="adjustPan" />
</application>
Intent intent = new Intent(context, MyRootActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
You should use your starting or root activity for MyRootActivity.
This will bring an existing task to the foreground without actually creating a new Activity. If your application is not running, it will create an instance of MyRootActivity and start it.
EDIT
I added Intent.FLAG_ACTIVITY_SINGLE_TOP to Intent to make it really work!
Second EDIT
There is another way to do this. You can simulate the "launching" of the app the same way that Android launches the app when the user selects it from the list of available apps. If the user starts an application that is already running, Android just brings the existing task to the foreground (which is what you want). Do it like this:
Intent intent = new Intent(context, SplashScreen.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // You need this if starting
// the activity from a service
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(intent);
A combination that works for me is to use:
Intent bringToForegroundIntent = new Intent(context, RootActivity.class);
bringToForegroundIntent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(bringToForegroundIntent);
If you check the logs on the device whenever you start an activity from a launcher icon this is the intent that gets passed to launch the app or move it back to foreground if e.g. user clicked the Home button.
I find a new way to bring app to foreground,
It imitate click Icon to launch application.
PackageManager packageManager = context.getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage(pkName);
if (intent != null)
{
//模拟点击桌面图标的启动参数
intent.setPackage(null);
// intent.setSourceBounds(new Rect(804,378, 1068, 657));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
context.startActivity(intent);
}
it work for me, But who can tell me why package must be null
You can use the below code to bring the application to front:
private void bringApplicationToFront()
{
Log.d(TAG, "====Bringging Application to Front====");
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
try
{
pendingIntent.send();
}
catch (CanceledException e)
{
e.printStackTrace();
}
}
From my test, to mimic the Launcher behavior, the key is to:
intent.setPackage(null);
after
Intent intent = packageManager.getLaunchIntentForPackage(pkName);
Other methods in this thread doesn't work in my case.
Thanks to jiaqing's answer, I post this answer as I don't have the right to comment. I don't know the logic behind this either, I guess it's related with the task owner. Anyone knows, would be glad to know.
Using the ActivityManager class you can bring a running task to front
void bringToFront(){
ActivityManager activtyManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> runningTaskInfos = activtyManager.getRunningTasks(3);
for (ActivityManager.RunningTaskInfo runningTaskInfo : runningTaskInfos)
{
if (this.getPackageName().equals(runningTaskInfo.topActivity.getPackageName()))
{
activtyManager.moveTaskToFront(runningTaskInfo.id, ActivityManager.MOVE_TASK_WITH_HOME);
return;
}
}
}
For an alarm, you want to take a look at starting an Android Service
This service will be more resilient than your application which may be killed while in the background and can fire off an intent to bring your application to the front (or restart it if it was killed) when it is time for the alarm to go off.
Related
I am having a scenario. I am trying to lock my application using an PinActivity which I created. I am running a service which has a counter timer for 5mins. If there is no activity by the user in app for 5 mins. I will show him the PINActivity which he has to unlock and enter the app. I am launching the PINActivity like this:
Intent loginIntent = new Intent(this, PINActivity.class);
loginIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(loginIntent);
So when the user comes back to the app and see the PINActivity he will unlock it and enter the app. But what if the user doesn't enter the PIN and leave the app again ideal for more then 5 mins(Note: my service starts the counter timer the moment user brings the app to foreground). I don't want to add multiple instance of the same PINActivity at top. How can I make sure I have only one PINActivity at the top?
You should use FLAG_ACTIVITY_SINGLE_TOP. This will prevent Android from launching PINActivity if there is already an instance of PINActivity on top of the stack.
Intent loginIntent = new Intent(this, PINActivity.class);
loginIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP);
startActivity(loginIntent);
Please don't try to use special launch modes like singleTask or singleInstance as others have suggested. This won't help and will complicate the problem for you.
You can specify android:launchMode="singleTop" in the manifest entry for PINActivity as well. This has the same effect as FLAG_ACTIVITY_SINGLE_TOP.
Add launchMode="singleInstance" in your activity in manifest like this:
<activity
android:launchMode="singleInstance"
android:name=".MainActivity"
..... />
add android:launchMode="singleInstance" in manifest
<activity
android:name=".MainActivity"
android:launchMode="singleInstance"/>
When you launch activity you can use this :
Intent intent = new Intent(this, MyActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
If activity is already there then it will use the same instance else create new one and no duplication will be there.
In manifest add attribute of singleInstance in activity tag.
<activity
android:launchMode= "singleInstance" />
Click Here
Launcher activity for my app is called LaunchActivity. Inside this activity I check if the user is logged. If yes, then a network call is made to validate the user and if everything is fine MainActivity is started. If user runs app for first time or user validation fails, LoginActivity runs.
Inside LaunchActivity there is function that runs appropriate activity:
private void start(Class<? extends Activity> startActivity) {
Intent intent = new Intent(this, startActivity);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
finish();
startActivity(intent);
}
App is receiving notifications. After notification click i want to open app or bring it top if it is on background.
Code responsible for notification intent:
Intent intent = new Intent(context, LaunchActivity.class);
intent.putExtra("SHOW_NOTIFICATION_LIST", "");
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent notificationIntent = PendingIntent.getActivity(
context,
0,
intent,
0);
builder.setContentIntent(notificationIntent);
Notification notification = builder.build();
The problem is that with my current implementation after click, LaunchActivity is reopened even when MainActivity is on background. I can not figure out how to make the pending intent reopen just MainActivity (without LaunchActivity and valdation) if it is on background OR start LaunchActivity when there are no activity running on background ( app is not running ). I would be very grateful for any kind of help.
edit:
Activities declaration inside manifest:
<activity android:name=".activities.LaunchActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activities.MainActivity"
android:launchMode="singleTop"
android:screenOrientation="userPortrait" />
<activity
android:name=".activities.LoginActivity"
android:excludeFromRecents="true"
android:launchMode="singleTop"
android:noHistory="true"
android:screenOrientation="userPortrait"
android:windowSoftInputMode="adjustResize" >
</activity>
The reason for the absurd behavior is that because you are finishing the LaunchActivity when you call start()
private void start(Class<? extends Activity> startActivity) {
....
finish(); // This is the culprit
....
}
Remove finish() from start() and it will work fine as expected.
If you can't remove finish() because of your app behavior, then change your PendingIntent to launch MainActivity instead of LaunchActivity
Intent intent = new Intent(context, MainActivity.class);
And in MainActivity's onCreate() check if the user is already logged in . If not then navigate him to your LoginActivity.
Create a BroadcastReceiver which reacts to three different intents
ACTION_CREATED: set flag to true
ACTION_DESTROYED: set flag to false
ACTION_LAUNCH: if flag, start MainActivity, otherwise start LaunchActivity
(flag can be e.g. a boolean in sharedpreferences or a static field)
in MainActivity:
in onCreate: send ACTION_CREATED broadcast
in onDestroy: send ACTION_DESTROYEDbroadcast
The notification should send an ACTION_LAUNCHbroadcast.
Note: actions can be named as whatever you want. They also should start with you package name, so you don't interfere with other apps.
I have a confusion with how android starts its launcher activities.
If I am declaring a launcher activity in the manifest file like this
<activity android:name=".Activities.Home">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
and I have an Application class which calls an activity based on a check like
if(ParseUser.getCurrentUser() == null){
Intent intent = new Intent(context,Home.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}else{
Intent intent = new Intent(context,MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
which one has the precedence ? The manifest or the Application. What is the flow of events ? e.g. Application->Manifest (or) Manifest->Application (or) Application overrides Manifest ?
If I am receiving a notification in Android, my Application class is called. This makes the activities in the application class to be started, like shown above. Is there a way to detect who calls the Application class ? I mean whether the user starts it, or it starts from the notification which comes in ?
If there is a way to figure this out. How do I prevent the activity in the Application class to be called when I receive a notification ?
Thanks in advance.
Just pass the boolean extras from application and make the diffrence in the call from Application class and User Launch.
Intent intent = new Intent(context,Home.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("APP_CALL",True);
startActivity(intent);
Now in your Home activity Just check for APP_CALL if it is called from Application class then bool value will be true else false.
Also you can write in splash activity for checking login .
When user press "Home Button" my app go to Background, but is still running. I need bring my app to front again. For example i've this code:
Context ctx=getApplicationContext();
Intent i =ctx.getPackageManager().getLaunchIntentForPackage("com.example.test");
ctx.startActivity(i);
but this code is to open app in different activity, there are a method or something to do this "correctly"?
You can simulate the "launching" of the app the same way that Android launches the app when the user selects it from the list of available apps. If the user starts an application that is already running, Android just brings the existing task to the foreground (which is what you want). Do it like this:
Intent intent = new Intent(context, SplashScreen.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // You need this if starting
// the activity from a service
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(intent);
(Source: https://stackoverflow.com/a/12075313/3529926)
You can set this flag to your intent
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
and set the launch mode of your activity to single task in manifest (add this is your activity tags):
android:launchMode="singleTask"
Launching an activity by clicking the app icon in launcher, should bring the activity to foreground just like picking it from history. So no onCreate call should exist.
However,if we try to do this after starting the activity by clicking a notification, then the launcher just starts another instance of the activity.
What flags must I add so that the launcher keeps working as expected ( resuming the exact state of the app from background )?
I'll post the essential code.
This starts the notification:
Intent resumeIntent = new Intent(this, MainActivity.class);
PendingIntent resumePendingIntent = PendingIntent.getActivity(this, 2,
resumeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification resumeNotification = new Notification.Builder(this).setContentTitle(
"Resume style")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(resumePendingIntent)
.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
notificationManager.notify(1, launcherNotification);
This is how the manifest activity looks:
<activity
android:name="com.example.ihatenotifiicationsapp.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
This resumeIntent will be automatically added the FLAG_ACTIVITY_NEW_TASK by Android. This flag will allow the resuming of the application from background if present.
All nice until here, but, If after you click this notification and resume the app, you click the app from launcher then Android launches another instance of MainActivity.
This breakes my application and the backstack ( you will have 2 MainActivity in the stack, weird for user ).
The funnies thing is this happens ( clicking the launcher behaviour to launch another instance ) only after you click the notification.
You can use the Flag android:launchMode="singleTask" in your activity Tag if you want this behavior. This prevents the OS from launching any other Instance, if there is currently one Active. See the SDK Doku for more information on launchbehaviors here
I edited this Answer corresponding to Emanuel Moecklin Comment below. Mixed the lauchModes up.
Excerpt from the Doku:
The system creates the activity at the root of a new task and routes
the intent to it. However, if an instance of the activity already
exists, the system routes the intent to existing instance through a
call to its onNewIntent() method, rather than creating a new one.
Try
Intent resumeIntent = new Intent(this, MainActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP;
If set, and the activity being launched is already running in the
current task, then instead of launching a new instance of that
activity, all of the other activities on top of it will be closed and
this Intent will be delivered to the (now on top) old activity as a
new Intent.
http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_CLEAR_TOP