The voice assist may now be launched above the lockscreen

A possibility was introduced to launch voice assist over
the lockscreen using the left keyguard affordance.

Change-Id: Ic4618d24256b65441a50d77d0ef59b0ec99b6ead
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java
rename to packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index d9f2324..d1f8963 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -33,11 +33,11 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 /**
- * Class to manage everything around the assist gesture.
+ * Class to manage everything related to assist in SystemUI.
  */
-public class AssistGestureManager {
+public class AssistManager {
 
-    private static final String TAG = "AssistGestureManager";
+    private static final String TAG = "AssistManager";
     private static final String ASSIST_ICON_METADATA_NAME =
             "com.android.systemui.action_assist_icon";
 
@@ -77,7 +77,7 @@
         }
     };
 
-    public AssistGestureManager(PhoneStatusBar bar, Context context) {
+    public AssistManager(PhoneStatusBar bar, Context context) {
         mContext = context;
         mBar = bar;
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
@@ -194,6 +194,14 @@
         }
     }
 
+    public void launchVoiceAssistFromKeyguard() {
+        try {
+            mVoiceInteractionManagerService.launchVoiceAssistFromKeyguard();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to call launchVoiceAssistFromKeyguard", e);
+        }
+    }
+
     private boolean getVoiceInteractorSupportsAssistGesture() {
         try {
             return mVoiceInteractionManagerService.activeServiceSupportsAssist();
@@ -203,7 +211,16 @@
         }
     }
 
-    private ComponentName getVoiceInteractorComponentName() {
+    public boolean canVoiceAssistBeLaunchedFromKeyguard() {
+        try {
+            return mVoiceInteractionManagerService.activeServiceSupportsLaunchFromKeyguard();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to call activeServiceSupportsLaunchFromKeyguard", e);
+            return false;
+        }
+    }
+
+    public ComponentName getVoiceInteractorComponentName() {
         try {
             return mVoiceInteractionManagerService.getActiveServiceComponentName();
         } catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java
index 23810f9..ee5eb38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java
@@ -25,4 +25,5 @@
  */
 public interface ActivityStarter {
     public void startActivity(Intent intent, boolean dismissShade);
+    void preventNextAnimation();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 8343497..8bffdc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -131,6 +131,10 @@
         mCenterIcon = mCallback.getCenterIcon();
         mRightIcon = mCallback.getRightIcon();
         mRightIcon.setIsLeft(false);
+        updatePreviews();
+    }
+
+    public void updatePreviews() {
         mLeftIcon.setPreviewView(mCallback.getLeftPreview());
         mRightIcon.setPreviewView(mCallback.getRightPreview());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index fabc1a6..efc3ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -25,17 +25,13 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
-import android.hardware.fingerprint.FingerprintManager;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.os.Vibrator;
 import android.provider.MediaStore;
-import android.provider.Settings;
 import android.telecom.TelecomManager;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -54,6 +50,7 @@
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
+import com.android.systemui.assist.AssistManager;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -85,12 +82,12 @@
     private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
 
     private KeyguardAffordanceView mCameraImageView;
-    private KeyguardAffordanceView mPhoneImageView;
+    private KeyguardAffordanceView mLeftAffordanceView;
     private LockIcon mLockIcon;
     private TextView mIndicationText;
     private ViewGroup mPreviewContainer;
 
-    private View mPhonePreview;
+    private View mLeftPreview;
     private View mCameraPreview;
 
     private ActivityStarter mActivityStarter;
@@ -104,6 +101,8 @@
 
     private final Interpolator mLinearOutSlowInInterpolator;
     private boolean mPrewarmSent;
+    private boolean mLeftIsVoiceAssist;
+    private AssistManager mAssistManager;
 
     public KeyguardBottomAreaView(Context context) {
         this(context, null);
@@ -133,8 +132,12 @@
                 label = getResources().getString(R.string.unlock_label);
             } else if (host == mCameraImageView) {
                 label = getResources().getString(R.string.camera_label);
-            } else if (host == mPhoneImageView) {
-                label = getResources().getString(R.string.phone_label);
+            } else if (host == mLeftAffordanceView) {
+                if (mLeftIsVoiceAssist) {
+                    label = getResources().getString(R.string.voice_assist_label);
+                } else {
+                    label = getResources().getString(R.string.phone_label);
+                }
             }
             info.addAction(new AccessibilityAction(ACTION_CLICK, label));
         }
@@ -149,8 +152,8 @@
                 } else if (host == mCameraImageView) {
                     launchCamera();
                     return true;
-                } else if (host == mPhoneImageView) {
-                    launchPhone();
+                } else if (host == mLeftAffordanceView) {
+                    launchLeftAffordance();
                     return true;
                 }
             }
@@ -164,29 +167,28 @@
         mLockPatternUtils = new LockPatternUtils(mContext);
         mPreviewContainer = (ViewGroup) findViewById(R.id.preview_container);
         mCameraImageView = (KeyguardAffordanceView) findViewById(R.id.camera_button);
-        mPhoneImageView = (KeyguardAffordanceView) findViewById(R.id.phone_button);
+        mLeftAffordanceView = (KeyguardAffordanceView) findViewById(R.id.left_button);
         mLockIcon = (LockIcon) findViewById(R.id.lock_icon);
         mIndicationText = (TextView) findViewById(R.id.keyguard_indication_text);
         watchForCameraPolicyChanges();
         updateCameraVisibility();
-        updatePhoneVisibility();
         mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
         mUnlockMethodCache.addListener(this);
         mLockIcon.update();
         setClipChildren(false);
         setClipToPadding(false);
         mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext));
-        inflatePreviews();
+        inflateCameraPreview();
         mLockIcon.setOnClickListener(this);
         mLockIcon.setOnLongClickListener(this);
         mCameraImageView.setOnClickListener(this);
-        mPhoneImageView.setOnClickListener(this);
+        mLeftAffordanceView.setOnClickListener(this);
         initAccessibility();
     }
 
     private void initAccessibility() {
         mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
-        mPhoneImageView.setAccessibilityDelegate(mAccessibilityDelegate);
+        mLeftAffordanceView.setAccessibilityDelegate(mAccessibilityDelegate);
         mCameraImageView.setAccessibilityDelegate(mAccessibilityDelegate);
     }
 
@@ -247,9 +249,26 @@
         mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
-    private void updatePhoneVisibility() {
-        boolean visible = isPhoneVisible();
-        mPhoneImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
+    private void updateLeftAffordanceIcon() {
+        mLeftIsVoiceAssist = canLaunchVoiceAssist();
+        int drawableId;
+        int contentDescription;
+        if (mLeftIsVoiceAssist) {
+            mLeftAffordanceView.setVisibility(View.VISIBLE);
+            drawableId = R.drawable.ic_mic_26dp;
+            contentDescription = R.string.accessibility_voice_assist_button;
+        } else {
+            boolean visible = isPhoneVisible();
+            mLeftAffordanceView.setVisibility(visible ? View.VISIBLE : View.GONE);
+            drawableId = R.drawable.ic_phone_24dp;
+            contentDescription = R.string.accessibility_phone_button;
+        }
+        mLeftAffordanceView.setImageDrawable(mContext.getDrawable(drawableId));
+        mLeftAffordanceView.setContentDescription(mContext.getString(contentDescription));
+    }
+
+    public boolean isLeftVoiceAssist() {
+        return mLeftIsVoiceAssist;
     }
 
     private boolean isPhoneVisible() {
@@ -287,9 +306,9 @@
     @Override
     public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) {
         mCameraImageView.setClickable(touchExplorationEnabled);
-        mPhoneImageView.setClickable(touchExplorationEnabled);
+        mLeftAffordanceView.setClickable(touchExplorationEnabled);
         mCameraImageView.setFocusable(accessibilityEnabled);
-        mPhoneImageView.setFocusable(accessibilityEnabled);
+        mLeftAffordanceView.setFocusable(accessibilityEnabled);
         mLockIcon.update();
     }
 
@@ -297,8 +316,8 @@
     public void onClick(View v) {
         if (v == mCameraImageView) {
             launchCamera();
-        } else if (v == mPhoneImageView) {
-            launchPhone();
+        } else if (v == mLeftAffordanceView) {
+            launchLeftAffordance();
         } if (v == mLockIcon) {
             if (!mAccessibilityController.isAccessibilityEnabled()) {
                 handleTrustCircleClick();
@@ -363,6 +382,7 @@
                 @Override
                 public void run() {
                     mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+                    mActivityStarter.preventNextAnimation();
                 }
             });
         } else {
@@ -373,7 +393,35 @@
         }
     }
 
-    public void launchPhone() {
+    public void launchLeftAffordance() {
+        if (mLeftIsVoiceAssist) {
+            launchVoiceAssist();
+        } else {
+            launchPhone();
+        }
+    }
+
+    private void launchVoiceAssist() {
+        Runnable runnable = new Runnable() {
+            @Override
+            public void run() {
+                mAssistManager.launchVoiceAssistFromKeyguard();
+                mActivityStarter.preventNextAnimation();
+            }
+        };
+        if (mPhoneStatusBar.isKeyguardCurrentlySecure()) {
+            AsyncTask.execute(runnable);
+        } else {
+            mPhoneStatusBar.executeRunnableDismissingKeyguard(runnable, false /* dismissShade */,
+                    false /* afterKeyguardGone */);
+        }
+    }
+
+    private boolean canLaunchVoiceAssist() {
+        return mAssistManager.canVoiceAssistBeLaunchedFromKeyguard();
+    }
+
+    private void launchPhone() {
         final TelecomManager tm = TelecomManager.from(mContext);
         if (tm.isInCall()) {
             AsyncTask.execute(new Runnable() {
@@ -397,19 +445,19 @@
         }
     }
 
-    public KeyguardAffordanceView getPhoneView() {
-        return mPhoneImageView;
+    public KeyguardAffordanceView getLeftView() {
+        return mLeftAffordanceView;
     }
 
-    public KeyguardAffordanceView getCameraView() {
+    public KeyguardAffordanceView getRightView() {
         return mCameraImageView;
     }
 
-    public View getPhonePreview() {
-        return mPhonePreview;
+    public View getLeftPreview() {
+        return mLeftPreview;
     }
 
-    public View getCameraPreview() {
+    public View getRightPreview() {
         return mCameraPreview;
     }
 
@@ -432,23 +480,35 @@
         updateCameraVisibility();
     }
 
-    private void inflatePreviews() {
-        mPhonePreview = mPreviewInflater.inflatePreview(PHONE_INTENT);
+    private void inflateCameraPreview() {
         mCameraPreview = mPreviewInflater.inflatePreview(getCameraIntent());
-        if (mPhonePreview != null) {
-            mPreviewContainer.addView(mPhonePreview);
-            mPhonePreview.setVisibility(View.INVISIBLE);
-        }
         if (mCameraPreview != null) {
             mPreviewContainer.addView(mCameraPreview);
             mCameraPreview.setVisibility(View.INVISIBLE);
         }
     }
 
+    private void updateLeftPreview() {
+        View previewBefore = mLeftPreview;
+        if (previewBefore != null) {
+            mPreviewContainer.removeView(previewBefore);
+        }
+        if (mLeftIsVoiceAssist) {
+            mLeftPreview = mPreviewInflater.inflatePreviewFromService(
+                    mAssistManager.getVoiceInteractorComponentName());
+        } else {
+            mLeftPreview = mPreviewInflater.inflatePreview(PHONE_INTENT);
+        }
+        if (mLeftPreview != null) {
+            mPreviewContainer.addView(mLeftPreview);
+            mLeftPreview.setVisibility(View.INVISIBLE);
+        }
+    }
+
     public void startFinishDozeAnimation() {
         long delay = 0;
-        if (mPhoneImageView.getVisibility() == View.VISIBLE) {
-            startFinishDozeAnimationElement(mPhoneImageView, delay);
+        if (mLeftAffordanceView.getVisibility() == View.VISIBLE) {
+            startFinishDozeAnimationElement(mLeftAffordanceView, delay);
             delay += DOZE_ANIMATION_STAGGER_DELAY;
         }
         startFinishDozeAnimationElement(mLockIcon, delay);
@@ -543,4 +603,14 @@
             KeyguardIndicationController keyguardIndicationController) {
         mIndicationController = keyguardIndicationController;
     }
+
+    public void setAssistManager(AssistManager assistManager) {
+        mAssistManager = assistManager;
+        updateLeftAffordance();
+    }
+
+    public void updateLeftAffordance() {
+        updateLeftAffordanceIcon();
+        updateLeftPreview();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a8ecc42..c3ede75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -952,6 +952,10 @@
         } else {
             mKeyguardStatusBar.setAlpha(1f);
             mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
+            if (keyguardShowing && oldState != mStatusBarState) {
+                mKeyguardBottomArea.updateLeftAffordance();
+                mAfforanceHelper.updatePreviews();
+            }
         }
         resetVerticalPanelPosition();
         updateQsState();
@@ -1831,7 +1835,7 @@
         if (start) {
             EventLogTags.writeSysuiLockscreenGesture(
                     EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DIALER, lengthDp, velocityDp);
-            mKeyguardBottomArea.launchPhone();
+            mKeyguardBottomArea.launchLeftAffordance();
         } else {
             EventLogTags.writeSysuiLockscreenGesture(
                     EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA, lengthDp, velocityDp);
@@ -1912,15 +1916,19 @@
         if (rightIcon) {
             mStatusBar.onCameraHintStarted();
         } else {
-            mStatusBar.onPhoneHintStarted();
+            if (mKeyguardBottomArea.isLeftVoiceAssist()) {
+                mStatusBar.onVoiceAssistHintStarted();
+            } else {
+                mStatusBar.onPhoneHintStarted();
+            }
         }
     }
 
     @Override
     public KeyguardAffordanceView getLeftIcon() {
         return getLayoutDirection() == LAYOUT_DIRECTION_RTL
-                ? mKeyguardBottomArea.getCameraView()
-                : mKeyguardBottomArea.getPhoneView();
+                ? mKeyguardBottomArea.getRightView()
+                : mKeyguardBottomArea.getLeftView();
     }
 
     @Override
@@ -1931,22 +1939,22 @@
     @Override
     public KeyguardAffordanceView getRightIcon() {
         return getLayoutDirection() == LAYOUT_DIRECTION_RTL
-                ? mKeyguardBottomArea.getPhoneView()
-                : mKeyguardBottomArea.getCameraView();
+                ? mKeyguardBottomArea.getLeftView()
+                : mKeyguardBottomArea.getRightView();
     }
 
     @Override
     public View getLeftPreview() {
         return getLayoutDirection() == LAYOUT_DIRECTION_RTL
-                ? mKeyguardBottomArea.getCameraPreview()
-                : mKeyguardBottomArea.getPhonePreview();
+                ? mKeyguardBottomArea.getRightPreview()
+                : mKeyguardBottomArea.getLeftPreview();
     }
 
     @Override
     public View getRightPreview() {
         return getLayoutDirection() == LAYOUT_DIRECTION_RTL
-                ? mKeyguardBottomArea.getPhonePreview()
-                : mKeyguardBottomArea.getCameraPreview();
+                ? mKeyguardBottomArea.getLeftPreview()
+                : mKeyguardBottomArea.getRightPreview();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 1c46d42..f3335af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -101,7 +101,7 @@
 import com.android.systemui.EventLogTags;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.assist.AssistGestureManager;
+import com.android.systemui.assist.AssistManager;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -323,7 +323,7 @@
     private int mNavigationIconHints = 0;
     private HandlerThread mHandlerThread;
 
-    private AssistGestureManager mAssistGestureManager;
+    private AssistManager mAssistManager;
 
     // ensure quick settings is disabled until the current user makes it through the setup wizard
     private boolean mUserSetup = false;
@@ -645,8 +645,8 @@
                         new NavigationBarView.OnVerticalChangedListener() {
                     @Override
                     public void onVerticalChanged(boolean isVertical) {
-                        if (mAssistGestureManager != null) {
-                            mAssistGestureManager.onConfigurationChanged();
+                        if (mAssistManager != null) {
+                            mAssistManager.onConfigurationChanged();
                         }
                         mNotificationPanel.setQsScrimEnabled(!isVertical);
                     }
@@ -662,6 +662,8 @@
             // no window manager? good luck with that
         }
 
+        mAssistManager = new AssistManager(this, context);
+
         // figure out which pixel-format to use for the status bar.
         mPixelFormat = PixelFormat.OPAQUE;
 
@@ -720,6 +722,7 @@
         mKeyguardBottomArea =
                 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
         mKeyguardBottomArea.setActivityStarter(this);
+        mKeyguardBottomArea.setAssistManager(mAssistManager);
         mKeyguardIndicationController = new KeyguardIndicationController(mContext,
                 (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
                         R.id.keyguard_indication_text));
@@ -841,7 +844,6 @@
         mBroadcastReceiver.onReceive(mContext,
                 new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
 
-        mAssistGestureManager = new AssistGestureManager(this, context);
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -964,7 +966,7 @@
 
     public void invokeAssistGesture(boolean vibrate) {
         mHandler.removeCallbacks(mInvokeAssist);
-        mAssistGestureManager.onGestureInvoked(vibrate);
+        mAssistManager.onGestureInvoked(vibrate);
     }
 
     public int getStatusBarHeight() {
@@ -1044,7 +1046,7 @@
         mNavigationBarView.getBackButton().setLongClickable(true);
         mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
         mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
-        mAssistGestureManager.onConfigurationChanged();
+        mAssistManager.onConfigurationChanged();
     }
 
     // For small-screen devices (read: phones) that lack hardware navigation buttons
@@ -1805,6 +1807,11 @@
         startActivityDismissingKeyguard(intent, false, dismissShade);
     }
 
+    @Override
+    public void preventNextAnimation() {
+        overrideActivityPendingAppTransition(true /* keyguardShowing */);
+    }
+
     public void setQsExpanded(boolean expanded) {
         mStatusBarWindowManager.setQsExpanded(expanded);
     }
@@ -1924,6 +1931,10 @@
         return mHeadsUpManager.isSnoozed(sbn.getPackageName());
     }
 
+    public boolean isKeyguardCurrentlySecure() {
+        return !mUnlockMethodCache.isCurrentlyInsecure();
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -2650,6 +2661,23 @@
         final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
                 mContext, intent, mCurrentUserId);
         final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
+        Runnable runnable = new Runnable() {
+            public void run() {
+                intent.setFlags(
+                        Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                mContext.startActivityAsUser(
+                        intent, new UserHandle(UserHandle.USER_CURRENT));
+                overrideActivityPendingAppTransition(
+                        keyguardShowing && !afterKeyguardGone);
+            }
+        };
+        executeRunnableDismissingKeyguard(runnable, dismissShade, afterKeyguardGone);
+    }
+
+    public void executeRunnableDismissingKeyguard(final Runnable runnable,
+            final boolean dismissShade,
+            final boolean afterKeyguardGone) {
+        final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
         dismissKeyguardThenExecute(new OnDismissAction() {
             @Override
             public boolean onDismiss() {
@@ -2660,12 +2688,9 @@
                                 ActivityManagerNative.getDefault()
                                         .keyguardWaitingForActivityDrawn();
                             }
-                            intent.setFlags(
-                                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-                            mContext.startActivityAsUser(
-                                    intent, new UserHandle(UserHandle.USER_CURRENT));
-                            overrideActivityPendingAppTransition(
-                                    keyguardShowing && !afterKeyguardGone);
+                            if (runnable != null) {
+                                runnable.run();
+                            }
                         } catch (RemoteException e) {
                         }
                     }
@@ -3019,7 +3044,7 @@
             mHandlerThread = null;
         }
         mContext.unregisterReceiver(mBroadcastReceiver);
-        mAssistGestureManager.destroy();
+        mAssistManager.destroy();
     }
 
     private boolean mDemoModeAllowed;
@@ -3467,6 +3492,10 @@
         mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
     }
 
+    public void onVoiceAssistHintStarted() {
+        mKeyguardIndicationController.showTransientIndication(R.string.voice_hint);
+    }
+
     public void onPhoneHintStarted() {
         mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
index 5d89e2f..4269c19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.Bundle;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -50,6 +51,15 @@
 
     public View inflatePreview(Intent intent) {
         WidgetInfo info = getWidgetInfo(intent);
+        return inflatePreview(info);
+    }
+
+    public View inflatePreviewFromService(ComponentName componentName) {
+        WidgetInfo info = getWidgetInfoFromService(componentName);
+        return inflatePreview(info);
+    }
+
+    private KeyguardPreviewContainer inflatePreview(WidgetInfo info) {
         if (info == null) {
             return null;
         }
@@ -77,8 +87,36 @@
         return widgetView;
     }
 
-    private WidgetInfo getWidgetInfo(Intent intent) {
+    private WidgetInfo getWidgetInfoFromService(ComponentName componentName) {
+        PackageManager packageManager = mContext.getPackageManager();
+        // Look for the preview specified in the service meta-data
+        try {
+            Bundle metaData = packageManager.getServiceInfo(
+                    componentName, PackageManager.GET_META_DATA).metaData;
+            return getWidgetInfoFromMetaData(componentName.getPackageName(), metaData);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Failed to load preview; " + componentName.flattenToShortString()
+                    + " not found", e);
+        }
+        return null;
+    }
+
+    private WidgetInfo getWidgetInfoFromMetaData(String contextPackage,
+            Bundle metaData) {
+        if (metaData == null) {
+            return null;
+        }
+        int layoutId = metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
+        if (layoutId == 0) {
+            return null;
+        }
         WidgetInfo info = new WidgetInfo();
+        info.contextPackage = contextPackage;
+        info.layoutId = layoutId;
+        return info;
+    }
+
+    private WidgetInfo getWidgetInfo(Intent intent) {
         PackageManager packageManager = mContext.getPackageManager();
         final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
                 intent, PackageManager.MATCH_DEFAULT_ONLY, KeyguardUpdateMonitor.getCurrentUser());
@@ -94,16 +132,8 @@
         if (resolved == null || resolved.activityInfo == null) {
             return null;
         }
-        if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
-            return null;
-        }
-        int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
-        if (layoutId == 0) {
-            return null;
-        }
-        info.contextPackage = resolved.activityInfo.packageName;
-        info.layoutId = layoutId;
-        return info;
+        return getWidgetInfoFromMetaData(resolved.activityInfo.packageName,
+                resolved.activityInfo.metaData);
     }
 
     public static boolean wouldLaunchResolverActivity(Context ctx, Intent intent,