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