Home | History | Annotate | Download | only in assist
      1 package com.android.systemui.assist;
      2 
      3 import android.annotation.NonNull;
      4 import android.annotation.Nullable;
      5 import android.app.ActivityManager;
      6 import android.app.ActivityOptions;
      7 import android.app.SearchManager;
      8 import android.content.ActivityNotFoundException;
      9 import android.content.ComponentName;
     10 import android.content.Context;
     11 import android.content.Intent;
     12 import android.content.pm.ActivityInfo;
     13 import android.content.pm.PackageManager;
     14 import android.content.res.Configuration;
     15 import android.content.res.Resources;
     16 import android.graphics.PixelFormat;
     17 import android.os.AsyncTask;
     18 import android.os.Binder;
     19 import android.os.Bundle;
     20 import android.os.Handler;
     21 import android.os.RemoteException;
     22 import android.os.UserHandle;
     23 import android.provider.Settings;
     24 import android.service.voice.VoiceInteractionSession;
     25 import android.util.Log;
     26 import android.view.Gravity;
     27 import android.view.LayoutInflater;
     28 import android.view.View;
     29 import android.view.ViewGroup;
     30 import android.view.WindowManager;
     31 import android.widget.ImageView;
     32 
     33 import com.android.internal.app.AssistUtils;
     34 import com.android.internal.app.IVoiceInteractionSessionListener;
     35 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
     36 import com.android.keyguard.KeyguardUpdateMonitor;
     37 import com.android.settingslib.applications.InterestingConfigChanges;
     38 import com.android.systemui.ConfigurationChangedReceiver;
     39 import com.android.systemui.R;
     40 import com.android.systemui.SysUiServiceProvider;
     41 import com.android.systemui.statusbar.CommandQueue;
     42 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
     43 
     44 /**
     45  * Class to manage everything related to assist in SystemUI.
     46  */
     47 public class AssistManager implements ConfigurationChangedReceiver {
     48 
     49     private static final String TAG = "AssistManager";
     50     private static final String ASSIST_ICON_METADATA_NAME =
     51             "com.android.systemui.action_assist_icon";
     52 
     53     private static final long TIMEOUT_SERVICE = 2500;
     54     private static final long TIMEOUT_ACTIVITY = 1000;
     55 
     56     protected final Context mContext;
     57     private final WindowManager mWindowManager;
     58     private final AssistDisclosure mAssistDisclosure;
     59     private final InterestingConfigChanges mInterestingConfigChanges;
     60 
     61     private AssistOrbContainer mView;
     62     private final DeviceProvisionedController mDeviceProvisionedController;
     63     protected final AssistUtils mAssistUtils;
     64 
     65     private IVoiceInteractionSessionShowCallback mShowCallback =
     66             new IVoiceInteractionSessionShowCallback.Stub() {
     67 
     68         @Override
     69         public void onFailed() throws RemoteException {
     70             mView.post(mHideRunnable);
     71         }
     72 
     73         @Override
     74         public void onShown() throws RemoteException {
     75             mView.post(mHideRunnable);
     76         }
     77     };
     78 
     79     private Runnable mHideRunnable = new Runnable() {
     80         @Override
     81         public void run() {
     82             mView.removeCallbacks(this);
     83             mView.show(false /* show */, true /* animate */);
     84         }
     85     };
     86 
     87     public AssistManager(DeviceProvisionedController controller, Context context) {
     88         mContext = context;
     89         mDeviceProvisionedController = controller;
     90         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
     91         mAssistUtils = new AssistUtils(context);
     92         mAssistDisclosure = new AssistDisclosure(context, new Handler());
     93 
     94         registerVoiceInteractionSessionListener();
     95         mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION
     96                 | ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_UI_MODE
     97                 | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
     98         onConfigurationChanged(context.getResources().getConfiguration());
     99     }
    100 
    101     protected void registerVoiceInteractionSessionListener() {
    102         mAssistUtils.registerVoiceInteractionSessionListener(
    103                 new IVoiceInteractionSessionListener.Stub() {
    104             @Override
    105             public void onVoiceSessionShown() throws RemoteException {
    106                 Log.v(TAG, "Voice open");
    107             }
    108 
    109             @Override
    110             public void onVoiceSessionHidden() throws RemoteException {
    111                 Log.v(TAG, "Voice closed");
    112             }
    113         });
    114     }
    115 
    116     public void onConfigurationChanged(Configuration newConfiguration) {
    117         if (!mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
    118             return;
    119         }
    120         boolean visible = false;
    121         if (mView != null) {
    122             visible = mView.isShowing();
    123             mWindowManager.removeView(mView);
    124         }
    125 
    126         mView = (AssistOrbContainer) LayoutInflater.from(mContext).inflate(
    127                 R.layout.assist_orb, null);
    128         mView.setVisibility(View.GONE);
    129         mView.setSystemUiVisibility(
    130                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    131                         | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    132         WindowManager.LayoutParams lp = getLayoutParams();
    133         mWindowManager.addView(mView, lp);
    134         if (visible) {
    135             mView.show(true /* show */, false /* animate */);
    136         }
    137     }
    138 
    139     protected boolean shouldShowOrb() {
    140         return true;
    141     }
    142 
    143     public void startAssist(Bundle args) {
    144         final ComponentName assistComponent = getAssistInfo();
    145         if (assistComponent == null) {
    146             return;
    147         }
    148 
    149         final boolean isService = assistComponent.equals(getVoiceInteractorComponentName());
    150         if (!isService || (!isVoiceSessionRunning() && shouldShowOrb())) {
    151             showOrb(assistComponent, isService);
    152             mView.postDelayed(mHideRunnable, isService
    153                     ? TIMEOUT_SERVICE
    154                     : TIMEOUT_ACTIVITY);
    155         }
    156         startAssistInternal(args, assistComponent, isService);
    157     }
    158 
    159     public void hideAssist() {
    160         mAssistUtils.hideCurrentSession();
    161     }
    162 
    163     private WindowManager.LayoutParams getLayoutParams() {
    164         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
    165                 ViewGroup.LayoutParams.MATCH_PARENT,
    166                 mContext.getResources().getDimensionPixelSize(R.dimen.assist_orb_scrim_height),
    167                 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING,
    168                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
    169                         | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
    170                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
    171                 PixelFormat.TRANSLUCENT);
    172         lp.token = new Binder();
    173         if (ActivityManager.isHighEndGfx()) {
    174             lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
    175         }
    176         lp.gravity = Gravity.BOTTOM | Gravity.START;
    177         lp.setTitle("AssistPreviewPanel");
    178         lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
    179                 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
    180         return lp;
    181     }
    182 
    183     private void showOrb(@NonNull ComponentName assistComponent, boolean isService) {
    184         maybeSwapSearchIcon(assistComponent, isService);
    185         mView.show(true /* show */, true /* animate */);
    186     }
    187 
    188     private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent,
    189             boolean isService) {
    190         if (isService) {
    191             startVoiceInteractor(args);
    192         } else {
    193             startAssistActivity(args, assistComponent);
    194         }
    195     }
    196 
    197     private void startAssistActivity(Bundle args, @NonNull ComponentName assistComponent) {
    198         if (!mDeviceProvisionedController.isDeviceProvisioned()) {
    199             return;
    200         }
    201 
    202         // Close Recent Apps if needed
    203         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).animateCollapsePanels(
    204                 CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL | CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL);
    205 
    206         boolean structureEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
    207                 Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
    208 
    209         final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
    210                 .getAssistIntent(structureEnabled);
    211         if (intent == null) {
    212             return;
    213         }
    214         intent.setComponent(assistComponent);
    215         intent.putExtras(args);
    216 
    217         if (structureEnabled) {
    218             showDisclosure();
    219         }
    220 
    221         try {
    222             final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
    223                     R.anim.search_launch_enter, R.anim.search_launch_exit);
    224             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    225             AsyncTask.execute(new Runnable() {
    226                 @Override
    227                 public void run() {
    228                     mContext.startActivityAsUser(intent, opts.toBundle(),
    229                             new UserHandle(UserHandle.USER_CURRENT));
    230                 }
    231             });
    232         } catch (ActivityNotFoundException e) {
    233             Log.w(TAG, "Activity not found for " + intent.getAction());
    234         }
    235     }
    236 
    237     private void startVoiceInteractor(Bundle args) {
    238         mAssistUtils.showSessionForActiveService(args,
    239                 VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, null);
    240     }
    241 
    242     public void launchVoiceAssistFromKeyguard() {
    243         mAssistUtils.launchVoiceAssistFromKeyguard();
    244     }
    245 
    246     public boolean canVoiceAssistBeLaunchedFromKeyguard() {
    247         return mAssistUtils.activeServiceSupportsLaunchFromKeyguard();
    248     }
    249 
    250     public ComponentName getVoiceInteractorComponentName() {
    251         return mAssistUtils.getActiveServiceComponentName();
    252     }
    253 
    254     private boolean isVoiceSessionRunning() {
    255         return mAssistUtils.isSessionRunning();
    256     }
    257 
    258     public void destroy() {
    259         mWindowManager.removeViewImmediate(mView);
    260     }
    261 
    262     private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) {
    263         replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME,
    264                 isService);
    265     }
    266 
    267     public void replaceDrawable(ImageView v, ComponentName component, String name,
    268             boolean isService) {
    269         if (component != null) {
    270             try {
    271                 PackageManager packageManager = mContext.getPackageManager();
    272                 // Look for the search icon specified in the activity meta-data
    273                 Bundle metaData = isService
    274                         ? packageManager.getServiceInfo(
    275                                 component, PackageManager.GET_META_DATA).metaData
    276                         : packageManager.getActivityInfo(
    277                                 component, PackageManager.GET_META_DATA).metaData;
    278                 if (metaData != null) {
    279                     int iconResId = metaData.getInt(name);
    280                     if (iconResId != 0) {
    281                         Resources res = packageManager.getResourcesForApplication(
    282                                 component.getPackageName());
    283                         v.setImageDrawable(res.getDrawable(iconResId));
    284                         return;
    285                     }
    286                 }
    287             } catch (PackageManager.NameNotFoundException e) {
    288                 Log.v(TAG, "Assistant component "
    289                         + component.flattenToShortString() + " not found");
    290             } catch (Resources.NotFoundException nfe) {
    291                 Log.w(TAG, "Failed to swap drawable from "
    292                         + component.flattenToShortString(), nfe);
    293             }
    294         }
    295         v.setImageDrawable(null);
    296     }
    297 
    298     @Nullable
    299     private ComponentName getAssistInfo() {
    300         return mAssistUtils.getAssistComponentForUser(KeyguardUpdateMonitor.getCurrentUser());
    301     }
    302 
    303     public void showDisclosure() {
    304         mAssistDisclosure.postShow();
    305     }
    306 
    307     public void onLockscreenShown() {
    308         mAssistUtils.onLockscreenShown();
    309     }
    310 }
    311