1 package com.android.launcher3.popup;
2 
3 
4 import android.app.ActivityOptions;
5 import android.content.Context;
6 import android.content.Intent;
7 import android.graphics.Rect;
8 import android.graphics.drawable.Icon;
9 import android.os.Handler;
10 import android.os.Looper;
11 import android.view.View;
12 import android.view.accessibility.AccessibilityNodeInfo;
13 import android.widget.ImageView;
14 import android.widget.TextView;
15 
16 import com.android.launcher3.AbstractFloatingView;
17 import com.android.launcher3.BaseDraggingActivity;
18 import com.android.launcher3.ItemInfo;
19 import com.android.launcher3.Launcher;
20 import com.android.launcher3.LauncherSettings;
21 import com.android.launcher3.R;
22 import com.android.launcher3.WorkspaceItemInfo;
23 import com.android.launcher3.config.FeatureFlags;
24 import com.android.launcher3.model.AppLaunchTracker;
25 import com.android.launcher3.model.WidgetItem;
26 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
27 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
28 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
29 import com.android.launcher3.util.InstantAppResolver;
30 import com.android.launcher3.util.PackageManagerHelper;
31 import com.android.launcher3.util.PackageUserKey;
32 import com.android.launcher3.widget.WidgetsBottomSheet;
33 
34 import java.util.List;
35 /**
36  * Represents a system shortcut for a given app. The shortcut should have a label and icon, and an
37  * onClickListener that depends on the item that the shortcut services.
38  *
39  * Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
40  * @param <T>
41  */
42 public abstract class SystemShortcut<T extends BaseDraggingActivity>
43         extends ItemInfo {
44     private final int mIconResId;
45     private final int mLabelResId;
46     private final Icon mIcon;
47     private final CharSequence mLabel;
48     private final CharSequence mContentDescription;
49     private final int mAccessibilityActionId;
50 
SystemShortcut(int iconResId, int labelResId)51     public SystemShortcut(int iconResId, int labelResId) {
52         mIconResId = iconResId;
53         mLabelResId = labelResId;
54         mAccessibilityActionId = labelResId;
55         mIcon = null;
56         mLabel = null;
57         mContentDescription = null;
58     }
59 
SystemShortcut(Icon icon, CharSequence label, CharSequence contentDescription, int accessibilityActionId)60     public SystemShortcut(Icon icon, CharSequence label, CharSequence contentDescription,
61             int accessibilityActionId) {
62         mIcon = icon;
63         mLabel = label;
64         mContentDescription = contentDescription;
65         mAccessibilityActionId = accessibilityActionId;
66         mIconResId = 0;
67         mLabelResId = 0;
68     }
69 
SystemShortcut(SystemShortcut other)70     public SystemShortcut(SystemShortcut other) {
71         mIconResId = other.mIconResId;
72         mLabelResId = other.mLabelResId;
73         mIcon = other.mIcon;
74         mLabel = other.mLabel;
75         mContentDescription = other.mContentDescription;
76         mAccessibilityActionId = other.mAccessibilityActionId;
77     }
78 
79     /**
80      * Should be in the left group of icons in app's context menu header.
81      */
isLeftGroup()82     public boolean isLeftGroup() {
83         return false;
84     }
85 
setIconAndLabelFor(View iconView, TextView labelView)86     public void setIconAndLabelFor(View iconView, TextView labelView) {
87         if (mIcon != null) {
88             mIcon.loadDrawableAsync(iconView.getContext(),
89                     iconView::setBackground,
90                     new Handler(Looper.getMainLooper()));
91         } else {
92             iconView.setBackgroundResource(mIconResId);
93         }
94 
95         if (mLabel != null) {
96             labelView.setText(mLabel);
97         } else {
98             labelView.setText(mLabelResId);
99         }
100     }
101 
setIconAndContentDescriptionFor(ImageView view)102     public void setIconAndContentDescriptionFor(ImageView view) {
103         if (mIcon != null) {
104             mIcon.loadDrawableAsync(view.getContext(),
105                     view::setImageDrawable,
106                     new Handler(Looper.getMainLooper()));
107         } else {
108             view.setImageResource(mIconResId);
109         }
110 
111         view.setContentDescription(getContentDescription(view.getContext()));
112     }
113 
getContentDescription(Context context)114     private CharSequence getContentDescription(Context context) {
115         return mContentDescription != null ? mContentDescription : context.getText(mLabelResId);
116     }
117 
createAccessibilityAction(Context context)118     public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
119         return new AccessibilityNodeInfo.AccessibilityAction(mAccessibilityActionId,
120                 getContentDescription(context));
121     }
122 
hasHandlerForAction(int action)123     public boolean hasHandlerForAction(int action) {
124         return mAccessibilityActionId == action;
125     }
126 
getOnClickListener(T activity, ItemInfo itemInfo)127     public abstract View.OnClickListener getOnClickListener(T activity, ItemInfo itemInfo);
128 
129     public static class Widgets extends SystemShortcut<Launcher> {
130 
Widgets()131         public Widgets() {
132             super(R.drawable.ic_widget, R.string.widget_button_text);
133         }
134 
135         @Override
getOnClickListener(final Launcher launcher, final ItemInfo itemInfo)136         public View.OnClickListener getOnClickListener(final Launcher launcher,
137                 final ItemInfo itemInfo) {
138             if (itemInfo.getTargetComponent() == null) return null;
139             final List<WidgetItem> widgets =
140                     launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
141                             itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
142             if (widgets == null) {
143                 return null;
144             }
145             return (view) -> {
146                 AbstractFloatingView.closeAllOpenViews(launcher);
147                 WidgetsBottomSheet widgetsBottomSheet =
148                         (WidgetsBottomSheet) launcher.getLayoutInflater().inflate(
149                                 R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false);
150                 widgetsBottomSheet.populateAndShow(itemInfo);
151                 launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
152                         ControlType.WIDGETS_BUTTON, view);
153             };
154         }
155     }
156 
157     public static class AppInfo extends SystemShortcut {
AppInfo()158         public AppInfo() {
159             super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label);
160         }
161 
162         @Override
getOnClickListener( BaseDraggingActivity activity, ItemInfo itemInfo)163         public View.OnClickListener getOnClickListener(
164                 BaseDraggingActivity activity, ItemInfo itemInfo) {
165             return (view) -> {
166                 dismissTaskMenuView(activity);
167                 Rect sourceBounds = activity.getViewBounds(view);
168                 new PackageManagerHelper(activity).startDetailsActivityForInfo(
169                         itemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
170                 activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
171                         ControlType.APPINFO_TARGET, view);
172             };
173         }
174     }
175 
176     public static class Install extends SystemShortcut {
177         public Install() {
178             super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label);
179         }
180 
181         @Override
182         public View.OnClickListener getOnClickListener(
183                 BaseDraggingActivity activity, ItemInfo itemInfo) {
184             boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo) &&
185                     ((WorkspaceItemInfo) itemInfo).hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
186             boolean isInstantApp = false;
187             if (itemInfo instanceof com.android.launcher3.AppInfo) {
188                 com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
189                 isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
190             }
191             boolean enabled = supportsWebUI || isInstantApp;
192             if (!enabled) {
193                 return null;
194             }
195             return createOnClickListener(activity, itemInfo);
196         }
197 
198         public View.OnClickListener createOnClickListener(
199                 BaseDraggingActivity activity, ItemInfo itemInfo) {
200             return view -> {
201                 Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
202                         itemInfo.getTargetComponent().getPackageName());
203                 activity.startActivitySafely(view, intent, itemInfo, null);
204                 AbstractFloatingView.closeAllOpenViews(activity);
205             };
206         }
207     }
208 
209     public static class DismissPrediction extends SystemShortcut<Launcher> {
210         public DismissPrediction() {
211             super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label);
212         }
213 
214         @Override
215         public View.OnClickListener getOnClickListener(Launcher activity, ItemInfo itemInfo) {
216             if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null;
217             if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) return null;
218             return (view) -> {
219                 PopupContainerWithArrow.closeAllOpenViews(activity);
220                 activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
221                         ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS);
222                 AppLaunchTracker.INSTANCE.get(view.getContext())
223                         .onDismissApp(itemInfo.getTargetComponent(),
224                                 itemInfo.user,
225                                 AppLaunchTracker.CONTAINER_PREDICTIONS);
226             };
227         }
228     }
229 
230     protected static void dismissTaskMenuView(BaseDraggingActivity activity) {
231         AbstractFloatingView.closeOpenViews(activity, true,
232             AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
233     }
234 }
235