OSDN Git Service

Move the hotword code to KeyguardHostView
authorSandeep Siddhartha <sansid@google.com>
Wed, 4 Sep 2013 21:59:59 +0000 (14:59 -0700)
committerSandeep Siddhartha <sansid@google.com>
Thu, 5 Sep 2013 00:47:34 +0000 (17:47 -0700)
- Try hotword detection on Pattern unlock screen as well - disabled in
  this CL
- Explicitly stop hotword detection when starting the assist intent.

Change-Id: I3d22a9029abf888431113b86e4410ea9e9866c57

packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java

index eedb7d0..00124b0 100644 (file)
@@ -24,6 +24,7 @@ import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.AlertDialog;
+import android.app.PendingIntent;
 import android.app.SearchManager;
 import android.app.admin.DevicePolicyManager;
 import android.appwidget.AppWidgetHost;
@@ -40,6 +41,7 @@ import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.media.RemoteControlClient;
+import android.os.Bundle;
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -47,6 +49,9 @@ import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.speech.hotword.HotwordRecognitionListener;
+import android.speech.hotword.HotwordRecognizer;
+import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
@@ -63,6 +68,11 @@ import java.util.List;
 
 public class KeyguardHostView extends KeyguardViewBase {
     private static final String TAG = "KeyguardHostView";
+    // Don't enable hotword on limited-memory devices.
+    private static final boolean ENABLE_HOTWORD = !ActivityManager.isLowRamDeviceStatic();
+    // Indicates if hotword is enabled, should it also be available on secure keyguard(s).
+    private static final boolean ENABLE_HOTWORD_SECURE = false;
+
     // Transport control states.
     static final int TRANSPORT_GONE = 0;
     static final int TRANSPORT_INVISIBLE = 1;
@@ -77,6 +87,13 @@ public class KeyguardHostView extends KeyguardViewBase {
     // Found in KeyguardAppWidgetPickActivity.java
     static final int APPWIDGET_HOST_ID = 0x4B455947;
 
+    // TODO: Fix this to be non-static.
+    // We need to be careful here to make stopRecognition calls on the same instance
+    // that started it. Since KeyguardHostView is a view, it keeps getting
+    // recreated every now and then, and unless we figure out a better way,
+    // this needs to be a static field.
+    private static HotwordRecognizer sHotwordClient;
+
     private final int MAX_WIDGETS = 5;
 
     private AppWidgetHost mAppWidgetHost;
@@ -117,6 +134,8 @@ public class KeyguardHostView extends KeyguardViewBase {
 
     private KeyguardMultiUserSelectorView mKeyguardMultiUserSelectorView;
 
+    private boolean mIsScreenOn;
+
     protected int mClientGeneration;
 
     protected boolean mShowSecurityWhenReturn;
@@ -198,6 +217,11 @@ public class KeyguardHostView extends KeyguardViewBase {
         if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0) {
             Log.v(TAG, "Keyguard secure camera disabled by DPM");
         }
+
+        // Create Hotword recognizer, for the first time.
+        if (ENABLE_HOTWORD && sHotwordClient == null) {
+            sHotwordClient = HotwordRecognizer.createHotwordRecognizer(getContext());
+        }
     }
 
     private void getInitialTransportState() {
@@ -295,6 +319,21 @@ public class KeyguardHostView extends KeyguardViewBase {
                 }
             }
         }
+        @Override
+        public void onPhoneStateChanged(int phoneState) {
+            // We need to stop hotword detection when a call state is not idle anymore.
+            if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)
+                    && TelephonyManager.CALL_STATE_IDLE != phoneState) {
+                if (DEBUG) Log.d(TAG, "Stopping due to call state not being idle");
+                maybeStopHotwordDetector();
+            }
+        }
+        @Override
+        public void onUserSwitching(int userId) {
+            if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) {
+                maybeStopHotwordDetector();
+            }
+        }
     };
 
     private static final boolean isMusicPlaying(int playbackState) {
@@ -778,6 +817,9 @@ public class KeyguardHostView extends KeyguardViewBase {
             // If the alternate unlock was suppressed, it can now be safely
             // enabled because the user has left keyguard.
             KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
+            if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)){
+                maybeStopHotwordDetector();
+            }
 
             // If there's a pending runnable because the user interacted with a widget
             // and we're leaving keyguard, then run it.
@@ -942,6 +984,9 @@ public class KeyguardHostView extends KeyguardViewBase {
 
         // Emulate Activity life cycle
         if (oldView != null) {
+            if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) {
+                maybeStopHotwordDetector();
+            }
             oldView.onPause();
             oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
         }
@@ -982,6 +1027,7 @@ public class KeyguardHostView extends KeyguardViewBase {
     @Override
     public void onScreenTurnedOn() {
         if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
+        mIsScreenOn = true;
         showPrimarySecurityScreen(false);
         getSecurityView(mCurrentSecuritySelection).onResume(KeyguardSecurityView.SCREEN_ON);
 
@@ -993,6 +1039,12 @@ public class KeyguardHostView extends KeyguardViewBase {
         if (mViewStateManager != null) {
             mViewStateManager.showUsabilityHints();
         }
+
+        // Start hotword detection on insecure Keyguard.
+        if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) {
+            maybeStartHotwordDetector();
+        }
+
         requestFocus();
     }
 
@@ -1000,6 +1052,7 @@ public class KeyguardHostView extends KeyguardViewBase {
     public void onScreenTurnedOff() {
         if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
                 Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
+        mIsScreenOn = false;
         // Once the screen turns off, we no longer consider this to be first boot and we want the
         // biometric unlock to start next time keyguard is shown.
         KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
@@ -1013,6 +1066,12 @@ public class KeyguardHostView extends KeyguardViewBase {
         if (cameraPage != null) {
             cameraPage.onScreenTurnedOff();
         }
+
+        // Stop hotword detection on insecure Keyguard.
+        if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) {
+            maybeStopHotwordDetector();
+        }
+
         clearFocus();
     }
 
@@ -1097,6 +1156,9 @@ public class KeyguardHostView extends KeyguardViewBase {
         new CameraWidgetFrame.Callbacks() {
             @Override
             public void onLaunchingCamera() {
+                if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) {
+                    maybeStopHotwordDetector();
+                }
                 setSliderHandleAlpha(0);
             }
 
@@ -1626,6 +1688,9 @@ public class KeyguardHostView extends KeyguardViewBase {
     }
 
     public void showAssistant() {
+        if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) {
+            maybeStopHotwordDetector();
+        }
         final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
           .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
 
@@ -1640,4 +1705,130 @@ public class KeyguardHostView extends KeyguardViewBase {
         mActivityLauncher.launchActivityWithAnimation(
                 intent, false, opts.toBundle(), null, null);
     }
+
+
+    /**
+     * Start the hotword detector if:
+     * <li> ENABLE_HOTWORD is true and
+     * <li> Hotword detection is not already running and
+     * <li> TelephonyManager is in CALL_STATE_IDLE
+     * <li> and Screen is turned on.
+     */
+    private void maybeStartHotwordDetector() {
+        if (!ENABLE_HOTWORD) return;
+
+        if (sHotwordClient != null) {
+            if (DEBUG) Log.d(TAG, "maybeStartHotwordDetector()");
+            // Don't start hotword detection if the screen is off.
+            if (!mIsScreenOn) {
+                if (DEBUG) Log.d(TAG, "screen was off, not starting");
+                return;
+            }
+
+            KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(getContext());
+            if (monitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE) {
+                if (DEBUG) Log.d(TAG, "Call underway, not starting");
+                return;
+            }
+
+            try {
+                sHotwordClient.startRecognition(mHotwordCallback);
+            } catch(Exception ex) {
+                // Don't allow hotword errors to make the keyguard unusable
+                Log.e(TAG, "Failed to start hotword recognition", ex);
+                sHotwordClient = null;
+            }
+        }
+    }
+
+    /**
+     * Stop hotword detector if:
+     * <li> ENABLE_HOTWORD is true
+     * <li> and hotword is running.
+     */
+    private void maybeStopHotwordDetector() {
+        if (!ENABLE_HOTWORD) return;
+
+        if (sHotwordClient != null) {
+            if (DEBUG) Log.d(TAG, "maybeStopHotwordDetector()");
+            try {
+                sHotwordClient.stopRecognition();
+            } catch(Exception ex) {
+                // Don't allow hotword errors to make the keyguard unusable
+                Log.e(TAG, "Failed to start hotword recognition", ex);
+            } finally {
+                sHotwordClient = null;
+            }
+        }
+    }
+
+    private final HotwordRecognitionListener mHotwordCallback = new HotwordRecognitionListener() {
+        private static final String TAG = "HotwordRecognitionListener";
+
+        public void onHotwordRecognitionStarted() {
+            if (DEBUG) Log.d(TAG, "onHotwordRecognitionStarted()");
+        }
+
+        public void onHotwordRecognitionStopped() {
+            if (DEBUG) Log.d(TAG, "onHotwordRecognitionStopped()");
+        }
+
+        public void onHotwordEvent(int eventType, Bundle eventBundle) {
+            if (DEBUG) Log.d(TAG, "onHotwordEvent: " + eventType);
+            if (eventType == HotwordRecognizer.EVENT_TYPE_STATE_CHANGED) {
+                if (eventBundle != null && eventBundle.containsKey(HotwordRecognizer.PROMPT_TEXT)) {
+                    new KeyguardMessageArea.Helper(
+                            (View) getSecurityView(mCurrentSecuritySelection))
+                        .setMessage(eventBundle.getString(HotwordRecognizer.PROMPT_TEXT),true);
+                }
+            }
+        }
+
+        public void onHotwordRecognized(final PendingIntent intent) {
+            if (DEBUG) Log.d(TAG, "onHotwordRecognized");
+            maybeStopHotwordDetector();
+            if (SecurityMode.None == mCurrentSecuritySelection) {
+                if (intent != null) {
+                    try {
+                        intent.send();
+                    } catch (PendingIntent.CanceledException e) {
+                        Log.w(TAG, "Failed to launch PendingIntent. Encountered CanceledException");
+                    }
+                }
+                mCallback.userActivity(0);
+                mCallback.dismiss(false);
+            } else if (ENABLE_HOTWORD_SECURE && mLockPatternUtils.isSecure()) {
+                setOnDismissAction(new OnDismissAction() {
+                    @Override
+                    public boolean onDismiss() {
+                        if (intent != null) {
+                            try {
+                                intent.send();
+                            } catch (PendingIntent.CanceledException e) {
+                                Log.w(TAG, "Failed to launch PendingIntent."
+                                        + "Encountered CanceledException");
+                            }
+                        }
+                        return false;
+                    }
+                });
+                getSecurityView(mCurrentSecuritySelection).showBouncer(0);
+            }
+        }
+
+        public void onHotwordError(int errorCode) {
+            if (DEBUG) Log.d(TAG, "onHotwordError: " + errorCode);
+            // TODO: Inspect the error code and handle the errors appropriately
+            // instead of blindly failing.
+            maybeStopHotwordDetector();
+        }
+    };
+
+    private boolean shouldRunHotwordInSecurityMode(SecurityMode mode) {
+        // Enable hotoword for insecure keyguard,
+        // and for pattern unlock if ENABLE_HOTWORD_SECURE is true.
+        return ENABLE_HOTWORD
+                && ((SecurityMode.None == mode)
+                        || (ENABLE_HOTWORD_SECURE && mLockPatternUtils.isSecure()));
+    }
 }
index 40d55cf..63be102 100644 (file)
@@ -28,8 +28,6 @@ import android.os.Bundle;
 import android.os.PowerManager;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.speech.hotword.HotwordRecognitionListener;
-import android.speech.hotword.HotwordRecognizer;
 import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -48,12 +46,6 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
     private static final String ASSIST_ICON_METADATA_NAME =
         "com.android.systemui.action_assist_icon";
 
-    // Don't enable hotword on limited-memory devices.
-    private static final boolean ENABLE_HOTWORD = !ActivityManager.isLowRamDeviceStatic();
-
-    // TODO: Fix this to be non-static.
-    private static HotwordRecognizer sHotwordClient;
-
     private KeyguardSecurityCallback mCallback;
     private GlowPadView mGlowPadView;
     private ObjectAnimator mAnim;
@@ -69,7 +61,6 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
 
         public void onTrigger(View v, int target) {
             final int resId = mGlowPadView.getResourceIdForTarget(target);
-            maybeStopHotwordDetector();
 
             switch (resId) {
                 case R.drawable.ic_action_assist_generic:
@@ -129,22 +120,6 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
         public void onSimStateChanged(State simState) {
             updateTargets();
         }
-
-        @Override
-        public void onPhoneStateChanged(int phoneState) {
-            if (ENABLE_HOTWORD) {
-                // We need to stop hotword detection when a call state is not idle anymore.
-                if (phoneState != TelephonyManager.CALL_STATE_IDLE) {
-                    if (DEBUG) Log.d(TAG, "Stopping due to call state not being idle");
-                    maybeStopHotwordDetector();
-                }
-            }
-        }
-
-        @Override
-        public void onUserSwitching(int userId) {
-            maybeStopHotwordDetector();
-        }
     };
 
     private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
@@ -183,9 +158,6 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
         mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
         View bouncerFrameView = findViewById(R.id.keyguard_selector_view_frame);
         mBouncerFrame = bouncerFrameView.getBackground();
-        if (ENABLE_HOTWORD && sHotwordClient == null) {
-            sHotwordClient = HotwordRecognizer.createHotwordRecognizer(getContext());
-        }
     }
 
     public void setCarrierArea(View carrierArea) {
@@ -289,20 +261,11 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
     @Override
     public void onPause() {
         KeyguardUpdateMonitor.getInstance(getContext()).removeCallback(mUpdateCallback);
-        maybeStopHotwordDetector();
     }
 
     @Override
     public void onResume(int reason) {
         KeyguardUpdateMonitor.getInstance(getContext()).registerCallback(mUpdateCallback);
-        // TODO: Figure out if there's a better way to do it.
-        // onResume gets called multiple times, however we are interested in
-        // the reason to figure out when to start/stop hotword detection.
-        if (reason == SCREEN_ON) {
-            if (!KeyguardUpdateMonitor.getInstance(getContext()).isSwitchingUser()) {
-                maybeStartHotwordDetector();
-            }
-        }
     }
 
     @Override
@@ -323,100 +286,4 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
         KeyguardSecurityViewHelper.
                 hideBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration);
     }
-
-    /**
-     * Start the hotword detector if:
-     * <li> FLAG_HOTWORD is true and
-     * <li> Hotword detection is not already running and
-     * <li> TelephonyManager is in CALL_STATE_IDLE
-     *
-     * If this method is called when the screen is off,
-     * it attempts to stop hotword detection if it's running.
-     */
-    private void maybeStartHotwordDetector() {
-        if (ENABLE_HOTWORD && sHotwordClient != null) {
-            if (DEBUG) Log.d(TAG, "maybeStartHotwordDetector()");
-            // Don't start it if the screen is off or not showing
-            PowerManager powerManager = (PowerManager) getContext().getSystemService(
-                    Context.POWER_SERVICE);
-            if (!powerManager.isScreenOn()) {
-                if (DEBUG) Log.d(TAG, "screen was off, not starting");
-                return;
-            }
-
-            KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(getContext());
-            if (monitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE) {
-                if (DEBUG) Log.d(TAG, "Call underway, not starting");
-                return;
-            }
-
-            try {
-                sHotwordClient.startRecognition(mHotwordCallback);
-            } catch(Exception ex) {
-                // Don't allow hotword errors to make the keyguard unusable
-                Log.e(TAG, "Failed to start hotword recognition", ex);
-                sHotwordClient = null;
-            }
-        }
-    }
-
-    /**
-     * Stop hotword detector if HOTWORDING_ENABLED is true.
-     */
-    private void maybeStopHotwordDetector() {
-        if (ENABLE_HOTWORD && sHotwordClient != null) {
-            if (DEBUG) Log.d(TAG, "maybeStopHotwordDetector()");
-            try {
-                sHotwordClient.stopRecognition();
-            } catch(Exception ex) {
-                // Don't allow hotword errors to make the keyguard unusable
-                Log.e(TAG, "Failed to start hotword recognition", ex);
-            } finally {
-                sHotwordClient = null;
-            }
-        }
-    }
-
-    private final HotwordRecognitionListener mHotwordCallback = new HotwordRecognitionListener() {
-        private static final String TAG = "HotwordRecognitionListener";
-
-        public void onHotwordRecognitionStarted() {
-            if (DEBUG) Log.d(TAG, "onHotwordRecognitionStarted()");
-        }
-
-        public void onHotwordRecognitionStopped() {
-            if (DEBUG) Log.d(TAG, "onHotwordRecognitionStopped()");
-        }
-
-        public void onHotwordEvent(int eventType, Bundle eventBundle) {
-            if (DEBUG) Log.d(TAG, "onHotwordEvent: " + eventType);
-            if (eventType == HotwordRecognizer.EVENT_TYPE_STATE_CHANGED) {
-                if (eventBundle != null && eventBundle.containsKey(HotwordRecognizer.PROMPT_TEXT)) {
-                    mSecurityMessageDisplay.setMessage(
-                            eventBundle.getString(HotwordRecognizer.PROMPT_TEXT), true);
-                }
-            }
-        }
-
-        public void onHotwordRecognized(PendingIntent intent) {
-            if (DEBUG) Log.d(TAG, "onHotwordRecognized");
-            maybeStopHotwordDetector();
-            if (intent != null) {
-                try {
-                    intent.send();
-                } catch (PendingIntent.CanceledException e) {
-                    Log.w(TAG, "Failed to launch PendingIntent. Encountered CanceledException");
-                }
-            }
-            mCallback.userActivity(0);
-            mCallback.dismiss(false);
-        }
-
-        public void onHotwordError(int errorCode) {
-            if (DEBUG) Log.d(TAG, "onHotwordError: " + errorCode);
-            // TODO: Inspect the error code and handle the errors appropriately
-            // instead of blindly failing.
-            maybeStopHotwordDetector();
-        }
-    };
 }