Merge "Move the hotword code to KeyguardHostView" into klp-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
228b35109a
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user