AssistManager.java revision 4c249ff7a07e6de0cf26b7416a5b0c321ad5ad9a
1package com.android.systemui.assist;
2
3import android.annotation.NonNull;
4import android.annotation.Nullable;
5import android.app.ActivityManager;
6import android.app.ActivityOptions;
7import android.app.SearchManager;
8import android.content.ActivityNotFoundException;
9import android.content.ComponentName;
10import android.content.Context;
11import android.content.Intent;
12import android.content.pm.PackageManager;
13import android.content.res.Resources;
14import android.graphics.PixelFormat;
15import android.os.AsyncTask;
16import android.os.Bundle;
17import android.os.Handler;
18import android.os.IBinder;
19import android.os.RemoteException;
20import android.os.UserHandle;
21import android.provider.Settings;
22import android.service.voice.VoiceInteractionSession;
23import android.util.Log;
24import android.view.Gravity;
25import android.view.LayoutInflater;
26import android.view.View;
27import android.view.ViewGroup;
28import android.view.WindowManager;
29import android.widget.ImageView;
30
31import com.android.internal.app.AssistUtils;
32import com.android.internal.app.IVoiceInteractionSessionListener;
33import com.android.internal.app.IVoiceInteractionSessionShowCallback;
34import com.android.systemui.R;
35import com.android.systemui.statusbar.BaseStatusBar;
36import com.android.systemui.statusbar.CommandQueue;
37
38/**
39 * Class to manage everything related to assist in SystemUI.
40 */
41public class AssistManager {
42
43    private static final String TAG = "AssistManager";
44    private static final String ASSIST_ICON_METADATA_NAME =
45            "com.android.systemui.action_assist_icon";
46
47    private static final long TIMEOUT_SERVICE = 2500;
48    private static final long TIMEOUT_ACTIVITY = 1000;
49
50    protected final Context mContext;
51    private final WindowManager mWindowManager;
52    private final AssistDisclosure mAssistDisclosure;
53
54    private AssistOrbContainer mView;
55    private final BaseStatusBar mBar;
56    protected final AssistUtils mAssistUtils;
57
58    private IVoiceInteractionSessionShowCallback mShowCallback =
59            new IVoiceInteractionSessionShowCallback.Stub() {
60
61        @Override
62        public void onFailed() throws RemoteException {
63            mView.post(mHideRunnable);
64        }
65
66        @Override
67        public void onShown() throws RemoteException {
68            mView.post(mHideRunnable);
69        }
70    };
71
72    private Runnable mHideRunnable = new Runnable() {
73        @Override
74        public void run() {
75            mView.removeCallbacks(this);
76            mView.show(false /* show */, true /* animate */);
77        }
78    };
79
80    public AssistManager(BaseStatusBar bar, Context context) {
81        mContext = context;
82        mBar = bar;
83        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
84        mAssistUtils = new AssistUtils(context);
85        mAssistDisclosure = new AssistDisclosure(context, new Handler());
86
87        registerVoiceInteractionSessionListener();
88    }
89
90    protected void registerVoiceInteractionSessionListener() {
91        mAssistUtils.registerVoiceInteractionSessionListener(
92                new IVoiceInteractionSessionListener.Stub() {
93            @Override
94            public void onVoiceSessionShown() throws RemoteException {
95                Log.v(TAG, "Voice open");
96            }
97
98            @Override
99            public void onVoiceSessionHidden() throws RemoteException {
100                Log.v(TAG, "Voice closed");
101            }
102        });
103    }
104
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
125    protected boolean shouldShowOrb() {
126        return true;
127    }
128
129    public void startAssist(Bundle args) {
130        final ComponentName assistComponent = getAssistInfo();
131        if (assistComponent == null) {
132            return;
133        }
134
135        final boolean isService = assistComponent.equals(getVoiceInteractorComponentName());
136        if (!isService || (!isVoiceSessionRunning() && shouldShowOrb())) {
137            showOrb(assistComponent, isService);
138            mView.postDelayed(mHideRunnable, isService
139                    ? TIMEOUT_SERVICE
140                    : TIMEOUT_ACTIVITY);
141        }
142        startAssistInternal(args, assistComponent, isService);
143    }
144
145    public void hideAssist() {
146        mAssistUtils.hideCurrentSession();
147    }
148
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;
160        }
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;
165        return lp;
166    }
167
168    private void showOrb(@NonNull ComponentName assistComponent, boolean isService) {
169        maybeSwapSearchIcon(assistComponent, isService);
170        mView.show(true /* show */, true /* animate */);
171    }
172
173    private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent,
174            boolean isService) {
175        if (isService) {
176            startVoiceInteractor(args);
177        } else {
178            startAssistActivity(args, assistComponent);
179        }
180    }
181
182    private void startAssistActivity(Bundle args, @NonNull ComponentName assistComponent) {
183        if (!mBar.isDeviceProvisioned()) {
184            return;
185        }
186
187        // Close Recent Apps if needed
188        mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL |
189                CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL);
190
191        boolean structureEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
192                Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
193
194        final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
195                .getAssistIntent(structureEnabled);
196        if (intent == null) {
197            return;
198        }
199        intent.setComponent(assistComponent);
200        intent.putExtras(args);
201
202        if (structureEnabled) {
203            showDisclosure();
204        }
205
206        try {
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() {
211                @Override
212                public void run() {
213                    mContext.startActivityAsUser(intent, opts.toBundle(),
214                            new UserHandle(UserHandle.USER_CURRENT));
215                }
216            });
217        } catch (ActivityNotFoundException e) {
218            Log.w(TAG, "Activity not found for " + intent.getAction());
219        }
220    }
221
222    private void startVoiceInteractor(Bundle args) {
223        mAssistUtils.showSessionForActiveService(args,
224                VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, null);
225    }
226
227    public void launchVoiceAssistFromKeyguard() {
228        mAssistUtils.launchVoiceAssistFromKeyguard();
229    }
230
231    public boolean canVoiceAssistBeLaunchedFromKeyguard() {
232        return mAssistUtils.activeServiceSupportsLaunchFromKeyguard();
233    }
234
235    public ComponentName getVoiceInteractorComponentName() {
236        return mAssistUtils.getActiveServiceComponentName();
237    }
238
239    private boolean isVoiceSessionRunning() {
240        return mAssistUtils.isSessionRunning();
241    }
242
243    public void destroy() {
244        mWindowManager.removeViewImmediate(mView);
245    }
246
247    private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) {
248        replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME,
249                isService);
250    }
251
252    public void replaceDrawable(ImageView v, ComponentName component, String name,
253            boolean isService) {
254        if (component != null) {
255            try {
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));
269                        return;
270                    }
271                }
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);
278            }
279        }
280        v.setImageDrawable(null);
281    }
282
283    @Nullable
284    private ComponentName getAssistInfo() {
285        return mAssistUtils.getAssistComponentForUser(UserHandle.USER_CURRENT);
286    }
287
288    public void showDisclosure() {
289        mAssistDisclosure.postShow();
290    }
291
292    public void onLockscreenShown() {
293        mAssistUtils.onLockscreenShown();
294    }
295}
296