// Copyright (c) 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium; import android.app.Activity; import android.app.Application; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; public class BackgroundActivity extends Activity { private static final String LOG_TAG = "BackgroundActivity"; static boolean prevLaunchWasProgrammatic; // Used to set cordova.resumeType static BackgroundActivity topInstance; // Used for finish()ing the activity after re-parenting @Override public void onCreate(Bundle savedInstanceState) { // This is called only when launchBackground() is first called, and this is the activity // at the top of the MainActivity sandwich. super.onCreate(savedInstanceState); topInstance = this; final Application app = (Application)getApplicationContext(); app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { app.unregisterActivityLifecycleCallbacks(this); activity.moveTaskToBack(true); } @Override public void onActivityStarted(Activity activity) {} @Override public void onActivityResumed(Activity activity) {} @Override public void onActivityPaused(Activity activity) {} @Override public void onActivityStopped(Activity activity) {} @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} @Override public void onActivityDestroyed(Activity activity) {} }); Intent i = makeMainActivityIntent(this, false, Intent.FLAG_FROM_BACKGROUND | Intent.FLAG_ACTIVITY_NO_ANIMATION); startActivity(i); } private static Intent makeMainActivityIntent(Context context, boolean fromLauncher, int flags) { ComponentName foregroundActivityComponent = findMainActivityComponentName(context); Intent ret = new Intent(); ret.setComponent(foregroundActivityComponent); ret.setFlags(flags); if (fromLauncher) { ret.setAction(Intent.ACTION_MAIN); ret.addCategory(Intent.CATEGORY_LAUNCHER); } else { ret.addCategory(Intent.CATEGORY_DEFAULT); } return ret; } private static ComponentName findMainActivityComponentName(Context context) { PackageManager pm = context.getPackageManager(); PackageInfo packageInfo = null; try { packageInfo = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES); } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException("No package info for " + context.getPackageName(), e); } for (ActivityInfo activityInfo : packageInfo.activities) { if ((activityInfo.flags & ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS) == 0) { return new ComponentName(packageInfo.packageName, activityInfo.name); } } throw new RuntimeException("Could not find main activity"); } public static void launchBackground(Context context) { // To verify the state of the task stacks, use: // adb shell dumpsys activity activities Intent activityIntent = new Intent(context, BackgroundActivity.class); activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_FROM_BACKGROUND | Intent.FLAG_ACTIVITY_NO_ANIMATION); context.startActivity(activityIntent); } public static void launchForeground(Context context, boolean fromLauncher) { boolean isAlreadyRunning = BackgroundPlugin.pluginInstance != null; // When transitioning from background to foreground, the RESET_TASK_IF_NEEDED is what causes // the MainActivity to be "re-parented" to its own task stack rather than a new activity // being created on it. // If the launcher would contain this flag, then we wouldn't need BackgroundLauncherActivity. // However, it seems that on Lollipop, the flag isn't present, while on JellyBean it is. // It's launcher-dependent though. Intent intent = makeMainActivityIntent(context, fromLauncher, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); prevLaunchWasProgrammatic = !fromLauncher; if (!isAlreadyRunning) { Log.i(LOG_TAG, "Starting foreground for first time. fromLauncher=" + fromLauncher); } else if (topInstance != null) { Log.i(LOG_TAG, "Reparenting background->foreground. fromLauncher=" + fromLauncher); } else { Log.i(LOG_TAG, "Resuming foreground activity. fromLauncher=" + fromLauncher); } // Need to use application context on older androids for intents not to be ignored :S context.getApplicationContext().startActivity(intent); } }