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