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