Moving click and long click handling for items from launcher to separate class
Change-Id: I1d12862205f7fa7f3671ff351e66dba58bb430e2
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index fdf468c..375deb7 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -19,11 +19,6 @@
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -39,7 +34,6 @@
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.ActivityOptions;
-import android.app.AlertDialog;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.content.ActivityNotFoundException;
@@ -47,7 +41,6 @@
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.ContextWrapper;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
@@ -80,6 +73,7 @@
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@@ -99,10 +93,8 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dynamicui.WallpaperColorInfo;
-import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
@@ -115,6 +107,7 @@
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.states.InternalStateHandler;
import com.android.launcher3.states.RotationHelper;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -155,7 +148,7 @@
* Default launcher application.
*/
public class Launcher extends BaseActivity
- implements LauncherExterns, View.OnClickListener, OnLongClickListener,
+ implements LauncherExterns, OnClickListener, OnLongClickListener,
LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
WallpaperColorInfo.OnThemeChangeListener {
public static final String TAG = "Launcher";
@@ -170,8 +163,8 @@
private static final int REQUEST_PICK_WALLPAPER = 10;
private static final int REQUEST_BIND_APPWIDGET = 11;
- private static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
- private static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
+ public static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
+ public static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
private static final int REQUEST_PERMISSION_CALL_PHONE = 14;
@@ -1017,7 +1010,7 @@
BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_icon, parent, false);
favorite.applyFromShortcutInfo(info);
- favorite.setOnClickListener(this);
+ favorite.setOnClickListener(ItemClickHandler.INSTANCE);
favorite.setOnFocusChangeListener(mFocusHandler);
return favorite;
}
@@ -1670,6 +1663,7 @@
*
* @param v The view representing the clicked shortcut.
*/
+ @Override
public void onClick(View v) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
@@ -1702,21 +1696,6 @@
}
return;
}
-
- Object tag = v.getTag();
- if (tag instanceof ShortcutInfo) {
- onClickAppShortcut(v);
- } else if (tag instanceof FolderInfo) {
- if (v instanceof FolderIcon) {
- onClickFolderIcon(v);
- }
- } else if (tag instanceof AppInfo) {
- startAppShortcutOrInfoActivity(v);
- } else if (tag instanceof LauncherAppWidgetInfo) {
- if (v instanceof PendingAppWidgetHostView) {
- onClickPendingWidget((PendingAppWidgetHostView) v);
- }
- }
}
@SuppressLint("ClickableViewAccessibility")
@@ -1725,173 +1704,6 @@
}
/**
- * Event handler for the app widget view which has not fully restored.
- */
- public void onClickPendingWidget(final PendingAppWidgetHostView v) {
- if (mIsSafeModeEnabled) {
- Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
- return;
- }
-
- final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
- if (v.isReadyForClickSetup()) {
- LauncherAppWidgetProviderInfo appWidgetInfo =
- mAppWidgetManager.findProvider(info.providerName, info.user);
- if (appWidgetInfo == null) {
- return;
- }
- WidgetAddFlowHandler addFlowHandler = new WidgetAddFlowHandler(appWidgetInfo);
-
- if (info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
- if (!info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
- // This should not happen, as we make sure that an Id is allocated during bind.
- return;
- }
- addFlowHandler.startBindFlow(this, info.appWidgetId, info,
- REQUEST_BIND_PENDING_APPWIDGET);
- } else {
- addFlowHandler.startConfigActivity(this, info, REQUEST_RECONFIGURE_APPWIDGET);
- }
- } else {
- final String packageName = info.providerName.getPackageName();
- onClickPendingAppItem(v, packageName, info.installProgress >= 0);
- }
- }
-
- private void onClickPendingAppItem(final View v, final String packageName,
- boolean downloadStarted) {
- if (downloadStarted) {
- // If the download has started, simply direct to the market app.
- startMarketIntentForPackage(v, packageName);
- return;
- }
- new AlertDialog.Builder(this)
- .setTitle(R.string.abandoned_promises_title)
- .setMessage(R.string.abandoned_promise_explanation)
- .setPositiveButton(R.string.abandoned_search, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- startMarketIntentForPackage(v, packageName);
- }
- })
- .setNeutralButton(R.string.abandoned_clean_this,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- final UserHandle user = Process.myUserHandle();
- mWorkspace.removeAbandonedPromise(packageName, user);
- }
- })
- .create().show();
- }
-
- private void startMarketIntentForPackage(View v, String packageName) {
- ItemInfo item = (ItemInfo) v.getTag();
- Intent intent = new PackageManagerHelper(v.getContext()).getMarketIntent(packageName);
- startActivitySafely(v, intent, item);
- }
-
- /**
- * Event handler for an app shortcut click.
- *
- * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
- */
- protected void onClickAppShortcut(final View v) {
- if (LOGD) Log.d(TAG, "onClickAppShortcut");
- Object tag = v.getTag();
- if (!(tag instanceof ShortcutInfo)) {
- throw new IllegalArgumentException("Input must be a Shortcut");
- }
-
- // Open shortcut
- final ShortcutInfo shortcut = (ShortcutInfo) tag;
-
- if (shortcut.isDisabled()) {
- final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
- if ((disabledFlags &
- ~FLAG_DISABLED_SUSPENDED &
- ~FLAG_DISABLED_QUIET_USER) == 0) {
- // If the app is only disabled because of the above flags, launch activity anyway.
- // Framework will tell the user why the app is suspended.
- } else {
- if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
- // Use a message specific to this shortcut, if it has one.
- Toast.makeText(this, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
- return;
- }
- // Otherwise just use a generic error message.
- int error = R.string.activity_not_available;
- if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
- error = R.string.safemode_shortcut_error;
- } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 ||
- (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
- error = R.string.shortcut_not_available;
- }
- Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
- return;
- }
- }
-
- // Check for abandoned promise
- if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
- String packageName = shortcut.intent.getComponent() != null ?
- shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
- if (!TextUtils.isEmpty(packageName)) {
- onClickPendingAppItem(v, packageName,
- shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE));
- return;
- }
- }
-
- // Start activities
- startAppShortcutOrInfoActivity(v);
- }
-
- private void startAppShortcutOrInfoActivity(View v) {
- ItemInfo item = (ItemInfo) v.getTag();
- Intent intent;
- if (item instanceof PromiseAppInfo) {
- PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
- intent = promiseAppInfo.getMarketIntent(this);
- } else {
- intent = item.getIntent();
- }
- if (intent == null) {
- throw new IllegalArgumentException("Input must have a valid intent");
- }
- if (item instanceof ShortcutInfo) {
- ShortcutInfo si = (ShortcutInfo) item;
- if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)
- && intent.getAction() == Intent.ACTION_VIEW) {
- // make a copy of the intent that has the package set to null
- // we do this because the platform sometimes disables instant
- // apps temporarily (triggered by the user) and fallbacks to the
- // web ui. This only works though if the package isn't set
- intent = new Intent(intent);
- intent.setPackage(null);
- }
- }
- startActivitySafely(v, intent, item);
- }
-
- /**
- * Event handler for a folder icon click.
- *
- * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
- */
- protected void onClickFolderIcon(View v) {
- if (LOGD) Log.d(TAG, "onClickFolder");
- if (!(v instanceof FolderIcon)){
- throw new IllegalArgumentException("Input must be a FolderIcon");
- }
-
- Folder folder = ((FolderIcon) v).getFolder();
- if (!folder.isOpen() && !folder.isDestroyed()) {
- // Open the requested folder
- folder.animateOpen();
- }
- }
-
- /**
* Event handler for the wallpaper picker button that appears after a long press
* on the home screen.
*/
@@ -2081,45 +1893,25 @@
}
}
- CellLayout.CellInfo longClickCellInfo = null;
- View itemUnderLongClick = null;
- if (v.getTag() instanceof ItemInfo) {
- ItemInfo info = (ItemInfo) v.getTag();
- longClickCellInfo = new CellLayout.CellInfo(v, info);
- itemUnderLongClick = longClickCellInfo.cell;
- mPendingRequestArgs = null;
- }
-
// The hotseat touch handling does not go through Workspace, and we always allow long press
// on hotseat items.
if (!mDragController.isDragging()) {
- if (itemUnderLongClick == null) {
- // User long pressed on empty space
- if (mWorkspace.isPageRearrangeEnabled()) {
- mWorkspace.startReordering(v);
- getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
- Action.Direction.NONE, ContainerType.OVERVIEW);
- } else {
- if (ignoreLongPressToOverview) {
- return false;
- }
- getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
- Action.Direction.NONE, ContainerType.WORKSPACE,
- mWorkspace.getCurrentPage());
- UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
- }
- mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ // User long pressed on empty space
+ if (mWorkspace.isPageRearrangeEnabled()) {
+ mWorkspace.startReordering(v);
+ getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+ Action.Direction.NONE, ContainerType.OVERVIEW);
} else {
- final boolean isAllAppsButton =
- !FeatureFlags.NO_ALL_APPS_ICON && isHotseatLayout(v) &&
- mDeviceProfile.inv.isAllAppsButtonRank(mHotseat.getOrderInHotseat(
- longClickCellInfo.cellX, longClickCellInfo.cellY));
- if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
- // User long pressed on an item
- mWorkspace.startDrag(longClickCellInfo, new DragOptions());
+ if (ignoreLongPressToOverview) {
+ return false;
}
+ getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+ Action.Direction.NONE, ContainerType.WORKSPACE,
+ mWorkspace.getCurrentPage());
+ UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
}
+ mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
return true;
}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index ec608ca..8588c7a 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -79,7 +79,7 @@
* A message to display when the user tries to start a disabled shortcut.
* This is currently only used for deep shortcuts.
*/
- CharSequence disabledMessage;
+ public CharSequence disabledMessage;
public int status;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 41d0cd8..f329f5e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -81,6 +81,7 @@
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -935,10 +936,9 @@
Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
}
- if (!(child instanceof Folder)) {
- child.setHapticFeedbackEnabled(false);
- child.setOnLongClickListener(mLongClickListener);
- }
+ child.setHapticFeedbackEnabled(false);
+ child.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
+
if (child instanceof DropTarget) {
mDragController.addDropTarget((DropTarget) child);
}
@@ -1635,7 +1635,6 @@
new DragPreviewProvider(child), options);
}
-
public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
DragPreviewProvider previewProvider, DragOptions dragOptions) {
child.clearFocus();
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 2cd8b1d..4398f6e 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -25,6 +25,7 @@
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
@@ -358,29 +359,14 @@
mDragInfo.dragType = DragType.WIDGET;
}
- CellLayout.CellInfo cellInfo = new CellLayout.CellInfo(item, info);
-
Rect pos = new Rect();
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY());
-
- Folder folder = Folder.getOpen(mLauncher);
- if (folder != null) {
- if (!folder.getItemsInReadingOrder().contains(item)) {
- folder.close(true);
- folder = null;
- }
- }
-
mLauncher.getDragController().addDragListener(this);
DragOptions options = new DragOptions();
options.isAccessibleDrag = true;
- if (folder != null) {
- folder.startDrag(cellInfo.cell, options);
- } else {
- mLauncher.getWorkspace().startDrag(cellInfo, options);
- }
+ ItemLongClickListener.beginDrag(item, mLauncher, info, options);
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 2cd9d1b..3fe5d7a 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -34,24 +34,19 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import com.android.launcher3.AppInfo;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Insettable;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.graphics.ColorScrim;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -65,7 +60,7 @@
* The all apps view container.
*/
public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
- OnLongClickListener, Insettable, OnDeviceProfileChangeListener {
+ Insettable, OnDeviceProfileChangeListener {
private final Launcher mLauncher;
private final AdapterHolder[] mAH;
@@ -252,37 +247,6 @@
}
@Override
- public boolean onLongClick(final View v) {
- // When we have exited all apps or are in transition, disregard long clicks
- if (!mLauncher.isInState(LauncherState.ALL_APPS) ||
- mLauncher.getWorkspace().isSwitchingState()) return false;
- // Return if global dragging is not enabled or we are already dragging
- if (!mLauncher.isDraggingEnabled()) return false;
- if (mLauncher.getDragController().isDragging()) return false;
-
- // Start the drag
- final DragController dragController = mLauncher.getDragController();
- dragController.addDragListener(new DragController.DragListener() {
- @Override
- public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- v.setVisibility(INVISIBLE);
- }
-
- @Override
- public void onDragEnd() {
- v.setVisibility(VISIBLE);
- dragController.removeDragListener(this);
- }
- });
-
- DeviceProfile grid = mLauncher.getDeviceProfile();
- DragOptions options = new DragOptions();
- options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
- mLauncher.getWorkspace().beginDragShared(v, this, options);
- return false;
- }
-
- @Override
public void onDropCompleted(View target, DragObject d, boolean success) { }
@Override
@@ -476,8 +440,7 @@
AdapterHolder(boolean isWork) {
appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore, isWork);
- adapter = new AllAppsGridAdapter(mLauncher, appsList, mLauncher,
- AllAppsContainerView.this, true);
+ adapter = new AllAppsGridAdapter(mLauncher, appsList);
appsList.setAdapter(adapter);
layoutManager = adapter.getLayoutManager();
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index b9443fd..27fc53a 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -38,6 +38,8 @@
import com.android.launcher3.R;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageManagerHelper;
import java.util.List;
@@ -176,8 +178,6 @@
private final AlphabeticalAppsList mApps;
private final GridLayoutManager mGridLayoutMgr;
private final GridSpanSizer mGridSizer;
- private final View.OnClickListener mIconClickListener;
- private final View.OnLongClickListener mIconLongClickListener;
private final int mAppsPerRow;
@@ -189,8 +189,7 @@
// The intent to send off to the market app, updated each time the search query changes.
private Intent mMarketSearchIntent;
- public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
- iconClickListener, View.OnLongClickListener iconLongClickListener, boolean springAnim) {
+ public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps) {
Resources res = launcher.getResources();
mLauncher = launcher;
mApps = apps;
@@ -199,8 +198,6 @@
mGridLayoutMgr = new AppsGridLayoutManager(launcher);
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
mLayoutInflater = LayoutInflater.from(launcher);
- mIconClickListener = iconClickListener;
- mIconLongClickListener = iconLongClickListener;
mAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
mGridLayoutMgr.setSpanCount(mAppsPerRow);
@@ -252,8 +249,8 @@
case VIEW_TYPE_ICON:
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.all_apps_icon, parent, false);
- icon.setOnClickListener(mIconClickListener);
- icon.setOnLongClickListener(mIconLongClickListener);
+ icon.setOnClickListener(ItemClickHandler.INSTANCE);
+ icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
icon.setOnFocusChangeListener(mIconFocusListener);
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 13926db..6c94273 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -63,6 +63,7 @@
import com.android.launcher3.dragndrop.BaseItemDragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -159,14 +160,14 @@
.inflate(resId, group, false);
icon.setClipToPadding(false);
- icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
+ icon.mFolderName = icon.findViewById(R.id.folder_icon_name);
icon.mFolderName.setText(folderInfo.title);
icon.mFolderName.setCompoundDrawablePadding(0);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
icon.setTag(folderInfo);
- icon.setOnClickListener(launcher);
+ icon.setOnClickListener(ItemClickHandler.INSTANCE);
icon.mInfo = folderInfo;
icon.mLauncher = launcher;
icon.mBadgeRenderer = launcher.getDeviceProfile().mBadgeRenderer;
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index b3ef7bb..e427a81 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -77,6 +77,7 @@
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Themes;
@@ -907,12 +908,9 @@
@Override
public boolean onLongClick(View v) {
+ if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
// Return early if not the correct view
if (!(v.getParent() instanceof DeepShortcutView)) return false;
- // Return early if global dragging is not enabled
- if (!mLauncher.isDraggingEnabled()) return false;
- // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
- if (mLauncher.getDragController().isDragging()) return false;
// Long clicked on a shortcut.
DeepShortcutView sv = (DeepShortcutView) v.getParent();
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 450a690..9ad266b 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -30,6 +30,7 @@
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.touch.ItemClickHandler;
/**
* A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
@@ -120,7 +121,7 @@
mBubbleText.setText(usingLongLabel ? longLabel : mDetail.getShortLabel());
// TODO: Add the click handler to this view directly and not the child view.
- mBubbleText.setOnClickListener(Launcher.getLauncher(getContext()));
+ mBubbleText.setOnClickListener(ItemClickHandler.INSTANCE);
mBubbleText.setOnLongClickListener(container);
mBubbleText.setOnTouchListener(container);
}
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
new file mode 100644
index 0000000..f2f5592
--- /dev/null
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.touch;
+
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
+import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
+
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.os.Process;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.PromiseAppInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
+import com.android.launcher3.widget.WidgetAddFlowHandler;
+
+/**
+ * Class for handling clicks on workspace and all-apps items
+ */
+public class ItemClickHandler {
+
+ /**
+ * Instance used for click handling on items
+ */
+ public static final OnClickListener INSTANCE = ItemClickHandler::onClick;
+
+ private static void onClick(View v) {
+ // Make sure that rogue clicks don't get through while allapps is launching, or after the
+ // view has detached (it's possible for this to happen if the view is removed mid touch).
+ if (v.getWindowToken() == null) {
+ return;
+ }
+
+ Launcher launcher = Launcher.getLauncher(v.getContext());
+ if (!launcher.getWorkspace().isFinishedSwitchingState()) {
+ return;
+ }
+
+ Object tag = v.getTag();
+ if (tag instanceof ShortcutInfo) {
+ onClickAppShortcut(v, (ShortcutInfo) tag, launcher);
+ } else if (tag instanceof FolderInfo) {
+ if (v instanceof FolderIcon) {
+ onClickFolderIcon(v);
+ }
+ } else if (tag instanceof AppInfo) {
+ startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
+ } else if (tag instanceof LauncherAppWidgetInfo) {
+ if (v instanceof PendingAppWidgetHostView) {
+ onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
+ }
+ }
+ }
+
+ /**
+ * Event handler for a folder icon click.
+ *
+ * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
+ */
+ private static void onClickFolderIcon(View v) {
+ Folder folder = ((FolderIcon) v).getFolder();
+ if (!folder.isOpen() && !folder.isDestroyed()) {
+ // Open the requested folder
+ folder.animateOpen();
+ }
+ }
+
+ /**
+ * Event handler for the app widget view which has not fully restored.
+ */
+ private static void onClickPendingWidget(PendingAppWidgetHostView v, Launcher launcher) {
+ if (launcher.getPackageManager().isSafeMode()) {
+ Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
+ if (v.isReadyForClickSetup()) {
+ LauncherAppWidgetProviderInfo appWidgetInfo = AppWidgetManagerCompat
+ .getInstance(launcher).findProvider(info.providerName, info.user);
+ if (appWidgetInfo == null) {
+ return;
+ }
+ WidgetAddFlowHandler addFlowHandler = new WidgetAddFlowHandler(appWidgetInfo);
+
+ if (info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+ if (!info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
+ // This should not happen, as we make sure that an Id is allocated during bind.
+ return;
+ }
+ addFlowHandler.startBindFlow(launcher, info.appWidgetId, info,
+ REQUEST_BIND_PENDING_APPWIDGET);
+ } else {
+ addFlowHandler.startConfigActivity(launcher, info, REQUEST_RECONFIGURE_APPWIDGET);
+ }
+ } else {
+ final String packageName = info.providerName.getPackageName();
+ onClickPendingAppItem(v, launcher, packageName, info.installProgress >= 0);
+ }
+ }
+
+ private static void onClickPendingAppItem(View v, Launcher launcher, String packageName,
+ boolean downloadStarted) {
+ if (downloadStarted) {
+ // If the download has started, simply direct to the market app.
+ startMarketIntentForPackage(v, launcher, packageName);
+ return;
+ }
+ new AlertDialog.Builder(launcher)
+ .setTitle(R.string.abandoned_promises_title)
+ .setMessage(R.string.abandoned_promise_explanation)
+ .setPositiveButton(R.string.abandoned_search,
+ (d, i) -> startMarketIntentForPackage(v, launcher, packageName))
+ .setNeutralButton(R.string.abandoned_clean_this,
+ (d, i) -> launcher.getWorkspace()
+ .removeAbandonedPromise(packageName, Process.myUserHandle()))
+ .create().show();
+ }
+
+ private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
+ ItemInfo item = (ItemInfo) v.getTag();
+ Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
+ launcher.startActivitySafely(v, intent, item);
+ }
+
+ /**
+ * Event handler for an app shortcut click.
+ *
+ * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
+ */
+ private static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher) {
+ if (shortcut.isDisabled()) {
+ final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
+ if ((disabledFlags &
+ ~FLAG_DISABLED_SUSPENDED &
+ ~FLAG_DISABLED_QUIET_USER) == 0) {
+ // If the app is only disabled because of the above flags, launch activity anyway.
+ // Framework will tell the user why the app is suspended.
+ } else {
+ if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
+ // Use a message specific to this shortcut, if it has one.
+ Toast.makeText(launcher, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ // Otherwise just use a generic error message.
+ int error = R.string.activity_not_available;
+ if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
+ error = R.string.safemode_shortcut_error;
+ } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 ||
+ (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
+ error = R.string.shortcut_not_available;
+ }
+ Toast.makeText(launcher, error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+
+ // Check for abandoned promise
+ if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
+ String packageName = shortcut.intent.getComponent() != null ?
+ shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
+ if (!TextUtils.isEmpty(packageName)) {
+ onClickPendingAppItem(v, launcher, packageName,
+ shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE));
+ return;
+ }
+ }
+
+ // Start activities
+ startAppShortcutOrInfoActivity(v, shortcut, launcher);
+ }
+
+ private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
+ Intent intent;
+ if (item instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
+ intent = promiseAppInfo.getMarketIntent(launcher);
+ } else {
+ intent = item.getIntent();
+ }
+ if (intent == null) {
+ throw new IllegalArgumentException("Input must have a valid intent");
+ }
+ if (item instanceof ShortcutInfo) {
+ ShortcutInfo si = (ShortcutInfo) item;
+ if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)
+ && intent.getAction() == Intent.ACTION_VIEW) {
+ // make a copy of the intent that has the package set to null
+ // we do this because the platform sometimes disables instant
+ // apps temporarily (triggered by the user) and fallbacks to the
+ // web ui. This only works though if the package isn't set
+ intent = new Intent(intent);
+ intent.setPackage(null);
+ }
+ }
+ launcher.startActivitySafely(v, intent, item);
+ }
+}
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
new file mode 100644
index 0000000..f10a695
--- /dev/null
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.touch;
+
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.view.View;
+import android.view.View.OnLongClickListener;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.folder.Folder;
+
+/**
+ * Class to handle long-clicks on workspace items and start drag as a result.
+ */
+public class ItemLongClickListener {
+
+ public static OnLongClickListener INSTANCE_WORKSPACE =
+ ItemLongClickListener::onWorkspaceItemLongClick;
+
+ public static OnLongClickListener INSTANCE_ALL_APPS =
+ ItemLongClickListener::onAllAppsItemLongClick;
+
+ private static boolean onWorkspaceItemLongClick(View v) {
+ Launcher launcher = Launcher.getLauncher(v.getContext());
+ if (!canStartDrag(launcher)) return false;
+ if (!launcher.isInState(NORMAL) && !launcher.isInState(OVERVIEW)) return false;
+ if (!(v.getTag() instanceof ItemInfo)) return false;
+
+ launcher.setWaitingForResult(null);
+ beginDrag(v, launcher, (ItemInfo) v.getTag(), new DragOptions());
+ return true;
+ }
+
+ public static void beginDrag(View v, Launcher launcher, ItemInfo info,
+ DragOptions dragOptions) {
+ if (info.container >= 0) {
+ Folder folder = Folder.getOpen(launcher);
+ if (folder != null) {
+ if (!folder.getItemsInReadingOrder().contains(v)) {
+ folder.close(true);
+ } else {
+ folder.startDrag(v, dragOptions);
+ return;
+ }
+ }
+ }
+
+ CellLayout.CellInfo longClickCellInfo = new CellLayout.CellInfo(v, info);
+ launcher.getWorkspace().startDrag(longClickCellInfo, dragOptions);
+ }
+
+ private static boolean onAllAppsItemLongClick(View v) {
+ Launcher launcher = Launcher.getLauncher(v.getContext());
+ if (!canStartDrag(launcher)) return false;
+ // When we have exited all apps or are in transition, disregard long clicks
+ if (!launcher.isInState(LauncherState.ALL_APPS) ||
+ launcher.getWorkspace().isSwitchingState()) return false;
+
+ // Start the drag
+ final DragController dragController = launcher.getDragController();
+ dragController.addDragListener(new DragController.DragListener() {
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ v.setVisibility(INVISIBLE);
+ }
+
+ @Override
+ public void onDragEnd() {
+ v.setVisibility(VISIBLE);
+ dragController.removeDragListener(this);
+ }
+ });
+
+ DeviceProfile grid = launcher.getDeviceProfile();
+ DragOptions options = new DragOptions();
+ options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
+ launcher.getWorkspace().beginDragShared(v, launcher.getAppsView(), options);
+ return false;
+ }
+
+ public static boolean canStartDrag(Launcher launcher) {
+ if (launcher == null) {
+ return false;
+ }
+ // We prevent dragging when we are loading the workspace as it is possible to pick up a view
+ // that is subsequently removed from the workspace in startBinding().
+ if (launcher.isWorkspaceLocked()) return false;
+ // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
+ if (launcher.getDragController().isDragging()) return false;
+
+ return true;
+ }
+}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index c51842d..10708d6 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -32,6 +32,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.graphics.ColorScrim;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.SystemUiController;
@@ -71,7 +72,7 @@
@Override
public final boolean onLongClick(View v) {
- if (!mLauncher.isDraggingEnabled()) return false;
+ if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
if (v instanceof WidgetCell) {
return beginDraggingWidget((WidgetCell) v);
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 6970833..961799d 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -43,6 +43,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Themes;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -83,7 +84,7 @@
setElevation(getResources().getDimension(R.dimen.pending_widget_elevation));
updateAppWidget(null);
- setOnClickListener(mLauncher);
+ setOnClickListener(ItemClickHandler.INSTANCE);
if (info.pendingItemInfo == null) {
info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName());