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