OSDN Git Service

Use singleTask to improve unsecure lockscreen startup
authorPuneet Lall <puneetl@google.com>
Thu, 25 Sep 2014 22:05:47 +0000 (15:05 -0700)
committerPuneet Lall <puneetl@google.com>
Fri, 3 Oct 2014 21:26:17 +0000 (14:26 -0700)
This enables warm-starts from the lockscreen by reusing the same
activity if possible.

Bug: 16035858
Change-Id: I4b1ce0c290e209a5098e3f3e71509e0ca86e1845

AndroidManifest.xml
src/com/android/camera/CameraActivity.java
src/com/android/camera/CaptureModule.java
src/com/android/camera/PhotoModule.java
src/com/android/camera/util/QuickActivity.java [new file with mode: 0644]

index e187f99..01c9dda 100644 (file)
@@ -53,6 +53,7 @@
             android:clearTaskOnLaunch="true"
             android:configChanges="orientation|screenSize|keyboardHidden"
             android:label="@string/app_name"
+            android:launchMode="singleTask"
             android:taskAffinity="com.android.camera.CameraActivity"
             android:theme="@style/Theme.Camera"
             android:windowSoftInputMode="stateAlwaysHidden|adjustPan" >
index ec65cd2..fb7ab64 100644 (file)
@@ -20,7 +20,6 @@ package com.android.camera;
 import android.animation.Animator;
 import android.annotation.TargetApi;
 import android.app.ActionBar;
-import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.ActivityNotFoundException;
@@ -128,6 +127,7 @@ import com.android.camera.util.GcamHelper;
 import com.android.camera.util.GoogleHelpHelper;
 import com.android.camera.util.IntentHelper;
 import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper;
+import com.android.camera.util.QuickActivity;
 import com.android.camera.util.ReleaseDialogHelper;
 import com.android.camera.util.UsageStatistics;
 import com.android.camera.widget.FilmstripView;
@@ -154,7 +154,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
-public class CameraActivity extends Activity
+public class CameraActivity extends QuickActivity
         implements AppController, CameraAgent.CameraOpenCallback,
         ShareActionProvider.OnShareTargetSelectedListener,
         OrientationManager.OnOrientationChangeListener {
@@ -1319,9 +1319,13 @@ public class CameraActivity extends Activity
             };
 
     @Override
-    public void onCreate(Bundle state) {
+    public void onNewIntentTasks(Intent intent) {
+        onModeSelected(getModeIndex());
+    }
+
+    @Override
+    public void onCreateTasks(Bundle state) {
         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_START);
-        super.onCreate(state);
         if (!Glide.isSetup()) {
             Glide.setup(new GlideBuilder(this)
                 .setResizeService(new FifoPriorityThreadPoolExecutor(2)));
@@ -1613,7 +1617,7 @@ public class CameraActivity extends Activity
     }
 
     @Override
-    public void onPause() {
+    public void onPauseTasks() {
         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_PAUSE);
 
         /*
@@ -1660,12 +1664,10 @@ public class CameraActivity extends Activity
                 finish();
             }
         }
-
-        super.onPause();
     }
 
     @Override
-    public void onResume() {
+    public void onResumeTasks() {
         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_RESUME);
         Log.v(TAG, "Build info: " + Build.DISPLAY);
 
@@ -1732,7 +1734,6 @@ public class CameraActivity extends Activity
         }
 
         mOrientationManager.resume();
-        super.onResume();
         mPeekAnimationThread = new HandlerThread("Peek animation");
         mPeekAnimationThread.start();
         mPeekAnimationHandler = new PeekAnimationHandler(mPeekAnimationThread.getLooper());
@@ -1819,8 +1820,7 @@ public class CameraActivity extends Activity
     }
 
     @Override
-    public void onStart() {
-        super.onStart();
+    public void onStartTasks() {
         mIsActivityRunning = true;
         mPanoramaViewHelper.onStart();
 
@@ -1846,16 +1846,15 @@ public class CameraActivity extends Activity
     }
 
     @Override
-    protected void onStop() {
+    protected void onStopTasks() {
         mIsActivityRunning = false;
         mPanoramaViewHelper.onStop();
 
         mLocationManager.disconnect();
-        super.onStop();
     }
 
     @Override
-    public void onDestroy() {
+    public void onDestroyTasks() {
         if (mSecureCamera) {
             unregisterReceiver(mScreenOffReceiver);
         }
@@ -1878,7 +1877,6 @@ public class CameraActivity extends Activity
         } catch (RuntimeException e) {
             Log.e(TAG, "CameraAgentFactory exception during destroy", e);
         }
-        super.onDestroy();
     }
 
     @Override
@@ -2311,10 +2309,12 @@ public class CameraActivity extends Activity
     private void openModule(CameraModule module) {
         module.init(this, isSecureCamera(), isCaptureIntent());
         module.hardResetSettings(mSettingsManager);
-        module.resume();
-        UsageStatistics.instance().changeScreen(currentUserInterfaceMode(),
-                NavigationChange.InteractionCause.BUTTON);
-        updatePreviewVisibility();
+        if (!mPaused) {
+            module.resume();
+            UsageStatistics.instance().changeScreen(currentUserInterfaceMode(),
+                    NavigationChange.InteractionCause.BUTTON);
+            updatePreviewVisibility();
+        }
     }
 
     private void closeModule(CameraModule module) {
index 9b2bb7b..eb471d2 100644 (file)
@@ -140,13 +140,6 @@ public class CaptureModule extends CameraModule
     private static final String PHOTO_MODULE_STRING_ID = "PhotoModule";
     /** Enable additional debug output. */
     private static final boolean DEBUG = true;
-    /**
-     * This is the delay before we execute onResume tasks when coming from the
-     * lock screen, to allow time for onPause to execute.
-     * <p>
-     * TODO: Make sure this value is in sync with what we see on L.
-     */
-    private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
 
     /** Timeout for camera open/close operations. */
     private static final int CAMERA_OPEN_CLOSE_TIMEOUT_MILLIS = 2500;
@@ -243,16 +236,6 @@ public class CaptureModule extends CameraModule
     /** Whether the module is paused right now. */
     private boolean mPaused;
 
-    /** Whether this module was resumed from lockscreen capture intent. */
-    private boolean mIsResumeFromLockScreen = false;
-
-    private final Runnable mResumeTaskRunnable = new Runnable() {
-        @Override
-        public void run() {
-            onResumeTasks();
-        }
-    };
-
     /** Main thread handler. */
     private Handler mMainHandler;
     /** Handler thread for camera-related operations. */
@@ -306,7 +289,6 @@ public class CaptureModule extends CameraModule
     @Override
     public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
         Log.d(TAG, "init");
-        mIsResumeFromLockScreen = isResumeFromLockscreen(activity);
         mMainHandler = new Handler(activity.getMainLooper());
         HandlerThread thread = new HandlerThread("CaptureModule.mCameraHandler");
         thread.start();
@@ -530,21 +512,6 @@ public class CaptureModule extends CameraModule
 
     @Override
     public void resume() {
-        // Add delay on resume from lock screen only, in order to to speed up
-        // the onResume --> onPause --> onResume cycle from lock screen.
-        // Don't do always because letting go of thread can cause delay.
-        if (mIsResumeFromLockScreen) {
-            Log.v(TAG, "Delayng onResumeTasks from lock screen. " + System.currentTimeMillis());
-            // Note: onPauseAfterSuper() will delete this runnable, so we will
-            // at most have 1 copy queued up.
-            mMainHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
-        } else {
-            onResumeTasks();
-        }
-    }
-
-    private void onResumeTasks() {
-        Log.d(TAG, "onResumeTasks + " + System.currentTimeMillis());
         mPaused = false;
         mAppController.getCameraAppUI().onChangeCamera();
         mAppController.addPreviewAreaSizeChangedListener(this);
index bed2684..1d353dc 100644 (file)
@@ -128,10 +128,6 @@ public class PhotoModule
     private static final int UPDATE_PARAM_PREFERENCE = 4;
     private static final int UPDATE_PARAM_ALL = -1;
 
-    // This is the delay before we execute onResume tasks when coming
-    // from the lock screen, to allow time for onPause to execute.
-    private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
-
     private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
 
     private CameraActivity mActivity;
@@ -285,13 +281,6 @@ public class PhotoModule
             };
     private boolean mShouldResizeTo16x9 = false;
 
-    private final Runnable mResumeTaskRunnable = new Runnable() {
-        @Override
-        public void run() {
-            onResumeTasks();
-        }
-    };
-
     /**
      * We keep the flash setting before entering scene modes (HDR)
      * and restore it after HDR is off.
@@ -1597,11 +1586,9 @@ public class PhotoModule
         focusAndCapture();
     }
 
-    private void onResumeTasks() {
-        if (mPaused) {
-            return;
-        }
-        Log.v(TAG, "Executing onResumeTasks.");
+    @Override
+    public void resume() {
+        mPaused = false;
 
         mCountdownSoundPlayer.loadSound(R.raw.timer_final_second);
         mCountdownSoundPlayer.loadSound(R.raw.timer_increment);
@@ -1701,27 +1688,8 @@ public class PhotoModule
     }
 
     @Override
-    public void resume() {
-        mPaused = false;
-
-        // Add delay on resume from lock screen only, in order to to speed up
-        // the onResume --> onPause --> onResume cycle from lock screen.
-        // Don't do always because letting go of thread can cause delay.
-        if (isResumeFromLockscreen()) {
-            Log.v(TAG, "On resume, from lock screen.");
-            // Note: onPauseAfterSuper() will delete this runnable, so we will
-            // at most have 1 copy queued up.
-            mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
-        } else {
-            Log.v(TAG, "On resume.");
-            onResumeTasks();
-        }
-    }
-
-    @Override
     public void pause() {
         mPaused = true;
-        mHandler.removeCallbacks(mResumeTaskRunnable);
         getServices().getRemoteShutterListener().onModuleExit();
         SessionStatsCollector.instance().sessionActive(false);
 
diff --git a/src/com/android/camera/util/QuickActivity.java b/src/com/android/camera/util/QuickActivity.java
new file mode 100644 (file)
index 0000000..957b972
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera.util;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.MediaStore;
+
+import com.android.camera.debug.Log;
+
+/**
+ * Workaround for lockscreen double-onResume() bug:
+ * <p>
+ * If started from the lockscreen, the activity may be quickly started, resumed,
+ * paused, stopped, and then started and resumed again. This is problematic for
+ * launch time from the lockscreen because we typically open the camera in
+ * onResume() and close it in onPause(). These camera operations take a long
+ * time to complete. To workaround it, this class filters out high-frequency
+ * onResume()->onPause() sequences if the current intent indicates that we have
+ * started from the lockscreen.
+ * </p>
+ * <p>
+ * Subclasses should override the appropriate on[Create|Start...]Tasks() method
+ * in place of the original.
+ * </p>
+ * <p>
+ * Sequences of onResume() followed quickly by onPause(), when the activity is
+ * started from an unsecure lockscreen will result in a quick no-op.<br>
+ * </p>
+ */
+public abstract class QuickActivity extends Activity {
+    private static final Log.Tag TAG = new Log.Tag("QuickActivity");
+
+    /**
+     * The amount of time to wait before running onResumeTasks when started from
+     * the lockscreen.
+     */
+    private static final long ON_RESUME_DELAY_MILLIS = 20;
+    /** A reference to the main handler on which to run lifecycle methods. */
+    private Handler mMainHandler;
+    private boolean mPaused;
+    private boolean mStopped;
+
+    /**
+     * A runnable for deferring tasks to be performed in onResume() if starting
+     * from the lockscreen.
+     */
+    private final Runnable mOnResumeTasks = new Runnable() {
+            @Override
+        public void run() {
+            logLifecycle("onResumeTasks", true);
+            if (mStopped) {
+                onStartTasks();
+                mStopped = false;
+            }
+            if (mPaused) {
+                onResumeTasks();
+                mPaused = false;
+            }
+            logLifecycle("onResumeTasks", false);
+        }
+    };
+
+    @Override
+    protected final void onNewIntent(Intent intent) {
+        logLifecycle("onNewIntent", true);
+        Log.v(TAG, "Intent Action = " + intent.getAction());
+        setIntent(intent);
+        super.onNewIntent(intent);
+        onNewIntentTasks(intent);
+        logLifecycle("onNewIntent", false);
+    }
+
+    @Override
+    protected final void onCreate(Bundle bundle) {
+        logLifecycle("onCreate", true);
+        Log.v(TAG, "Intent Action = " + getIntent().getAction());
+        super.onCreate(bundle);
+
+        mMainHandler = new Handler(getMainLooper());
+
+        onCreateTasks(bundle);
+
+        mPaused = true;
+        mStopped = true;
+
+        logLifecycle("onCreate", false);
+    }
+
+    @Override
+    protected final void onStart() {
+        logLifecycle("onStart", true);
+        if (delayOnResumeOnStart()) {
+            // Do nothing, instead wait for onResume() to be called and then
+            // execute onStartTasks() in mOnResumeTasks.
+        } else {
+            if (mStopped) {
+                onStartTasks();
+                mStopped = false;
+            }
+        }
+        super.onStart();
+        logLifecycle("onStart", false);
+    }
+
+    @Override
+    protected final void onResume() {
+        logLifecycle("onResume", true);
+        mMainHandler.removeCallbacks(mOnResumeTasks);
+        if (delayOnResumeOnStart()) {
+            mMainHandler.postDelayed(mOnResumeTasks, ON_RESUME_DELAY_MILLIS);
+        } else {
+            if (mPaused) {
+                onResumeTasks();
+                mPaused = false;
+            }
+        }
+        super.onResume();
+        logLifecycle("onResume", false);
+    }
+
+    @Override
+    protected final void onPause() {
+        logLifecycle("onPause", true);
+        mMainHandler.removeCallbacks(mOnResumeTasks);
+        if (!mPaused) {
+            onPauseTasks();
+            mPaused = true;
+        }
+        super.onPause();
+        logLifecycle("onPause", false);
+    }
+
+    @Override
+    protected final void onStop() {
+        if (isChangingConfigurations()) {
+            Log.v(TAG, "changing configurations");
+        }
+        logLifecycle("onStop", true);
+        mMainHandler.removeCallbacks(mOnResumeTasks);
+        if (!mStopped) {
+            onStopTasks();
+            mStopped = true;
+        }
+        super.onStop();
+        logLifecycle("onStop", false);
+    }
+
+    @Override
+    protected final void onRestart() {
+        logLifecycle("onRestart", true);
+        super.onRestart();
+        // TODO Support onRestartTasks() and handle the workaround for that too.
+        logLifecycle("onRestart", false);
+    }
+
+    @Override
+    protected final void onDestroy() {
+        logLifecycle("onDestroy", true);
+        onDestroyTasks();
+        super.onDestroy();
+        logLifecycle("onDestroy", false);
+    }
+
+    private void logLifecycle(String methodName, boolean start) {
+        String prefix = start ? "START" : "END";
+        Log.v(TAG, prefix + " " + methodName + ": Activity = " + toString());
+    }
+
+    private boolean delayOnResumeOnStart() {
+        String action = getIntent().getAction();
+        boolean isLockscreenCamera =
+                action.equals(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+        boolean isSecureLockscreenCamera =
+                action.equals(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
+        return isLockscreenCamera || isSecureLockscreenCamera;
+    }
+
+    /**
+     * Subclasses should override this in place of {@link Activity#onNewIntent}.
+     */
+    protected void onNewIntentTasks(Intent newIntent) {
+    }
+
+    /**
+     * Subclasses should override this in place of {@link Activity#onCreate}.
+     */
+    protected void onCreateTasks(Bundle savedInstanceState) {
+    }
+
+    /**
+     * Subclasses should override this in place of {@link Activity#onStart}.
+     */
+    protected void onStartTasks() {
+    }
+
+    /**
+     * Subclasses should override this in place of {@link Activity#onResume}.
+     */
+    protected void onResumeTasks() {
+    }
+
+    /**
+     * Subclasses should override this in place of {@link Activity#onPause}.
+     */
+    protected void onPauseTasks() {
+    }
+
+    /**
+     * Subclasses should override this in place of {@link Activity#onStop}.
+     */
+    protected void onStopTasks() {
+    }
+
+    /**
+     * Subclasses should override this in place of {@link Activity#onDestroy}.
+     */
+    protected void onDestroyTasks() {
+    }
+}