AssistManager.java revision 5ccae1fb7591178ee144836ceec5025c35dd0b04
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.RemoteException;
19import android.os.UserHandle;
20import android.provider.Settings;
21import android.service.voice.VoiceInteractionSession;
22import android.util.Log;
23import android.view.Gravity;
24import android.view.LayoutInflater;
25import android.view.View;
26import android.view.ViewGroup;
27import android.view.WindowManager;
28import android.widget.ImageView;
29
30import com.android.internal.app.AssistUtils;
31import com.android.internal.app.IVoiceInteractionSessionShowCallback;
32import com.android.systemui.R;
33import com.android.systemui.statusbar.BaseStatusBar;
34import com.android.systemui.statusbar.CommandQueue;
35
36/**
37 * Class to manage everything related to assist in SystemUI.
38 */
39public 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
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
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
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
122    public void hideAssist() {
123        mAssistUtils.hideCurrentSession();
124    }
125
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
145    private void showOrb(@NonNull ComponentName assistComponent, boolean isService) {
146        maybeSwapSearchIcon(assistComponent, isService);
147        mView.show(true /* show */, true /* animate */);
148    }
149
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
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
199    private void startVoiceInteractor(Bundle args) {
200        mAssistUtils.showSessionForActiveService(args,
201                VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, null);
202    }
203
204    public void launchVoiceAssistFromKeyguard() {
205        mAssistUtils.launchVoiceAssistFromKeyguard();
206    }
207
208    public boolean canVoiceAssistBeLaunchedFromKeyguard() {
209        return mAssistUtils.activeServiceSupportsLaunchFromKeyguard();
210    }
211
212    public ComponentName getVoiceInteractorComponentName() {
213        return mAssistUtils.getActiveServiceComponentName();
214    }
215
216    private boolean isVoiceSessionRunning() {
217        return mAssistUtils.isSessionRunning();
218    }
219
220    public void destroy() {
221        mWindowManager.removeViewImmediate(mView);
222    }
223
224    private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) {
225        replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME,
226                isService);
227    }
228
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
261    private ComponentName getAssistInfo() {
262        return mAssistUtils.getAssistComponentForUser(UserHandle.USER_CURRENT);
263    }
264
265    public void showDisclosure() {
266        mAssistDisclosure.postShow();
267    }
268
269    public void onLockscreenShown() {
270        mAssistUtils.onLockscreenShown();
271    }
272}
273