• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.assist;
18 
19 import android.annotation.NonNull;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.pm.ActivityInfo;
23 import android.content.pm.PackageManager;
24 import android.content.res.Configuration;
25 import android.content.res.Resources;
26 import android.graphics.PixelFormat;
27 import android.os.Binder;
28 import android.os.Bundle;
29 import android.util.Log;
30 import android.view.Gravity;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.view.WindowManager;
35 import android.widget.ImageView;
36 
37 import com.android.settingslib.applications.InterestingConfigChanges;
38 import com.android.systemui.R;
39 import com.android.systemui.statusbar.policy.ConfigurationController;
40 
41 /**
42  * AssistOrbController controls the showing and hiding of the assistant orb.
43  */
44 public class AssistOrbController {
45     private static final String ASSIST_ICON_METADATA_NAME =
46             "com.android.systemui.action_assist_icon";
47     private static final String TAG = "AssistOrbController";
48     private static final boolean VERBOSE = false;
49 
50     private final InterestingConfigChanges mInterestingConfigChanges;
51     private AssistOrbContainer mView;
52     private final Context mContext;
53     private final WindowManager mWindowManager;
54 
55     private Runnable mHideRunnable = new Runnable() {
56         @Override
57         public void run() {
58             mView.removeCallbacks(this);
59             mView.show(false /* show */, true /* animate */, () -> {
60                 mWindowManager.removeView(mView);
61             });
62         }
63     };
64 
65     private ConfigurationController.ConfigurationListener mConfigurationListener =
66             new ConfigurationController.ConfigurationListener() {
67                 @Override
68                 public void onConfigChanged(Configuration newConfig) {
69                     if (!mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
70                         return;
71                     }
72                     boolean visible = false;
73                     if (mView != null) {
74                         visible = mView.isShowing();
75                         if (mView.isAttachedToWindow()) {
76                             mWindowManager.removeView(mView);
77                         }
78                     }
79 
80                     if (visible) {
81                         showOrb(false);
82                     }
83                 }
84             };
85 
AssistOrbController(ConfigurationController configurationController, Context context)86     AssistOrbController(ConfigurationController configurationController, Context context) {
87         mContext = context;
88         mWindowManager =  mContext.getSystemService(WindowManager.class);
89         mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION
90                 | ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_UI_MODE
91                 | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
92 
93         configurationController.addCallback(mConfigurationListener);
94         mConfigurationListener.onConfigChanged(context.getResources().getConfiguration());
95     }
96 
postHide()97     public void postHide() {
98         mView.post(mHideRunnable);
99     }
100 
postHideDelayed(long delayMs)101     public void postHideDelayed(long delayMs) {
102         mView.postDelayed(mHideRunnable, delayMs);
103     }
104 
showOrb(boolean animated)105     private void showOrb(boolean animated) {
106         if (mView == null) {
107             mView = (AssistOrbContainer) LayoutInflater.from(mContext).inflate(
108                     R.layout.assist_orb, null);
109             mView.setVisibility(View.GONE);
110             mView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
111                     | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
112                     | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
113         }
114         if (!mView.isAttachedToWindow()) {
115             WindowManager.LayoutParams params = getLayoutParams();
116             mWindowManager.addView(mView, params);
117         }
118         mView.show(true, animated, null);
119     }
120 
getLayoutParams()121     private WindowManager.LayoutParams getLayoutParams() {
122         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
123                 ViewGroup.LayoutParams.MATCH_PARENT,
124                 mContext.getResources().getDimensionPixelSize(R.dimen.assist_orb_scrim_height),
125                 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING,
126                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
127                         | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
128                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
129                 PixelFormat.TRANSLUCENT);
130         lp.token = new Binder();
131         lp.gravity = Gravity.BOTTOM | Gravity.START;
132         lp.setTitle("AssistPreviewPanel");
133         lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
134                 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
135         return lp;
136     }
137 
showOrb(@onNull ComponentName assistComponent, boolean isService)138     public void showOrb(@NonNull ComponentName assistComponent, boolean isService) {
139         showOrb(true);
140         maybeSwapSearchIcon(assistComponent, isService);
141     }
142 
maybeSwapSearchIcon(@onNull ComponentName assistComponent, boolean isService)143     private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) {
144         replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME,
145                 isService);
146     }
147 
replaceDrawable(ImageView v, ComponentName component, String name, boolean isService)148     public void replaceDrawable(ImageView v, ComponentName component, String name,
149             boolean isService) {
150         if (component != null) {
151             try {
152                 PackageManager packageManager = mContext.getPackageManager();
153                 // Look for the search icon specified in the activity meta-data
154                 Bundle metaData = isService
155                         ? packageManager.getServiceInfo(
156                         component, PackageManager.GET_META_DATA).metaData
157                         : packageManager.getActivityInfo(
158                                 component, PackageManager.GET_META_DATA).metaData;
159                 if (metaData != null) {
160                     int iconResId = metaData.getInt(name);
161                     if (iconResId != 0) {
162                         Resources res = packageManager.getResourcesForApplication(
163                                 component.getPackageName());
164                         v.setImageDrawable(res.getDrawable(iconResId));
165                         return;
166                     }
167                 }
168             } catch (PackageManager.NameNotFoundException e) {
169                 if (VERBOSE) {
170                     Log.v(TAG, "Assistant component "
171                             + component.flattenToShortString() + " not found");
172                 }
173             } catch (Resources.NotFoundException nfe) {
174                 Log.w(TAG, "Failed to swap drawable from "
175                         + component.flattenToShortString(), nfe);
176             }
177         }
178         v.setImageDrawable(null);
179     }
180 }
181