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