How to bring most recently used third party Activity to front? - android

In my app, I want to programmatically bring the most recently used third party Activity to the front. After looking at other answers here, I've tried relaunching the activity using the baseIntent returned from a list of recent tasks, but that does not seem to bring the activity to the front over whatever else is going on.
My end goal is to create an app that replaces the incoming call screen with a small overlay so the user is not pulled completely out of whatever app they are using when they get a call. I've found you can't replace the default incoming call screen (if this is not true, please let me know as I'd rather do that instead) so as a workaround, I am trying to call the most recently used app to the front of the screen (to overlay the incoming call screen) and then display my overlay on top of that.
Here's the code I am using (The activity is launched from a broadcast receiver)
public class BringMRUAppToFront extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
ActivityManager activityManager = (ActivityManager) getSystemService("activity");
List<RecentTaskInfo> recentTasks = activityManager.getRecentTasks(1, ActivityManager.RECENT_WITH_EXCLUDED);
if(recentTasks.size() > 2) {
RecentTaskInfo recentTask = recentTasks.get(2);
Intent testIntent = recentTask.baseIntent;
Log.i("MyApp", "Recent task - " + recentTask.baseIntent.getComponent().getPackageName());
testIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(testIntent);
}
finish();
}
}
Is there a reliable way to bring any third party activity to the front? The activity is guaranteed to be in memory (if one is not in memory, then I would just display the home screen), so there shouldn't be an issue there. I also don't believe it would be a security issue in this case as it would just be displaying an app that was visible right before the phone rang - though I do understand that opening this up in general in the SDK could pose a risk...still hoping it is possible.
EDIT: Modified the code slightly to reflect what I'm doing. The desired task will almost always be the 3rd task in the list - first is the current task and second is the task of the ringing phone. I am able to call the task to the front, but it is not always in the same state (going to the browser's page instead of the settings screen in the browser, for example). How does the recent tasks list do this?

Figured it out by looking at the Android source code - this is exactly what the Recent Tasks screen does, both pre- and post-Honeycomb:
RecentTaskInfo recentTask = recentTasks.get(1); // task 0 is current task
// maintains state more accurately - only available in API 11+ (3.0/Honeycomb+)
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
am.moveTaskToFront(recentTask.id, ActivityManager.MOVE_TASK_WITH_HOME);
} else { // API 10 and below (Gingerbread on down)
Intent restartTaskIntent = new Intent(recentTask.baseIntent);
if (restartTaskIntent != null) {
restartTaskIntent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
try {
startActivity(restartTaskIntent);
} catch (ActivityNotFoundException e) {
Log.w("MyApp", "Unable to launch recent task", e);
}
}
}
Permission android.permission.REORDER_TASKS is necessary for this to work correctly.

Start from Android L, getRecentTasks() returns application’s own tasks and possibly some other non-sensitive tasks (such as Home), so you can not do such job.
References: https://developer.android.com/preview/api-overview.html#Behaviors

Why dont you use a global static variable that is accessed by all activities and on its onPause just set that variable to that activity value
Eg
I am placing a static variable called ActivityName
public static String ActivityName = "";
and in the onPause of every activity just assign it the activities pacakage name
#Override
public void onPause() {
super.onPause();
// The static global variable
com.pacakagename.Utls.ActivityName = "com.pacakagename.Act1";
}
So when any of the activity pauses the value is assinged and you will come to know of the most recent paused activity and you can restart it using Class.forName(ActivityName)

Related

Activity.startLockTask() from one Activity to another Activity

I am using Activity.startLockTask() and have noticed that if I pin the screen in Activity A, I am unable to transition to Activity B. It seems like I have to startLockTask() and then stopLockTask() and then startLockTask() again on Activity B.
Is there a way a better way of doing handling this so that I can pin the entire app, regardless of what Activity I am on?
This is how I pin the app:
// start lock task mode if it's not already active
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
// ActivityManager.getLockTaskModeState api is not available in pre-M
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
if (!am.isInLockTaskMode()) {
startLockTask();
}
} else {
if (am.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_NONE) {
startLockTask();
}
}
This is how I am stop pinning
stopLockTask()
This issue is a difficult issue to deal with, but the solution is very simple. For anyone else facing the same problem, all you have to do is change your launchMode to single task. Once I updated my Manifest, I was able to remain pinned while changing Activities seamlessly.
android:launchMode="singleTask"

How to detect which activity caused Application to start?

When launching an Activity for an app, the first piece of my code that runs is my subclass of Application.onCreate(). Is there a way to know which Activity triggered that?
More specifically, in my Application subclass onCreate(), I do some database initialization. This can fail and my general solution for failures is to launch another activity where I can display something to the user. This works fine if the failure is anywhere but in Application.onCreate().
When the failure is in Application.onCreate(), Android tries to restart my Application subclass, which in turn fails, and so on. I can prevent the infinite loop with the activity SingleInstance attribute. But that prevents any activity from starting up.
One solution would be to move my database code into my main activity's onStart(). However, I would prefer to leave it in Application.onCreate() if there's a way I can bypass it when the error handling activity is trying to launch.
One approach would be to switch to ACRA for your exception-handling activity, or at least to use their technique.
ACRA winds up in a separate :acra process. They then use ActivityManager and getRunningAppProcesses() to determine if the current process is the :acra process or not:
/**
* #return true if the current process is the process running the SenderService.
* NB this assumes that your SenderService is configured to used the default ':acra' process.
*/
public static boolean isACRASenderServiceProcess(#NonNull Application app) {
final String processName = getCurrentProcessName(app);
if (ACRA.DEV_LOGGING) log.d(LOG_TAG, "ACRA processName='" + processName + "'");
return (processName != null) && processName.equals(ACRA_PRIVATE_PROCESS_NAME);
}
#Nullable
private static String getCurrentProcessName(#NonNull Application app) {
final int processId = android.os.Process.myPid();
final ActivityManager manager = (ActivityManager) app.getSystemService(Context.ACTIVITY_SERVICE);
for (final ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()){
if(processInfo.pid == processId){
return processInfo.processName;
}
}
return null;
}
While getRunningAppProcesses() has been lobotomized in Android 5.0+, you can still use it for your own processes, which is all that we need here.
Given that you know whether you are in the ACRA process or not, you can decide whether or not to do certain initialization, such as your database initialization.
In your case, you would isolate the exception-handling activity in a separate named process, see if you are in that process in Application#onCreate(), and skip the database initialization if you are.
If I understand you correctly, you want to know, WHO started your activity.
And if I also understand correctly, you do start (at least sometimes) this activity from inside your app.
If both assumptions are true, take a look at the Intent class. You start an activity with an intent, where you can put anything in it, with methods like .putString(...) and similar.
So when starting your activity do something like
Intent intent = new Intent(this, myotheractivity.class);
intent.putString("caller", this.getClass().getSimpleName());
startActivity(intent);
And store the name of the calling class (or anything else!) in the activity.
In the onCreate() or your activity just check with a construct like this:
Intent intent = getIntent();
if (intent != null) {
String caller = intent.getString("caller", "");
if (!caller.equals("")) {
// Here caller contains the name of the calling class
}
}
If this intent is null or caller=="", it was not your own app that started this activity.
Cheers

Android - Open an external app and then call an intent URI

I have an app 'A'. I am opening another app 'B''s video player and playing a video using an intent URI call like so
String intentURI = "B://this/123";
try {
intent = Intent.parseUri(intentURI, Intent.URI_INTENT_SCHEME);
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
Logger.appendInfoLog("Something went wrong with B", TAG);
e.printStackTrace();
Logger.appendErrorLog(e.getMessage(), TAG);
finish();
}
startActivity(intent);
Now the necessary condition is for that app 'B' to be open in the background for this to work.If the app is closed(killed by Android or manually) or it has crashed,this throws an error.
Is there a way to open that app 'B' first or check its running status and then make the intent URI call. I will not get control back from that app and once I go to the other app I dont have any control on it until the user presses the back button to return to my app.
UPDATE:
I want to start app 'B' first and then call the intent programmatically. Is this possible
UPDATE
I Ran the Catlog app to check what message comes up in app B. It just shows file 123.file (The one i am trying to play in the app B's video player) not found, but when the app is running in the background it goes through fine. It also shows a warning
java.lang.NullPointerException: PrintLn needs a message
and then it says
Activity displayed, but mediaplayer.is not playingVideo
Also the other app is written in flash and packaged as a native app on adobe air
I have an app 'A'. I am opening another app 'B''s video player and playing a video using an intent URI call like so
No, you are not. You can tell that by reading the code -- there is no startActivity() call in that code block.
Now the necessary condition is for that app 'B' to be open in the background for this to work.If the app is closed(killed by Android or manually) or it has crashed,this throws an error.
Then apparently app B has a bug. Please contact the author of app B for assistance.
You can get a list of running apps easily.
ActivityManager manager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> processes = manager.getRunningAppProcesses();
And you can launch apps just as easily.
Intent LaunchIntent = getPackageManager().getLaunchIntentForPackage("com.package....");
startActivity(LaunchIntent);
You can't, however, launch an app into background, so this might not solve your issue.
You don't need to open that app 'B' first. Just check if that app is running with:
// Get running processes
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningProcesses = manager.getRunningAppProcesses();
So now you have all the running processes in runningProcesses. Just iterate over the values to find out if your app 'B' is running. An example of this iteration can be found here:
ActivityManager am = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
List l = am.getRunningAppProcesses();
Iterator i = l.iterator();
PackageManager pm = this.getPackageManager();
while(i.hasNext()) {
ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo)(i.next());
try {
CharSequence c = pm.getApplicationLabel(pm.getApplicationInfo(info.processName, PackageManager.GET_META_DATA));
Log.w("LABEL", c.toString());
}catch(Exception e) {
//Name Not FOund Exception
}
}
Define the intent listener inside of your manifest file.
See the docs for details.
Basically, what you would do is in your AndroidManifest.xml of App B (the app you want to start with the Intent), add a section like:
<receiver android:name=".MyIntentReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Then inside of your MyIntentReceiver class, you would define the code to handle the intent.
public class MyIntentReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
// handle intent here
}
}
I think your problem is that Intent.parseUri() returns an Intent with the action ACTION_VIEW (see http://developer.android.com/reference/android/content/Intent.html#parseUri%28java.lang.String,%20int%29). And from your code, I gather that android does not recognize that it actually has to start activity B for this. ACTION_VIEW is generic, so id does what seems to be most appropriate. If activity B does not fall into that category (probably not the standard video viewing app), it' will not get launched. I would suggest the following:
Find out how to launch activity B (e.g. Intent i = new Intent.("com.packageB.ActivityB"); or similar. Developer should be able to tell you.
Use i.setData() to set the data of your intent to your file.
Use startActivity()
For step 2, there may be other ways Activity B needs the Uri passed. You can get this information from the developer as well.
Using startActivity rids you of having to check whether App B is running. Something that Android does anyway.
I think it is possible to start another apk from your own, just check this answer. The problem is that if you dont know their uri, it will be hard to do what you propose.
Now you can either ask the developers for the proper names or take a look at your own risk (i don't know about its legality).

Remove app from recent apps programmatically

I know that Activities can be declared in manifest as being excluded from recents with android:excludeFromRecents:
http://developer.android.com/guide/topics/manifest/activity-element.html#exclude
However, that's not what I'm looking for, I would like to know if there is a way to remove the app from recent apps programmatically
Yes, generally when you want to have special properties for an Activity when starting it you supply special flags to the Intent. In this case FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS.
Updated:
If you need to hide the current already running activity, you might be able to use this flag in combination with FLAG_ACTIVITY_CLEAR_TOP which would send the new Intent to the existing Activity. You'll have to think and perhaps experiment with what happens as the user moves around your stack though and whether that will make your app re-appear in the recent apps.
This can be done using the ActivityManager.AppTask functionality (starting in API 21)
ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
if(am != null) {
List<ActivityManager.AppTask> tasks = am.getAppTasks();
if (tasks != null && tasks.size() > 0) {
tasks.get(0).setExcludeFromRecents(true);
}
}
Add these lines to the Activity from which you are exiting the application:
#Override
public void finish() {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
super.finishAndRemoveTask();
}
else {
super.finish();
}
}
Following is the definition of the flag android:excludeFromRecents (which i know you have already seen):
Whether or not the task initiated by this activity should be excluded from the list of recently used applications ("recent apps").
That is, when this activity is the root activity of a new task, this
attribute determines whether the task should not appear in the list of
recent apps. "true" if the task should be excluded from the list;
"false" if it should be included. The default value is "false".
so to remove the app from the list of recent app you can set this flag on the first activity in your application since that activity launches the the task for you application. If you have multiple tasks (unlikely for most apps) in your application then you need o set this flag for root activity of all the task.
Call this when your activity is done and should be closed and the task should be completely removed as a part of finishing the root activity of the task.
finishAndRemoveTask();
After receiving the other answers, I managed to get to the following workaround: I have an Activity, let's call it XPTO, declared in manifest with
and basically, when I want app to disappear from the recents list, I finish all other activities, then start the XPTO, which basically kills the app (calling android.os.Process.killProcess(android.os.Process.myPid()); in its onResume()
If anyone has a better way of doing this, please share it

Two instances of my android application are running....How to avoid this?

Here is my problem -
I copied my .apk file onto phone memory card and launch my application clicking on it and it allows me to install my application.I install my application.Finally,I got system installation pop up containing two options "Open" and "Done".When i click "Open" my application got launched.Up to this point everything is working without any problem.
Now in my application I click on a button and some download is taking place as a result(Showing progress dialog).Now I press a Home button,so my application goes to background.
Now I again launch my application by going inside Menu and clicking on my application icon.
Expected result - Still I Should see Progress Dialog for downloading.
Actual result - A new instance/session of my application is getting started.
So how to avoid this so that only one and one instance/session of my application should run.
#Palejandro, here you are. Put the code below into your main activity onCreate() method:
// Possible work around for market launches. See
// http://code.google.com/p/android/issues/detail?id=2373
// for more details. Essentially, the market launches the main activity
// on top of other activities.
// We never want this to happen. Instead, we check if we are the root
// and if not, we finish.
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Log.w(TAG, "Main Activity is not the root. Finishing Main Activity instead of launching.");
finish();
return;
}
}
I used this piece of code in my projects and it works fine!
I believe you need to put
<activity
android:launchMode="singleInstance"
</activity>
in the manifest file.
what do your OnPause, OnResume and OnCreate?
I will bet money you are not saving anything OnPause, and starting a new instance all the time via OnCreate.
You should read the notes on Activity Lifecycles.
If you haven't got this sorted yet, I would say your app is actually being killed when home is pressed, or perhaps you have a bug that doesn't latch onto whatever object is keeping state.
// put below code in your launcher activity before call super and setcontentview()
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
// get the info from the currently running task
List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(10);
boolean alreadyTask=false;
for(ActivityManager.RunningTaskInfo info : taskInfo){
ComponentName componentInfo = info.topActivity;
String value= componentInfo.getPackageName();
if(value.contains(getPackageName()) && !info.topActivity.getClassName().contains(getPackageName()+".LauncherActivity")){
alreadyTask=true;
Log.i(TAG, "second instance found!!!");
break;
}
}
if(alreadyTask){
finish();
}
I don't have a solution but the problem is that the intent used to start the app is different when you open it directly from install compared to opening it from your home screen. Since it will get started by two different intents it will open a new instance the second time round.
A quick work around is to avoid pressing "Open" when you have installed the application. Press "Done" and then find the application yourself.
See: http://code.google.com/p/android/issues/detail?id=2373

Categories

Resources