Home | History | Annotate | Download | only in util
      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
     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
    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
    119     protected final void onStart() {
    120         logLifecycle("onStart", true);
    121         onStartTasks();
    122         super.onStart();
    123         logLifecycle("onStart", false);
    124     }
    125 
    126     @Override
    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
    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
    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
    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
    195     protected final void onDestroy() {
    196         logLifecycle("onDestroy", true);
    197         onDestroyTasks();
    198         super.onDestroy();
    199         logLifecycle("onDestroy", false);
    200     }
    201 
    202     private void logLifecycle(String methodName, boolean start) {
    203         String prefix = start ? "START" : "END";
    204         Log.v(TAG, prefix + " " + methodName + ": Activity = " + toString());
    205     }
    206 
    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 
    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 
    227     /**
    228      * Subclasses should override this in place of {@link Activity#onNewIntent}.
    229      */
    230     protected void onNewIntentTasks(Intent newIntent) {
    231     }
    232 
    233     /**
    234      * Subclasses should override this in place of {@link Activity#onCreate}.
    235      */
    236     protected void onCreateTasks(Bundle savedInstanceState) {
    237     }
    238 
    239     /**
    240      * Subclasses should override this in place of {@link Activity#onStart}.
    241      */
    242     protected void onStartTasks() {
    243     }
    244 
    245     /**
    246      * Subclasses should override this in place of {@link Activity#onResume}.
    247      */
    248     protected void onResumeTasks() {
    249     }
    250 
    251     /**
    252      * Subclasses should override this in place of {@link Activity#onPause}.
    253      */
    254     protected void onPauseTasks() {
    255     }
    256 
    257     /**
    258      * Subclasses should override this in place of {@link Activity#onStop}.
    259      */
    260     protected void onStopTasks() {
    261     }
    262 
    263     /**
    264      * Subclasses should override this in place of {@link Activity#onDestroy}.
    265      */
    266     protected void onDestroyTasks() {
    267     }
    268 }
    269