From 8a2a63b3f0d0477a96f57037f926a3671705d8e7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 6 Mar 2018 22:15:18 -0800 Subject: [PATCH] Moving click and long click handling for items from launcher to separate class Change-Id: I1d12862205f7fa7f3671ff351e66dba58bb430e2 --- src/com/android/launcher3/Launcher.java | 248 ++---------------- src/com/android/launcher3/ShortcutInfo.java | 2 +- src/com/android/launcher3/Workspace.java | 9 +- .../LauncherAccessibilityDelegate.java | 18 +- .../allapps/AllAppsContainerView.java | 41 +-- .../launcher3/allapps/AllAppsGridAdapter.java | 13 +- .../android/launcher3/folder/FolderIcon.java | 5 +- .../popup/PopupContainerWithArrow.java | 6 +- .../launcher3/shortcuts/DeepShortcutView.java | 3 +- .../launcher3/touch/ItemClickHandler.java | 232 ++++++++++++++++ .../touch/ItemLongClickListener.java | 118 +++++++++ .../launcher3/widget/BaseWidgetSheet.java | 3 +- .../widget/PendingAppWidgetHostView.java | 3 +- 13 files changed, 395 insertions(+), 306 deletions(-) create mode 100644 src/com/android/launcher3/touch/ItemClickHandler.java create mode 100644 src/com/android/launcher3/touch/ItemLongClickListener.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index fdf468c98d..375deb745c 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -19,11 +19,6 @@ package com.android.launcher3; 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.animation.ValueAnimator; 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.BroadcastReceiver; 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.LayoutInflater; 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.compat.LauncherAppsCompatVO; 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.popup.PopupDataProvider; 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 @@ import java.util.Set; * 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 @@ public class Launcher extends BaseActivity 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 @@ public class Launcher extends BaseActivity 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 @@ public class Launcher extends BaseActivity * * @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 @@ public class Launcher extends BaseActivity } 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") @@ -1724,173 +1703,6 @@ public class Launcher extends BaseActivity return false; } - /** - * 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 @@ public class Launcher extends BaseActivity } } - 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 ec608ca8de..8588c7a92a 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -79,7 +79,7 @@ public class ShortcutInfo extends ItemInfoWithIcon { * 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 41d0cd8bc5..f329f5ea83 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -81,6 +81,7 @@ import com.android.launcher3.graphics.WorkspaceAndHotseatScrim; 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 @@ public class Workspace extends PagedView 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 @@ public class Workspace extends PagedView 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 2cd8b1d729..4398f6e6b6 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.DropTarget.DragObject; 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 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme 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 2cd9d1bb8f..3fe5d7ae15 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -34,24 +34,19 @@ import android.view.KeyEvent; 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 @@ import com.android.launcher3.views.SpringRelativeLayout; * 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; @@ -251,37 +246,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo return super.dispatchKeyEvent(event); } - @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) { } @@ -476,8 +440,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo 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 b9443fd73a..27fc53a820 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.Launcher; 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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter= 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 0000000000..f10a695c40 --- /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 c51842d0d2..10708d6004 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.R; 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 @@ abstract class BaseWidgetSheet extends AbstractSlideInView @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 6970833366..961799d57f 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.R; 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 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView 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());