1 package com.android.systemui.assist;
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.IBinder;
19 import android.os.RemoteException;
20 import android.os.UserHandle;
21 import android.provider.Settings;
22 import android.service.voice.VoiceInteractionSession;
23 import android.util.Log;
24 import android.view.Gravity;
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;
31 import com.android.internal.app.AssistUtils;
32 import com.android.internal.app.IVoiceInteractionSessionListener;
33 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
34 import com.android.systemui.R;
35 import com.android.systemui.statusbar.BaseStatusBar;
36 import com.android.systemui.statusbar.CommandQueue;
39 * Class to manage everything related to assist in SystemUI.
41 public class AssistManager {
43 private static final String TAG = "AssistManager";
44 private static final String ASSIST_ICON_METADATA_NAME =
45 "com.android.systemui.action_assist_icon";
47 private static final long TIMEOUT_SERVICE = 2500;
48 private static final long TIMEOUT_ACTIVITY = 1000;
50 protected final Context mContext;
51 private final WindowManager mWindowManager;
52 private final AssistDisclosure mAssistDisclosure;
54 private AssistOrbContainer mView;
55 private final BaseStatusBar mBar;
56 protected final AssistUtils mAssistUtils;
58 private IVoiceInteractionSessionShowCallback mShowCallback =
59 new IVoiceInteractionSessionShowCallback.Stub() {
62 public void onFailed() throws RemoteException {
63 mView.post(mHideRunnable);
67 public void onShown() throws RemoteException {
68 mView.post(mHideRunnable);
72 private Runnable mHideRunnable = new Runnable() {
75 mView.removeCallbacks(this);
76 mView.show(false /* show */, true /* animate */);
80 public AssistManager(BaseStatusBar bar, Context context) {
83 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
84 mAssistUtils = new AssistUtils(context);
85 mAssistDisclosure = new AssistDisclosure(context, new Handler());
87 registerVoiceInteractionSessionListener();
90 protected void registerVoiceInteractionSessionListener() {
91 mAssistUtils.registerVoiceInteractionSessionListener(
92 new IVoiceInteractionSessionListener.Stub() {
94 public void onVoiceSessionShown() throws RemoteException {
95 Log.v(TAG, "Voice open");
99 public void onVoiceSessionHidden() throws RemoteException {
100 Log.v(TAG, "Voice closed");
105 public void onConfigurationChanged() {
106 boolean visible = false;
108 visible = mView.isShowing();
109 mWindowManager.removeView(mView);
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);
121 mView.show(true /* show */, false /* animate */);
125 protected boolean shouldShowOrb() {
129 public void startAssist(Bundle args) {
130 final ComponentName assistComponent = getAssistInfo();
131 if (assistComponent == null) {
135 final boolean isService = assistComponent.equals(getVoiceInteractorComponentName());
136 if (!isService || (!isVoiceSessionRunning() && shouldShowOrb())) {
137 showOrb(assistComponent, isService);
138 mView.postDelayed(mHideRunnable, isService
142 startAssistInternal(args, assistComponent, isService);
145 public void hideAssist() {
146 mAssistUtils.hideCurrentSession();
149 private WindowManager.LayoutParams getLayoutParams() {
150 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
151 ViewGroup.LayoutParams.MATCH_PARENT,
152 mContext.getResources().getDimensionPixelSize(R.dimen.assist_orb_scrim_height),
153 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING,
154 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
155 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
156 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
157 PixelFormat.TRANSLUCENT);
158 if (ActivityManager.isHighEndGfx()) {
159 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
161 lp.gravity = Gravity.BOTTOM | Gravity.START;
162 lp.setTitle("AssistPreviewPanel");
163 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
164 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
168 private void showOrb(@NonNull ComponentName assistComponent, boolean isService) {
169 maybeSwapSearchIcon(assistComponent, isService);
170 mView.show(true /* show */, true /* animate */);
173 private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent,
176 startVoiceInteractor(args);
178 startAssistActivity(args, assistComponent);
182 private void startAssistActivity(Bundle args, @NonNull ComponentName assistComponent) {
183 if (!mBar.isDeviceProvisioned()) {
187 // Close Recent Apps if needed
188 mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL |
189 CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL);
191 boolean structureEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
192 Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
194 final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
195 .getAssistIntent(structureEnabled);
196 if (intent == null) {
199 intent.setComponent(assistComponent);
200 intent.putExtras(args);
202 if (structureEnabled) {
207 final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
208 R.anim.search_launch_enter, R.anim.search_launch_exit);
209 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
210 AsyncTask.execute(new Runnable() {
213 mContext.startActivityAsUser(intent, opts.toBundle(),
214 new UserHandle(UserHandle.USER_CURRENT));
217 } catch (ActivityNotFoundException e) {
218 Log.w(TAG, "Activity not found for " + intent.getAction());
222 private void startVoiceInteractor(Bundle args) {
223 mAssistUtils.showSessionForActiveService(args,
224 VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, null);
227 public void launchVoiceAssistFromKeyguard() {
228 mAssistUtils.launchVoiceAssistFromKeyguard();
231 public boolean canVoiceAssistBeLaunchedFromKeyguard() {
232 return mAssistUtils.activeServiceSupportsLaunchFromKeyguard();
235 public ComponentName getVoiceInteractorComponentName() {
236 return mAssistUtils.getActiveServiceComponentName();
239 private boolean isVoiceSessionRunning() {
240 return mAssistUtils.isSessionRunning();
243 public void destroy() {
244 mWindowManager.removeViewImmediate(mView);
247 private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) {
248 replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME,
252 public void replaceDrawable(ImageView v, ComponentName component, String name,
254 if (component != null) {
256 PackageManager packageManager = mContext.getPackageManager();
257 // Look for the search icon specified in the activity meta-data
258 Bundle metaData = isService
259 ? packageManager.getServiceInfo(
260 component, PackageManager.GET_META_DATA).metaData
261 : packageManager.getActivityInfo(
262 component, PackageManager.GET_META_DATA).metaData;
263 if (metaData != null) {
264 int iconResId = metaData.getInt(name);
265 if (iconResId != 0) {
266 Resources res = packageManager.getResourcesForApplication(
267 component.getPackageName());
268 v.setImageDrawable(res.getDrawable(iconResId));
272 } catch (PackageManager.NameNotFoundException e) {
273 Log.v(TAG, "Assistant component "
274 + component.flattenToShortString() + " not found");
275 } catch (Resources.NotFoundException nfe) {
276 Log.w(TAG, "Failed to swap drawable from "
277 + component.flattenToShortString(), nfe);
280 v.setImageDrawable(null);
284 private ComponentName getAssistInfo() {
285 return mAssistUtils.getAssistComponentForUser(UserHandle.USER_CURRENT);
288 public void showDisclosure() {
289 mAssistDisclosure.postShow();
292 public void onLockscreenShown() {
293 mAssistUtils.onLockscreenShown();