1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.camera.util; 18 19 import android.app.Activity; 20 import android.app.KeyguardManager; 21 import android.content.Intent; 22 import android.os.Bundle; 23 import android.os.Handler; 24 import android.os.SystemClock; 25 26 import com.android.camera.debug.Log; 27 import javax.annotation.Nullable; 28 29 /** 30 * Workaround for lockscreen double-onResume() bug: 31 * <p> 32 * We track 3 startup situations: 33 * <ul> 34 * <li>Normal startup -- e.g. from GEL.</li> 35 * <li>Secure lock screen startup -- e.g. with a keycode.</li> 36 * <li>Non-secure lock screen startup -- e.g. with just a swipe.</li> 37 * </ul> 38 * The KeyguardManager service can be queried to determine which state we are in. 39 * If started from the lock screen, the activity may be quickly started, 40 * resumed, paused, stopped, and then started and resumed again. This is 41 * problematic for launch time from the lock screen because we typically open the 42 * camera in onResume() and close it in onPause(). These camera operations take 43 * a long time to complete. To workaround it, this class filters out 44 * high-frequency onResume()->onPause() sequences if the KeyguardManager 45 * indicates that we have started from the lock screen. 46 * </p> 47 * <p> 48 * Subclasses should override the appropriate on[Create|Start...]Tasks() method 49 * in place of the original. 50 * </p> 51 * <p> 52 * Sequences of onResume() followed quickly by onPause(), when the activity is 53 * started from a lockscreen will result in a quick no-op.<br> 54 * </p> 55 */ 56 public abstract class QuickActivity extends Activity { 57 private static final Log.Tag TAG = new Log.Tag("QuickActivity"); 58 59 /** onResume tasks delay from secure lockscreen. */ 60 private static final long ON_RESUME_DELAY_SECURE_MILLIS = 30; 61 /** onResume tasks delay from non-secure lockscreen. */ 62 private static final long ON_RESUME_DELAY_NON_SECURE_MILLIS = 15; 63 64 /** A reference to the main handler on which to run lifecycle methods. */ 65 private Handler mMainHandler; 66 67 /** 68 * True if onResume tasks have been skipped, and made false again once they 69 * are executed within the onResume() method or from a delayed Runnable. 70 */ 71 private boolean mSkippedFirstOnResume = false; 72 73 /** When application execution started in SystemClock.elapsedRealtimeNanos(). */ 74 protected long mExecutionStartNanoTime = 0; 75 /** Was this session started with onCreate(). */ 76 protected boolean mStartupOnCreate = false; 77 78 /** Handle to Keyguard service. */ 79 @Nullable 80 private KeyguardManager mKeyguardManager = null; 81 /** 82 * A runnable for deferring tasks to be performed in onResume() if starting 83 * from the lockscreen. 84 */ 85 private final Runnable mOnResumeTasks = new Runnable() { 86 @Override 87 public void run() { 88 if (mSkippedFirstOnResume) { 89 Log.v(TAG, "delayed Runnable --> onResumeTasks()"); 90 // Doing the tasks, can set to false. 91 mSkippedFirstOnResume = false; 92 onResumeTasks(); 93 } 94 } 95 }; 96 97 @Override onNewIntent(Intent intent)98 protected final void onNewIntent(Intent intent) { 99 logLifecycle("onNewIntent", true); 100 Log.v(TAG, "Intent Action = " + intent.getAction()); 101 setIntent(intent); 102 super.onNewIntent(intent); 103 onNewIntentTasks(intent); 104 logLifecycle("onNewIntent", false); 105 } 106 107 @Override onCreate(Bundle bundle)108 protected final void onCreate(Bundle bundle) { 109 mExecutionStartNanoTime = SystemClock.elapsedRealtimeNanos(); 110 logLifecycle("onCreate", true); 111 mStartupOnCreate = true; 112 super.onCreate(bundle); 113 mMainHandler = new Handler(getMainLooper()); 114 onCreateTasks(bundle); 115 logLifecycle("onCreate", false); 116 } 117 118 @Override onStart()119 protected final void onStart() { 120 logLifecycle("onStart", true); 121 onStartTasks(); 122 super.onStart(); 123 logLifecycle("onStart", false); 124 } 125 126 @Override onResume()127 protected final void onResume() { 128 logLifecycle("onResume", true); 129 130 // For lockscreen launch, there are two possible flows: 131 // 1. onPause() does not occur before mOnResumeTasks is executed: 132 // Runnable mOnResumeTasks sets mSkippedFirstOnResume to false 133 // 2. onPause() occurs within ON_RESUME_DELAY_*_MILLIS: 134 // a. Runnable mOnResumeTasks is removed 135 // b. onPauseTasks() is skipped, mSkippedFirstOnResume remains true 136 // c. next onResume() will immediately execute onResumeTasks() 137 // and set mSkippedFirstOnResume to false 138 139 Log.v(TAG, "onResume(): isKeyguardLocked() = " + isKeyguardLocked()); 140 mMainHandler.removeCallbacks(mOnResumeTasks); 141 if (isKeyguardLocked() && mSkippedFirstOnResume == false) { 142 // Skipping onResumeTasks; set to true. 143 mSkippedFirstOnResume = true; 144 long delay = isKeyguardSecure() ? ON_RESUME_DELAY_SECURE_MILLIS : 145 ON_RESUME_DELAY_NON_SECURE_MILLIS; 146 Log.v(TAG, "onResume() --> postDelayed(mOnResumeTasks," + delay + ")"); 147 mMainHandler.postDelayed(mOnResumeTasks, delay); 148 } else { 149 Log.v(TAG, "onResume --> onResumeTasks()"); 150 // Doing the tasks, can set to false. 151 mSkippedFirstOnResume = false; 152 onResumeTasks(); 153 } 154 super.onResume(); 155 logLifecycle("onResume", false); 156 } 157 158 @Override onPause()159 protected final void onPause() { 160 logLifecycle("onPause", true); 161 mMainHandler.removeCallbacks(mOnResumeTasks); 162 // Only run onPauseTasks if we have not skipped onResumeTasks in a 163 // first call to onResume. If we did skip onResumeTasks (note: we 164 // just killed any delayed Runnable), we also skip onPauseTasks to 165 // adhere to lifecycle state machine. 166 if (mSkippedFirstOnResume == false) { 167 Log.v(TAG, "onPause --> onPauseTasks()"); 168 onPauseTasks(); 169 } 170 super.onPause(); 171 mStartupOnCreate = false; 172 logLifecycle("onPause", false); 173 } 174 175 @Override onStop()176 protected final void onStop() { 177 if (isChangingConfigurations()) { 178 Log.v(TAG, "changing configurations"); 179 } 180 logLifecycle("onStop", true); 181 onStopTasks(); 182 super.onStop(); 183 logLifecycle("onStop", false); 184 } 185 186 @Override onRestart()187 protected final void onRestart() { 188 logLifecycle("onRestart", true); 189 super.onRestart(); 190 // TODO Support onRestartTasks() and handle the workaround for that too. 191 logLifecycle("onRestart", false); 192 } 193 194 @Override onDestroy()195 protected final void onDestroy() { 196 logLifecycle("onDestroy", true); 197 onDestroyTasks(); 198 super.onDestroy(); 199 logLifecycle("onDestroy", false); 200 } 201 logLifecycle(String methodName, boolean start)202 private void logLifecycle(String methodName, boolean start) { 203 String prefix = start ? "START" : "END"; 204 Log.v(TAG, prefix + " " + methodName + ": Activity = " + toString()); 205 } 206 isKeyguardLocked()207 protected boolean isKeyguardLocked() { 208 if (mKeyguardManager == null) { 209 mKeyguardManager = AndroidServices.instance().provideKeyguardManager(); 210 } 211 if (mKeyguardManager != null) { 212 return mKeyguardManager.isKeyguardLocked(); 213 } 214 return false; 215 } 216 isKeyguardSecure()217 protected boolean isKeyguardSecure() { 218 if (mKeyguardManager == null) { 219 mKeyguardManager = AndroidServices.instance().provideKeyguardManager(); 220 } 221 if (mKeyguardManager != null) { 222 return mKeyguardManager.isKeyguardSecure(); 223 } 224 return false; 225 } 226 requestDismissKeyguard(Activity activity, @Nullable KeyguardManager.KeyguardDismissCallback callback)227 protected void requestDismissKeyguard(Activity activity, 228 @Nullable KeyguardManager.KeyguardDismissCallback callback) { 229 if (mKeyguardManager == null) { 230 mKeyguardManager = AndroidServices.instance().provideKeyguardManager(); 231 } 232 if (mKeyguardManager != null) { 233 mKeyguardManager.requestDismissKeyguard(activity, callback); 234 } 235 } 236 237 /** 238 * Subclasses should override this in place of {@link Activity#onNewIntent}. 239 */ onNewIntentTasks(Intent newIntent)240 protected void onNewIntentTasks(Intent newIntent) { 241 } 242 243 /** 244 * Subclasses should override this in place of {@link Activity#onCreate}. 245 */ onCreateTasks(Bundle savedInstanceState)246 protected void onCreateTasks(Bundle savedInstanceState) { 247 } 248 249 /** 250 * Subclasses should override this in place of {@link Activity#onStart}. 251 */ onStartTasks()252 protected void onStartTasks() { 253 } 254 255 /** 256 * Subclasses should override this in place of {@link Activity#onResume}. 257 */ onResumeTasks()258 protected void onResumeTasks() { 259 } 260 261 /** 262 * Subclasses should override this in place of {@link Activity#onPause}. 263 */ onPauseTasks()264 protected void onPauseTasks() { 265 } 266 267 /** 268 * Subclasses should override this in place of {@link Activity#onStop}. 269 */ onStopTasks()270 protected void onStopTasks() { 271 } 272 273 /** 274 * Subclasses should override this in place of {@link Activity#onDestroy}. 275 */ onDestroyTasks()276 protected void onDestroyTasks() { 277 } 278 } 279