• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.toast;
18 
19 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
20 import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
21 
22 import android.animation.Animator;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.UserIdInt;
26 import android.app.Application;
27 import android.content.Context;
28 import android.content.pm.ApplicationInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.PackageManager.NameNotFoundException;
31 import android.content.res.Configuration;
32 import android.content.res.Resources;
33 import android.graphics.Bitmap;
34 import android.graphics.drawable.BitmapDrawable;
35 import android.graphics.drawable.Drawable;
36 import android.os.Build;
37 import android.os.UserHandle;
38 import android.util.Log;
39 import android.view.LayoutInflater;
40 import android.view.View;
41 import android.widget.ImageView;
42 import android.widget.TextView;
43 
44 import com.android.internal.R;
45 import com.android.launcher3.icons.IconFactory;
46 import com.android.settingslib.applications.ApplicationsState;
47 import com.android.settingslib.applications.ApplicationsState.AppEntry;
48 import com.android.systemui.plugins.ToastPlugin;
49 
50 /**
51  * SystemUI TextToast that can be customized by ToastPlugins. Should never instantiate this class
52  * directly. Instead, use {@link ToastFactory#createToast}.
53  */
54 public class SystemUIToast implements ToastPlugin.Toast {
55     static final String TAG = "SystemUIToast";
56     final Context mContext;
57     final CharSequence mText;
58     final ToastPlugin.Toast mPluginToast;
59 
60     private final String mPackageName;
61     @UserIdInt private final int mUserId;
62     private final LayoutInflater mLayoutInflater;
63 
64     final int mDefaultX = 0;
65     final int mDefaultHorizontalMargin = 0;
66     final int mDefaultVerticalMargin = 0;
67 
68     private int mDefaultY;
69     private int mDefaultGravity;
70 
71     @NonNull private final View mToastView;
72     @Nullable private final Animator mInAnimator;
73     @Nullable private final Animator mOutAnimator;
74 
SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text, String packageName, int userId, int orientation)75     SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
76             String packageName, int userId, int orientation) {
77         this(layoutInflater, context, text, null, packageName, userId,
78                 orientation);
79     }
80 
SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text, ToastPlugin.Toast pluginToast, String packageName, @UserIdInt int userId, int orientation)81     SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
82             ToastPlugin.Toast pluginToast, String packageName, @UserIdInt int userId,
83             int orientation) {
84         mLayoutInflater = layoutInflater;
85         mContext = context;
86         mText = text;
87         mPluginToast = pluginToast;
88         mPackageName = packageName;
89         mUserId = userId;
90         mToastView = inflateToastView();
91         mInAnimator = createInAnimator();
92         mOutAnimator = createOutAnimator();
93 
94         onOrientationChange(orientation);
95     }
96 
97     @Override
98     @NonNull
getGravity()99     public Integer getGravity() {
100         if (isPluginToast() && mPluginToast.getGravity() != null) {
101             return mPluginToast.getGravity();
102         }
103         return mDefaultGravity;
104     }
105 
106     @Override
107     @NonNull
getXOffset()108     public Integer getXOffset() {
109         if (isPluginToast() && mPluginToast.getXOffset() != null) {
110             return mPluginToast.getXOffset();
111         }
112         return mDefaultX;
113     }
114 
115     @Override
116     @NonNull
getYOffset()117     public Integer getYOffset() {
118         if (isPluginToast() && mPluginToast.getYOffset() != null) {
119             return mPluginToast.getYOffset();
120         }
121         return mDefaultY;
122     }
123 
124     @Override
125     @NonNull
getHorizontalMargin()126     public Integer getHorizontalMargin() {
127         if (isPluginToast() && mPluginToast.getHorizontalMargin() != null) {
128             return mPluginToast.getHorizontalMargin();
129         }
130         return mDefaultHorizontalMargin;
131     }
132 
133     @Override
134     @NonNull
getVerticalMargin()135     public Integer getVerticalMargin() {
136         if (isPluginToast() && mPluginToast.getVerticalMargin() != null) {
137             return mPluginToast.getVerticalMargin();
138         }
139         return mDefaultVerticalMargin;
140     }
141 
142     @Override
143     @NonNull
getView()144     public View getView() {
145         return mToastView;
146     }
147 
148     @Override
149     @Nullable
getInAnimation()150     public Animator getInAnimation() {
151         return mInAnimator;
152     }
153 
154     @Override
155     @Nullable
getOutAnimation()156     public Animator getOutAnimation() {
157         return mOutAnimator;
158     }
159 
160     /**
161      * Whether this toast has a custom animation.
162      */
hasCustomAnimation()163     public boolean hasCustomAnimation() {
164         return getInAnimation() != null || getOutAnimation() != null;
165     }
166 
isPluginToast()167     private boolean isPluginToast() {
168         return mPluginToast != null;
169     }
170 
inflateToastView()171     private View inflateToastView() {
172         if (isPluginToast() && mPluginToast.getView() != null) {
173             return mPluginToast.getView();
174         }
175 
176         final View toastView = mLayoutInflater.inflate(
177                     com.android.systemui.R.layout.text_toast, null);
178         final TextView textView = toastView.findViewById(com.android.systemui.R.id.text);
179         final ImageView iconView = toastView.findViewById(com.android.systemui.R.id.icon);
180         textView.setText(mText);
181 
182         ApplicationInfo appInfo = null;
183         try {
184             appInfo = mContext.getPackageManager()
185                     .getApplicationInfoAsUser(mPackageName, 0, mUserId);
186         } catch (PackageManager.NameNotFoundException e) {
187             Log.e(TAG, "Package name not found package=" + mPackageName
188                     + " user=" + mUserId);
189         }
190 
191         if (appInfo != null && appInfo.targetSdkVersion < Build.VERSION_CODES.S) {
192             // no two-line limit
193             textView.setMaxLines(Integer.MAX_VALUE);
194 
195             // no app icon
196             toastView.findViewById(com.android.systemui.R.id.icon).setVisibility(View.GONE);
197         } else {
198             Drawable icon = getBadgedIcon(mContext, mPackageName, mUserId);
199             if (icon == null) {
200                 iconView.setVisibility(View.GONE);
201             } else {
202                 iconView.setImageDrawable(icon);
203                 if (appInfo.labelRes != 0) {
204                     try {
205                         Resources res = mContext.getPackageManager().getResourcesForApplication(
206                                 appInfo,
207                                 new Configuration(mContext.getResources().getConfiguration()));
208                         iconView.setContentDescription(res.getString(appInfo.labelRes));
209                     } catch (PackageManager.NameNotFoundException e) {
210                         Log.d(TAG, "Cannot find application resources for icon label.");
211                     }
212                 }
213             }
214         }
215         return toastView;
216     }
217 
218     /**
219      * Called on orientation changes to update parameters associated with the toast placement.
220      */
onOrientationChange(int orientation)221     public void onOrientationChange(int orientation) {
222         if (mPluginToast != null) {
223             mPluginToast.onOrientationChange(orientation);
224         }
225 
226         mDefaultY = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
227         mDefaultGravity =
228                 mContext.getResources().getInteger(R.integer.config_toastDefaultGravity);
229     }
230 
createInAnimator()231     private Animator createInAnimator() {
232         if (isPluginToast() && mPluginToast.getInAnimation() != null) {
233             return mPluginToast.getInAnimation();
234         }
235 
236         return ToastDefaultAnimation.Companion.toastIn(getView());
237     }
238 
createOutAnimator()239     private Animator createOutAnimator() {
240         if (isPluginToast() && mPluginToast.getOutAnimation() != null) {
241             return mPluginToast.getOutAnimation();
242         }
243         return ToastDefaultAnimation.Companion.toastOut(getView());
244     }
245 
246     /**
247      * Get badged app icon if necessary, similar as used in the Settings UI.
248      * @return The icon to use
249      */
getBadgedIcon(@onNull Context context, String packageName, int userId)250     public static Drawable getBadgedIcon(@NonNull Context context, String packageName,
251             int userId) {
252         if (!(context.getApplicationContext() instanceof Application)) {
253             return null;
254         }
255 
256         final Context userContext;
257         try {
258             userContext = context.createPackageContextAsUser("android",
259                 0, new UserHandle(userId));
260         } catch (NameNotFoundException e) {
261             Log.e(TAG, "Could not create user package context");
262             return null;
263         }
264 
265         final ApplicationsState appState =
266                 ApplicationsState.getInstance((Application) context.getApplicationContext());
267         if (!appState.isUserAdded(userId)) {
268             Log.d(TAG, "user hasn't been fully initialized, not showing an app icon for "
269                     + "packageName=" + packageName);
270             return null;
271         }
272 
273         final PackageManager packageManager = userContext.getPackageManager();
274         final AppEntry appEntry = appState.getEntry(packageName, userId);
275         if (appEntry == null || appEntry.info == null
276                 || !showApplicationIcon(appEntry.info, packageManager)) {
277             return null;
278         }
279 
280         final ApplicationInfo appInfo = appEntry.info;
281         UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
282         IconFactory iconFactory = IconFactory.obtain(context);
283         Bitmap iconBmp = iconFactory.createBadgedIconBitmap(
284                 appInfo.loadUnbadgedIcon(packageManager), user, true).icon;
285         return new BitmapDrawable(context.getResources(), iconBmp);
286     }
287 
showApplicationIcon(ApplicationInfo appInfo, PackageManager packageManager)288     private static boolean showApplicationIcon(ApplicationInfo appInfo,
289             PackageManager packageManager) {
290         if (hasFlag(appInfo.flags, FLAG_UPDATED_SYSTEM_APP)) {
291             return packageManager.getLaunchIntentForPackage(appInfo.packageName)
292                 != null;
293         }
294         return !hasFlag(appInfo.flags, FLAG_SYSTEM);
295     }
296 
hasFlag(int flags, int flag)297     private static boolean hasFlag(int flags, int flag) {
298         return (flags & flag) != 0;
299     }
300 }
301