1 package com.android.launcher3.popup;
2 
3 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP;
4 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP;
5 
6 import android.app.ActivityOptions;
7 import android.content.Context;
8 import android.content.Intent;
9 import android.graphics.Rect;
10 import android.view.View;
11 import android.view.accessibility.AccessibilityNodeInfo;
12 import android.widget.ImageView;
13 import android.widget.TextView;
14 
15 import androidx.annotation.Nullable;
16 
17 import com.android.launcher3.AbstractFloatingView;
18 import com.android.launcher3.BaseDraggingActivity;
19 import com.android.launcher3.Launcher;
20 import com.android.launcher3.R;
21 import com.android.launcher3.model.WidgetItem;
22 import com.android.launcher3.model.data.ItemInfo;
23 import com.android.launcher3.model.data.WorkspaceItemInfo;
24 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
25 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
26 import com.android.launcher3.util.InstantAppResolver;
27 import com.android.launcher3.util.PackageManagerHelper;
28 import com.android.launcher3.util.PackageUserKey;
29 import com.android.launcher3.widget.WidgetsBottomSheet;
30 
31 import java.util.List;
32 
33 /**
34  * Represents a system shortcut for a given app. The shortcut should have a label and icon, and an
35  * onClickListener that depends on the item that the shortcut services.
36  *
37  * Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
38  * @param <T>
39  */
40 public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo
41         implements View.OnClickListener {
42 
43     private final int mIconResId;
44     private final int mLabelResId;
45     private final int mAccessibilityActionId;
46 
47     protected final T mTarget;
48     protected final ItemInfo mItemInfo;
49 
SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo)50     public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo) {
51         mIconResId = iconResId;
52         mLabelResId = labelResId;
53         mAccessibilityActionId = labelResId;
54         mTarget = target;
55         mItemInfo = itemInfo;
56     }
57 
SystemShortcut(SystemShortcut<T> other)58     public SystemShortcut(SystemShortcut<T> other) {
59         mIconResId = other.mIconResId;
60         mLabelResId = other.mLabelResId;
61         mAccessibilityActionId = other.mAccessibilityActionId;
62         mTarget = other.mTarget;
63         mItemInfo = other.mItemInfo;
64     }
65 
66     /**
67      * Should be in the left group of icons in app's context menu header.
68      */
isLeftGroup()69     public boolean isLeftGroup() {
70         return false;
71     }
72 
setIconAndLabelFor(View iconView, TextView labelView)73     public void setIconAndLabelFor(View iconView, TextView labelView) {
74         iconView.setBackgroundResource(mIconResId);
75         labelView.setText(mLabelResId);
76     }
77 
setIconAndContentDescriptionFor(ImageView view)78     public void setIconAndContentDescriptionFor(ImageView view) {
79         view.setImageResource(mIconResId);
80         view.setContentDescription(view.getContext().getText(mLabelResId));
81     }
82 
createAccessibilityAction(Context context)83     public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
84         return new AccessibilityNodeInfo.AccessibilityAction(
85                 mAccessibilityActionId, context.getText(mLabelResId));
86     }
87 
hasHandlerForAction(int action)88     public boolean hasHandlerForAction(int action) {
89         return mAccessibilityActionId == action;
90     }
91 
92     public interface Factory<T extends BaseDraggingActivity> {
93 
getShortcut(T activity, ItemInfo itemInfo)94         @Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo);
95     }
96 
97     public static final Factory<Launcher> WIDGETS = (launcher, itemInfo) -> {
98         if (itemInfo.getTargetComponent() == null) return null;
99         final List<WidgetItem> widgets =
100                 launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
101                         itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
102         if (widgets == null) {
103             return null;
104         }
105         return new Widgets(launcher, itemInfo);
106     };
107 
108     public static class Widgets extends SystemShortcut<Launcher> {
Widgets(Launcher target, ItemInfo itemInfo)109         public Widgets(Launcher target, ItemInfo itemInfo) {
110             super(R.drawable.ic_widget, R.string.widget_button_text, target, itemInfo);
111         }
112 
113         @Override
onClick(View view)114         public void onClick(View view) {
115             AbstractFloatingView.closeAllOpenViews(mTarget);
116             WidgetsBottomSheet widgetsBottomSheet =
117                     (WidgetsBottomSheet) mTarget.getLayoutInflater().inflate(
118                             R.layout.widgets_bottom_sheet, mTarget.getDragLayer(), false);
119             widgetsBottomSheet.populateAndShow(mItemInfo);
120             mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
121                     ControlType.WIDGETS_BUTTON, view);
122             mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
123                     .log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP);
124         }
125     }
126 
127     public static final Factory<BaseDraggingActivity> APP_INFO = AppInfo::new;
128 
129     public static class AppInfo extends SystemShortcut {
130 
AppInfo(BaseDraggingActivity target, ItemInfo itemInfo)131         public AppInfo(BaseDraggingActivity target, ItemInfo itemInfo) {
132             super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
133                     itemInfo);
134         }
135 
136         @Override
onClick(View view)137         public void onClick(View view) {
138             dismissTaskMenuView(mTarget);
139             Rect sourceBounds = mTarget.getViewBounds(view);
140             new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
141                     mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
142             mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
143                     ControlType.APPINFO_TARGET, view);
144             mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
145                     .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP);
146         }
147     }
148 
149     public static final Factory<BaseDraggingActivity> INSTALL = (activity, itemInfo) -> {
150         boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo)
151                 && ((WorkspaceItemInfo) itemInfo).hasStatusFlag(
152                         WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
153         boolean isInstantApp = false;
154         if (itemInfo instanceof com.android.launcher3.model.data.AppInfo) {
155             com.android.launcher3.model.data.AppInfo
156                     appInfo = (com.android.launcher3.model.data.AppInfo) itemInfo;
157             isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
158         }
159         boolean enabled = supportsWebUI || isInstantApp;
160         if (!enabled) {
161             return null;
162         }
163         return new Install(activity, itemInfo);
164     };
165 
166     public static class Install extends SystemShortcut {
167 
Install(BaseDraggingActivity target, ItemInfo itemInfo)168         public Install(BaseDraggingActivity target, ItemInfo itemInfo) {
169             super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label,
170                     target, itemInfo);
171         }
172 
173         @Override
onClick(View view)174         public void onClick(View view) {
175             Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
176                     mItemInfo.getTargetComponent().getPackageName());
177             mTarget.startActivitySafely(view, intent, mItemInfo, null);
178             AbstractFloatingView.closeAllOpenViews(mTarget);
179         }
180     }
181 
dismissTaskMenuView(BaseDraggingActivity activity)182     public static void dismissTaskMenuView(BaseDraggingActivity activity) {
183         AbstractFloatingView.closeOpenViews(activity, true,
184             AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
185     }
186 }
187