diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2018-03-14 12:30:11 -0700 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2018-03-15 17:29:43 -0700 |
commit | 0b0847b272a37ed115504956be5ce44a96b5784c (patch) | |
tree | 03014c8d93aca5eadcdb1838808a3a035f41619a | |
parent | 39b5534b96a889393b3bafd84dda6333ea8eb157 (diff) | |
download | android_packages_apps_Trebuchet-0b0847b272a37ed115504956be5ce44a96b5784c.tar.gz android_packages_apps_Trebuchet-0b0847b272a37ed115504956be5ce44a96b5784c.tar.bz2 android_packages_apps_Trebuchet-0b0847b272a37ed115504956be5ce44a96b5784c.zip |
Fixing taskMenu and taskView clicks in fallback activity
> Extracting common methods from Launcher & DragLauncher to base classes
> Remoting some dependencies on Launcher and using the base class instead
Change-Id: I121cacf8a14190b4703cda60bdeb4f79eee69ded
21 files changed, 749 insertions, 546 deletions
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml index c41684455..b3fc4ed58 100644 --- a/quickstep/res/layout/fallback_recents_activity.xml +++ b/quickstep/res/layout/fallback_recents_activity.xml @@ -15,15 +15,17 @@ --> <com.android.quickstep.RecentsRootView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/drag_layer" android:layout_width="match_parent" android:layout_height="match_parent"> <com.android.quickstep.FallbackRecentsView xmlns:android="http://schemas.android.com/apk/res/android" - android:theme="@style/HomeScreenElementTheme" + android:id="@+id/overview_panel" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" - android:clipToPadding="false" /> + android:clipToPadding="false" + android:theme="@style/HomeScreenElementTheme" /> </com.android.quickstep.RecentsRootView>
\ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index 9051cfbfc..789185a39 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -36,11 +36,13 @@ public class UiFactory { public static TouchController[] createTouchControllers(Launcher launcher) { if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) { return new TouchController[] { + launcher.getDragController(), new EdgeSwipeController(launcher), new TwoStepSwipeController(launcher), new OverviewSwipeController(launcher)}; } else { return new TouchController[] { + launcher.getDragController(), new TwoStepSwipeController(launcher), new OverviewSwipeController(launcher)}; } diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index 598c34de0..1d443fd2a 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -15,17 +15,25 @@ */ package com.android.quickstep; +import android.app.ActivityOptions; import android.os.Bundle; +import android.view.View; -import com.android.launcher3.BaseActivity; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; +import com.android.launcher3.badge.BadgeInfo; +import com.android.launcher3.views.BaseDragLayer; /** * A simple activity to show the recently launched tasks */ -public class RecentsActivity extends BaseActivity { +public class RecentsActivity extends BaseDraggingActivity { + + private RecentsRootView mRecentsRootView; + private FallbackRecentsView mFallbackRecentsView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -39,5 +47,30 @@ public class RecentsActivity extends BaseActivity { : new InvariantDeviceProfile(this).getDeviceProfile(this)); setContentView(R.layout.fallback_recents_activity); + mRecentsRootView = findViewById(R.id.drag_layer); + mFallbackRecentsView = findViewById(R.id.overview_panel); + } + + @Override + public BaseDragLayer getDragLayer() { + return mRecentsRootView; } + + @Override + public <T extends View> T getOverviewPanel() { + return (T) mFallbackRecentsView; + } + + @Override + public BadgeInfo getBadgeInfoForItem(ItemInfo info) { + return null; + } + + @Override + public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) { + return null; + } + + @Override + public void invalidateParent(ItemInfo info) { } } diff --git a/quickstep/src/com/android/quickstep/RecentsRootView.java b/quickstep/src/com/android/quickstep/RecentsRootView.java index 3c69dbfd6..24785f931 100644 --- a/quickstep/src/com/android/quickstep/RecentsRootView.java +++ b/quickstep/src/com/android/quickstep/RecentsRootView.java @@ -21,17 +21,19 @@ import android.graphics.Rect; import android.util.AttributeSet; import com.android.launcher3.BaseActivity; -import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.R; import com.android.launcher3.util.Themes; +import com.android.launcher3.util.TouchController; +import com.android.launcher3.views.BaseDragLayer; -public class RecentsRootView extends InsettableFrameLayout { +public class RecentsRootView extends BaseDragLayer<RecentsActivity> { private final BaseActivity mActivity; public RecentsRootView(Context context, AttributeSet attrs) { super(context, attrs); mActivity = BaseActivity.fromContext(context); + mControllers = new TouchController[0]; } @TargetApi(23) diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java index eebfb9165..202259530 100644 --- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java +++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java @@ -30,8 +30,8 @@ import android.view.View; import android.view.ViewTreeObserver.OnPreDrawListener; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.popup.SystemShortcut; @@ -70,11 +70,12 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut } @Override - public View.OnClickListener getOnClickListener(Launcher launcher, ItemInfo itemInfo) { + public View.OnClickListener getOnClickListener( + BaseDraggingActivity activity, ItemInfo itemInfo) { return null; } - public View.OnClickListener getOnClickListener(final Launcher launcher, final TaskView view) { + public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) { Task task = view.getTask(); ShortcutInfo dummyInfo = new ShortcutInfo(); @@ -82,14 +83,14 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut ComponentName component = task.getTopComponent(); dummyInfo.intent.setComponent(component); dummyInfo.user = UserHandle.of(task.key.userId); - dummyInfo.title = TaskUtils.getTitle(launcher, task); + dummyInfo.title = TaskUtils.getTitle(activity, task); - return getOnClickListenerForTask(launcher, task, dummyInfo); + return getOnClickListenerForTask(activity, task, dummyInfo); } - protected View.OnClickListener getOnClickListenerForTask(final Launcher launcher, - final Task task, final ItemInfo dummyInfo) { - return mSystemShortcut.getOnClickListener(launcher, dummyInfo); + protected View.OnClickListener getOnClickListenerForTask( + BaseDraggingActivity activity, Task task, ItemInfo dummyInfo) { + return mSystemShortcut.getOnClickListener(activity, dummyInfo); } public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> { @@ -109,8 +110,9 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut } @Override - public View.OnClickListener getOnClickListener(Launcher launcher, TaskView taskView) { - if (launcher.getDeviceProfile().isMultiWindowMode) { + public View.OnClickListener getOnClickListener( + BaseDraggingActivity activity, TaskView taskView) { + if (activity.getDeviceProfile().isMultiWindowMode) { return null; } final Task task = taskView.getTask(); @@ -119,12 +121,12 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut } mTaskView = taskView; return (v -> { - AbstractFloatingView.closeOpenViews(launcher, true, + AbstractFloatingView.closeOpenViews(activity, true, AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); if (ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key.id, ActivityOptionsCompat.makeSplitScreenOptions(true))) { - ISystemUiProxy sysUiProxy = RecentsModel.getInstance(launcher).getSystemUiProxy(); + ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy(); try { sysUiProxy.onSplitScreenInvoked(); } catch (RemoteException e) { @@ -134,7 +136,7 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut final Runnable animStartedListener = () -> { mTaskView.getViewTreeObserver().addOnPreDrawListener(SplitScreen.this); - launcher.<RecentsView>getOverviewPanel().removeView(taskView); + activity.<RecentsView>getOverviewPanel().removeView(taskView); }; final int[] position = new int[2]; @@ -178,8 +180,9 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut } @Override - public View.OnClickListener getOnClickListener(Launcher launcher, TaskView taskView) { - ISystemUiProxy sysUiProxy = RecentsModel.getInstance(launcher).getSystemUiProxy(); + public View.OnClickListener getOnClickListener( + BaseDraggingActivity activity, TaskView taskView) { + ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy(); if (sysUiProxy == null) { return null; } @@ -211,11 +214,11 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut } @Override - protected View.OnClickListener getOnClickListenerForTask(Launcher launcher, Task task, - ItemInfo itemInfo) { - if (InstantAppResolver.newInstance(launcher).isInstantApp(launcher, + protected View.OnClickListener getOnClickListenerForTask( + BaseDraggingActivity activity, Task task, ItemInfo itemInfo) { + if (InstantAppResolver.newInstance(activity).isInstantApp(activity, task.getTopComponent().getPackageName())) { - return mSystemShortcut.createOnClickListener(launcher, itemInfo); + return mSystemShortcut.createOnClickListener(activity, itemInfo); } return null; } diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java index b31d42fb6..2df951b86 100644 --- a/quickstep/src/com/android/quickstep/TaskUtils.java +++ b/quickstep/src/com/android/quickstep/TaskUtils.java @@ -16,12 +16,12 @@ package com.android.quickstep; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.UserHandle; import android.util.Log; -import com.android.launcher3.Launcher; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.systemui.shared.recents.model.Task; @@ -34,10 +34,10 @@ public class TaskUtils { private static final String TAG = "TaskUtils"; - public static CharSequence getTitle(Launcher launcher, Task task) { - LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(launcher); - UserManagerCompat userManagerCompat = UserManagerCompat.getInstance(launcher); - PackageManager packageManager = launcher.getPackageManager(); + public static CharSequence getTitle(Context context, Task task) { + LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(context); + UserManagerCompat userManagerCompat = UserManagerCompat.getInstance(context); + PackageManager packageManager = context.getPackageManager(); UserHandle user = UserHandle.of(task.key.userId); ApplicationInfo applicationInfo = launcherAppsCompat.getApplicationInfo( task.getTopComponent().getPackageName(), 0, user); diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java index 94f440dcd..30cbcdf67 100644 --- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java +++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java @@ -32,14 +32,14 @@ import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.TextView; import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.Launcher; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; -import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.shortcuts.DeepShortcutView; +import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.TaskSystemShortcut; import com.android.quickstep.TaskUtils; @@ -60,7 +60,7 @@ public class TaskMenuView extends AbstractFloatingView { private static final long OPEN_CLOSE_DURATION = 220; - private Launcher mLauncher; + private BaseDraggingActivity mActivity; private TextView mTaskIconAndName; private AnimatorSet mOpenCloseAnimator; private TaskView mTaskView; @@ -72,7 +72,7 @@ public class TaskMenuView extends AbstractFloatingView { public TaskMenuView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mLauncher = Launcher.getLauncher(context); + mActivity = BaseDraggingActivity.fromContext(context); setClipToOutline(true); setOutlineProvider(new ViewOutlineProvider() { @Override @@ -92,7 +92,7 @@ public class TaskMenuView extends AbstractFloatingView { @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { - DragLayer dl = mLauncher.getDragLayer(); + BaseDragLayer dl = mActivity.getDragLayer(); if (!dl.isEventOverView(this, ev)) { // TODO: log this once we have a new container type for it? close(true); @@ -122,9 +122,9 @@ public class TaskMenuView extends AbstractFloatingView { } public static boolean showForTask(TaskView taskView) { - Launcher launcher = Launcher.getLauncher(taskView.getContext()); - final TaskMenuView taskMenuView = (TaskMenuView) launcher.getLayoutInflater().inflate( - R.layout.task_menu, launcher.getDragLayer(), false); + BaseDraggingActivity activity = BaseDraggingActivity.fromContext(taskView.getContext()); + final TaskMenuView taskMenuView = (TaskMenuView) activity.getLayoutInflater().inflate( + R.layout.task_menu, activity.getDragLayer(), false); return taskMenuView.populateAndShowForTask(taskView); } @@ -132,7 +132,7 @@ public class TaskMenuView extends AbstractFloatingView { if (isAttachedToWindow()) { return false; } - mLauncher.getDragLayer().addView(this); + mActivity.getDragLayer().addView(this); mTaskView = taskView; addMenuOptions(mTaskView); orientAroundTaskView(mTaskView); @@ -145,11 +145,11 @@ public class TaskMenuView extends AbstractFloatingView { int iconSize = getResources().getDimensionPixelSize(R.dimen.task_thumbnail_icon_size); icon.setBounds(0, 0, iconSize, iconSize); mTaskIconAndName.setCompoundDrawables(null, icon, null, null); - mTaskIconAndName.setText(TaskUtils.getTitle(mLauncher, taskView.getTask())); + mTaskIconAndName.setText(TaskUtils.getTitle(getContext(), taskView.getTask())); mTaskIconAndName.setOnClickListener(v -> close(true)); for (TaskSystemShortcut menuOption : MENU_OPTIONS) { - OnClickListener onClickListener = menuOption.getOnClickListener(mLauncher, taskView); + OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, taskView); if (onClickListener != null) { addMenuOption(menuOption, onClickListener); } @@ -157,7 +157,7 @@ public class TaskMenuView extends AbstractFloatingView { } private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) { - DeepShortcutView menuOptionView = (DeepShortcutView) mLauncher.getLayoutInflater().inflate( + DeepShortcutView menuOptionView = (DeepShortcutView) mActivity.getLayoutInflater().inflate( R.layout.system_shortcut, this, false); menuOptionView.getIconView().setBackgroundResource(menuOption.iconResId); menuOptionView.getBubbleText().setText(menuOption.labelResId); @@ -167,8 +167,8 @@ public class TaskMenuView extends AbstractFloatingView { private void orientAroundTaskView(TaskView taskView) { measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - mLauncher.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect); - Rect insets = mLauncher.getDragLayer().getInsets(); + mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect); + Rect insets = mActivity.getDragLayer().getInsets(); int x = sTempRect.left + (sTempRect.width() - getMeasuredWidth()) / 2 - insets.left; setX(Utilities.isRtl(getResources()) ? -x : x); setY(sTempRect.top - mTaskIconAndName.getPaddingTop() - insets.top); @@ -211,7 +211,7 @@ public class TaskMenuView extends AbstractFloatingView { private void closeComplete() { mIsOpen = false; - mLauncher.getDragLayer().removeView(this); + mActivity.getDragLayer().removeView(this); } private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() { diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 7a575ad25..c20b577e1 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -28,6 +28,7 @@ import android.view.ViewOutlineProvider; import android.widget.FrameLayout; import android.widget.ImageView; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.quickstep.views.RecentsView.PageCallbacks; @@ -110,7 +111,8 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback if (mTask != null) { final ActivityOptions opts; if (animate) { - opts = Launcher.getLauncher(getContext()).getActivityLaunchOptions(this, false); + opts = BaseDraggingActivity.fromContext(getContext()) + .getActivityLaunchOptions(this, false); } else { opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0); } diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index fc5ce8f2b..f34cf0dc5 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -24,9 +24,9 @@ import android.view.MotionEvent; import android.view.View; import android.widget.LinearLayout; -import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.util.TouchController; +import com.android.launcher3.views.BaseDragLayer; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -92,7 +92,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public final void close(boolean animate) { animate &= !Utilities.isPowerSaverOn(getContext()); handleClose(animate); - Launcher.getLauncher(getContext()).getUserEventDispatcher() + BaseActivity.fromContext(getContext()).getUserEventDispatcher() .resetElapsedContainerMillis("container closed"); } @@ -120,8 +120,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch } protected static <T extends AbstractFloatingView> T getOpenView( - Launcher launcher, @FloatingViewType int type) { - DragLayer dragLayer = launcher.getDragLayer(); + BaseDraggingActivity activity, @FloatingViewType int type) { + BaseDragLayer dragLayer = activity.getDragLayer(); // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer, // and will be one of the last views. for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) { @@ -136,16 +136,17 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch return null; } - public static void closeOpenContainer(Launcher launcher, @FloatingViewType int type) { - AbstractFloatingView view = getOpenView(launcher, type); + public static void closeOpenContainer(BaseDraggingActivity activity, + @FloatingViewType int type) { + AbstractFloatingView view = getOpenView(activity, type); if (view != null) { view.close(true); } } - public static void closeOpenViews(Launcher launcher, boolean animate, + public static void closeOpenViews(BaseDraggingActivity activity, boolean animate, @FloatingViewType int type) { - DragLayer dragLayer = launcher.getDragLayer(); + BaseDragLayer dragLayer = activity.getDragLayer(); // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer, // and will be one of the last views. for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) { @@ -159,16 +160,16 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch } } - public static void closeAllOpenViews(Launcher launcher, boolean animate) { - closeOpenViews(launcher, animate, TYPE_ALL); - launcher.finishAutoCancelActionMode(); + public static void closeAllOpenViews(BaseDraggingActivity activity, boolean animate) { + closeOpenViews(activity, animate, TYPE_ALL); + activity.finishAutoCancelActionMode(); } - public static void closeAllOpenViews(Launcher launcher) { - closeAllOpenViews(launcher, true); + public static void closeAllOpenViews(BaseDraggingActivity activity) { + closeAllOpenViews(activity, true); } - public static AbstractFloatingView getTopOpenView(Launcher launcher) { - return getOpenView(launcher, TYPE_ALL); + public static AbstractFloatingView getTopOpenView(BaseDraggingActivity activity) { + return getOpenView(activity, TYPE_ALL); } } diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java new file mode 100644 index 000000000..35edaf466 --- /dev/null +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -0,0 +1,191 @@ +/* + * 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; + +import android.app.ActivityOptions; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.Process; +import android.os.StrictMode; +import android.os.UserHandle; +import android.util.Log; +import android.view.ActionMode; +import android.view.View; +import android.widget.Toast; + +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.badge.BadgeInfo; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.shortcuts.DeepShortcutManager; +import com.android.launcher3.views.BaseDragLayer; + +/** + * Extension of BaseActivity allowing support for drag-n-drop + */ +public abstract class BaseDraggingActivity extends BaseActivity { + + private static final String TAG = "BaseDraggingActivity"; + + // The Intent extra that defines whether to ignore the launch animation + private static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION = + "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION"; + + // When starting an action mode, setting this tag will cause the action mode to be cancelled + // automatically when user interacts with the launcher. + public static final Object AUTO_CANCEL_ACTION_MODE = new Object(); + + private ActionMode mCurrentActionMode; + protected boolean mIsSafeModeEnabled; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mIsSafeModeEnabled = getPackageManager().isSafeMode(); + } + + @Override + public void onActionModeStarted(ActionMode mode) { + super.onActionModeStarted(mode); + mCurrentActionMode = mode; + } + + @Override + public void onActionModeFinished(ActionMode mode) { + super.onActionModeFinished(mode); + mCurrentActionMode = null; + } + + public boolean finishAutoCancelActionMode() { + if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) { + mCurrentActionMode.finish(); + return true; + } + return false; + } + + public abstract BaseDragLayer getDragLayer(); + + public abstract <T extends View> T getOverviewPanel(); + + public abstract BadgeInfo getBadgeInfoForItem(ItemInfo info); + + public abstract void invalidateParent(ItemInfo info); + + public static BaseDraggingActivity fromContext(Context context) { + if (context instanceof BaseDraggingActivity) { + return (BaseDraggingActivity) context; + } + return ((BaseDraggingActivity) ((ContextWrapper) context).getBaseContext()); + } + + public Rect getViewBounds(View v) { + int[] pos = new int[2]; + v.getLocationOnScreen(pos); + return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight()); + } + + public final Bundle getActivityLaunchOptionsAsBundle(View v, boolean useDefaultLaunchOptions) { + ActivityOptions activityOptions = getActivityLaunchOptions(v, useDefaultLaunchOptions); + return activityOptions == null ? null : activityOptions.toBundle(); + } + + public abstract ActivityOptions getActivityLaunchOptions( + View v, boolean useDefaultLaunchOptions); + + public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { + if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { + Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); + return false; + } + + // Only launch using the new animation if the shortcut has not opted out (this is a + // private contract between launcher and may be ignored in the future). + boolean useLaunchAnimation = (v != null) && + !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION); + Bundle optsBundle = useLaunchAnimation + ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat()) + : null; + + UserHandle user = item == null ? null : item.user; + + // Prepare intent + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (v != null) { + intent.setSourceBounds(getViewBounds(v)); + } + try { + boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW + && (item instanceof ShortcutInfo) + && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT + || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) + && !((ShortcutInfo) item).isPromise(); + if (isShortcut) { + // Shortcuts need some special checks due to legacy reasons. + startShortcutIntentSafely(intent, optsBundle, item); + } else if (user == null || user.equals(Process.myUserHandle())) { + // Could be launching some bookkeeping activity + startActivity(intent, optsBundle); + } else { + LauncherAppsCompat.getInstance(this).startActivityForProfile( + intent.getComponent(), user, intent.getSourceBounds(), optsBundle); + } + getUserEventDispatcher().logAppLaunch(v, intent); + return true; + } catch (ActivityNotFoundException|SecurityException e) { + Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); + Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e); + } + return false; + } + + private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) { + try { + StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); + try { + // Temporarily disable deathPenalty on all default checks. For eg, shortcuts + // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure + // is enabled by default on NYC. + StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll() + .penaltyLog().build()); + + if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + String id = ((ShortcutInfo) info).getDeepShortcutId(); + String packageName = intent.getPackage(); + DeepShortcutManager.getInstance(this).startShortcut( + packageName, id, intent.getSourceBounds(), optsBundle, info.user); + } else { + // Could be launching some bookkeeping activity + startActivity(intent, optsBundle); + } + } finally { + StrictMode.setVmPolicy(oldPolicy); + } + } catch (SecurityException e) { + if (!onErrorStartingShortcut(intent, info)) { + throw e; + } + } + } + + protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) { + return false; + } +} diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 8b6d9f825..fc61155d3 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -44,7 +44,6 @@ import com.android.launcher3.Launcher.OnResumeCallback; import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.badge.BadgeRenderer; import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.folder.FolderIconPreviewVerifier; import com.android.launcher3.graphics.DrawableFactory; import com.android.launcher3.graphics.IconPalette; import com.android.launcher3.graphics.PreloadIconDrawable; @@ -65,7 +64,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed}; - private final Launcher mLauncher; + private final BaseDraggingActivity mActivity; private Drawable mIcon; private final boolean mCenterVertically; @@ -133,8 +132,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, public BubbleTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mLauncher = Launcher.getLauncher(context); - DeviceProfile grid = mLauncher.getDeviceProfile(); + mActivity = BaseDraggingActivity.fromContext(context); + DeviceProfile grid = mActivity.getDeviceProfile(); mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); TypedArray a = context.obtainStyledAttributes(attrs, @@ -164,7 +163,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, mLongPressHelper = new CheckLongPressHelper(this); mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); - setAccessibilityDelegate(mLauncher.getAccessibilityDelegate()); + setAccessibilityDelegate(mActivity.getAccessibilityDelegate()); } @@ -493,10 +492,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, public void applyBadgeState(ItemInfo itemInfo, boolean animate) { if (mIcon instanceof FastBitmapDrawable) { boolean wasBadged = mBadgeInfo != null; - mBadgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo); + mBadgeInfo = mActivity.getBadgeInfoForItem(itemInfo); boolean isBadged = mBadgeInfo != null; float newBadgeScale = isBadged ? 1f : 0; - mBadgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer; + mBadgeRenderer = mActivity.getDeviceProfile().mBadgeRenderer; if (wasBadged || isBadged) { // Animate when a badge is first added or when it is removed. if (animate && (wasBadged ^ isBadged) && isShown()) { @@ -572,15 +571,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, applyFromApplicationInfo((AppInfo) info); } else if (info instanceof ShortcutInfo) { applyFromShortcutInfo((ShortcutInfo) info); - FolderIconPreviewVerifier verifier = - new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv); - if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) { - View folderIcon = - mLauncher.getWorkspace().getHomescreenIconByItemId(info.container); - if (folderIcon != null) { - folderIcon.invalidate(); - } - } + mActivity.invalidateParent(info); } else if (info instanceof PackageItemInfo) { applyFromPackageItemInfo((PackageItemInfo) info); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0bbb90c45..a38ce078a 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -25,7 +25,6 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; -import android.Manifest; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -47,7 +46,6 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; -import android.graphics.Rect; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; @@ -60,7 +58,6 @@ import android.text.TextUtils; import android.text.method.TextKeyListener; import android.util.Log; import android.util.SparseArray; -import android.view.ActionMode; import android.view.KeyEvent; import android.view.KeyboardShortcutGroup; import android.view.KeyboardShortcutInfo; @@ -73,14 +70,13 @@ import android.view.animation.OvershootInterpolator; import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.DiscoveryBounce; +import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.compat.AppWidgetManagerCompat; -import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.LauncherAppsCompatVO; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; @@ -88,6 +84,7 @@ import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.dynamicui.WallpaperColorInfo; import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.folder.FolderIconPreviewVerifier; import com.android.launcher3.keyboard.CustomActionsPopup; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.logging.FileLog; @@ -139,7 +136,7 @@ import java.util.Set; /** * Default launcher application. */ -public class Launcher extends BaseActivity implements LauncherExterns, LauncherModel.Callbacks, +public class Launcher extends BaseDraggingActivity implements LauncherExterns, LauncherModel.Callbacks, LauncherProviderChangeListener, WallpaperColorInfo.OnThemeChangeListener { public static final String TAG = "Launcher"; static final boolean LOGD = false; @@ -166,10 +163,6 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM */ protected static final int REQUEST_LAST = 100; - // The Intent extra that defines whether to ignore the launch animation - static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION = - "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION"; - // Type: int private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen"; // Type: int @@ -181,14 +174,8 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM // Type: SparseArray<Parcelable> private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel"; - // When starting an action mode, setting this tag will cause the action mode to be cancelled - // automatically when user interacts with the launcher. - public static final Object AUTO_CANCEL_ACTION_MODE = new Object(); - private LauncherStateManager mStateManager; - private boolean mIsSafeModeEnabled; - private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; // How long to wait before the new-shortcut animation automatically pans the workspace @@ -255,7 +242,6 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM private boolean mAppLaunchSuccess; private RotationHelper mRotationHelper; - private ActionMode mCurrentActionMode; @Override protected void onCreate(Bundle savedInstanceState) { @@ -288,7 +274,6 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM initDeviceProfile(app.getInvariantDeviceProfile()); mSharedPrefs = Utilities.getPrefs(this); - mIsSafeModeEnabled = getPackageManager().isSafeMode(); mIconCache = app.getIconCache(); mAccessibilityDelegate = new LauncherAccessibilityDelegate(this); @@ -482,6 +467,22 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM return mPopupDataProvider; } + @Override + public BadgeInfo getBadgeInfoForItem(ItemInfo info) { + return mPopupDataProvider.getBadgeInfoForItem(info); + } + + @Override + public void invalidateParent(ItemInfo info) { + FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(getDeviceProfile().inv); + if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) { + View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container); + if (folderIcon != null) { + folderIcon.invalidate(); + } + } + } + /** * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have * a configuration step, this allows the proper animations to run after other transitions. @@ -941,7 +942,7 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); // Setup the drag layer - mDragLayer.setup(this, mDragController); + mDragLayer.setup(mDragController); mWorkspace.setup(mDragController); // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the @@ -1180,6 +1181,7 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM return (LauncherRootView) mLauncherView; } + @Override public DragLayer getDragLayer() { return mDragLayer; } @@ -1672,119 +1674,45 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM } } - private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) { - try { - StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); - try { - // Temporarily disable deathPenalty on all default checks. For eg, shortcuts - // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure - // is enabled by default on NYC. - StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll() - .penaltyLog().build()); - - if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - String id = ((ShortcutInfo) info).getDeepShortcutId(); - String packageName = intent.getPackage(); - DeepShortcutManager.getInstance(this).startShortcut( - packageName, id, intent.getSourceBounds(), optsBundle, info.user); - } else { - // Could be launching some bookkeeping activity - startActivity(intent, optsBundle); - } - } finally { - StrictMode.setVmPolicy(oldPolicy); - } - } catch (SecurityException e) { - // Due to legacy reasons, direct call shortcuts require Launchers to have the - // corresponding permission. Show the appropriate permission prompt if that - // is the case. - if (intent.getComponent() == null - && Intent.ACTION_CALL.equals(intent.getAction()) - && checkSelfPermission(Manifest.permission.CALL_PHONE) != - PackageManager.PERMISSION_GRANTED) { - - setWaitingForResult(PendingRequestArgs - .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info)); - requestPermissions(new String[]{Manifest.permission.CALL_PHONE}, - REQUEST_PERMISSION_CALL_PHONE); - } else { - // No idea why this was thrown. - throw e; - } - } - } - - public Bundle getActivityLaunchOptionsAsBundle(View v, boolean useDefaultLaunchOptions) { - ActivityOptions activityOptions = getActivityLaunchOptions(v, useDefaultLaunchOptions); - return activityOptions == null ? null : activityOptions.toBundle(); - } - @TargetApi(Build.VERSION_CODES.M) + @Override public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) { return useDefaultLaunchOptions ? mAppTransitionManager.getDefaultActivityLaunchOptions(this, v) : mAppTransitionManager.getActivityLaunchOptions(this, v); } - public Rect getViewBounds(View v) { - int[] pos = new int[2]; - v.getLocationOnScreen(pos); - return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight()); + @TargetApi(Build.VERSION_CODES.M) + @Override + protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) { + // Due to legacy reasons, direct call shortcuts require Launchers to have the + // corresponding permission. Show the appropriate permission prompt if that + // is the case. + if (intent.getComponent() == null + && Intent.ACTION_CALL.equals(intent.getAction()) + && checkSelfPermission(android.Manifest.permission.CALL_PHONE) != + PackageManager.PERMISSION_GRANTED) { + + setWaitingForResult(PendingRequestArgs + .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info)); + requestPermissions(new String[]{android.Manifest.permission.CALL_PHONE}, + REQUEST_PERMISSION_CALL_PHONE); + return true; + } else { + return false; + } } public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { - mAppLaunchSuccess = false; - if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { - Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); - return mAppLaunchSuccess; - } - - // Only launch using the new animation if the shortcut has not opted out (this is a - // private contract between launcher and may be ignored in the future). - boolean useLaunchAnimation = (v != null) && - !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION); - Bundle optsBundle = useLaunchAnimation - ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat()) - : null; - - UserHandle user = item == null ? null : item.user; - - // Prepare intent - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - if (v != null) { - intent.setSourceBounds(getViewBounds(v)); - } - try { - boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW - && (item instanceof ShortcutInfo) - && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT - || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) - && !((ShortcutInfo) item).isPromise(); - if (isShortcut) { - // Shortcuts need some special checks due to legacy reasons. - startShortcutIntentSafely(intent, optsBundle, item); - } else if (user == null || user.equals(Process.myUserHandle())) { - // Could be launching some bookkeeping activity - startActivity(intent, optsBundle); - } else { - LauncherAppsCompat.getInstance(this).startActivityForProfile( - intent.getComponent(), user, intent.getSourceBounds(), optsBundle); - } - - if (v instanceof BubbleTextView) { - // This is set to the view that launched the activity that navigated the user away - // from launcher. Since there is no callback for when the activity has finished - // launching, enable the press state and keep this reference to reset the press - // state when we return to launcher. - BubbleTextView btv = (BubbleTextView) v; - btv.setStayPressed(true); - setOnResumeCallback(btv); - } - mAppLaunchSuccess = true; - getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115 - } catch (ActivityNotFoundException|SecurityException e) { - Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); - Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e); + mAppLaunchSuccess = super.startActivitySafely(v, intent, item); + if (mAppLaunchSuccess && v instanceof BubbleTextView) { + // This is set to the view that launched the activity that navigated the user away + // from launcher. Since there is no callback for when the activity has finished + // launching, enable the press state and keep this reference to reset the press + // state when we return to launcher. + BubbleTextView btv = (BubbleTextView) v; + btv.setStayPressed(true); + setOnResumeCallback(btv); } return mAppLaunchSuccess; } @@ -2515,26 +2443,6 @@ public class Launcher extends BaseActivity implements LauncherExterns, LauncherM return ((Launcher) ((ContextWrapper) context).getBaseContext()); } - @Override - public void onActionModeStarted(ActionMode mode) { - super.onActionModeStarted(mode); - mCurrentActionMode = mode; - } - - @Override - public void onActionModeFinished(ActionMode mode) { - super.onActionModeFinished(mode); - mCurrentActionMode = null; - } - - public boolean finishAutoCancelActionMode() { - if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) { - mCurrentActionMode.finish(); - return true; - } - return false; - } - /** * Callback for listening for onResume */ diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 996095392..68ad25370 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3254,8 +3254,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator> && v instanceof FolderIcon) { FolderBadgeInfo folderBadgeInfo = new FolderBadgeInfo(); for (ShortcutInfo si : ((FolderInfo) info).contents) { - folderBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider() - .getBadgeInfoForItem(si)); + folderBadgeInfo.addBadgeInfo(mLauncher.getBadgeInfoForItem(si)); } ((FolderIcon) v).setBadgeInfo(folderBadgeInfo); } diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index f5d0b2492..8519365e7 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -31,21 +31,17 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.Interpolator; -import android.widget.FrameLayout; import android.widget.TextView; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.CellLayout; import com.android.launcher3.DropTargetBar; -import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; -import com.android.launcher3.Utilities; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; @@ -53,24 +49,20 @@ import com.android.launcher3.graphics.ViewScrim; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.util.Thunk; -import com.android.launcher3.util.TouchController; +import com.android.launcher3.views.BaseDragLayer; import java.util.ArrayList; /** * A ViewGroup that coordinates dragging across its descendants */ -public class DragLayer extends InsettableFrameLayout { +public class DragLayer extends BaseDragLayer<Launcher> { public static final int ANIMATION_END_DISAPPEAR = 0; public static final int ANIMATION_END_REMAIN_VISIBLE = 2; - private final int[] mTmpXY = new int[2]; - @Thunk DragController mDragController; - private Launcher mLauncher; - // Variables relating to animation of views after drop private ValueAnimator mDropAnim = null; private final TimeInterpolator mCubicEaseOutInterpolator = Interpolators.DEACCEL_1_5; @@ -79,9 +71,6 @@ public class DragLayer extends InsettableFrameLayout { @Thunk View mAnchorView = null; private boolean mHoverPointClosesFolder = false; - private final Rect mHitRect = new Rect(); - - private TouchCompleteListener mTouchCompleteListener; private int mTopViewIndex; private int mChildCountOnLastUpdate = -1; @@ -89,8 +78,6 @@ public class DragLayer extends InsettableFrameLayout { // Related to adjacent page hints private final ViewGroupFocusHelper mFocusIndicatorHelper; - protected TouchController[] mControllers; - private TouchController mActiveController; /** * Used to create a new DragLayer from XML. * @@ -107,10 +94,9 @@ public class DragLayer extends InsettableFrameLayout { mFocusIndicatorHelper = new ViewGroupFocusHelper(this); } - public void setup(Launcher launcher, DragController dragController) { - mLauncher = launcher; + public void setup(DragController dragController) { mDragController = dragController; - mControllers = UiFactory.createTouchControllers(mLauncher); + mControllers = UiFactory.createTouchControllers(mActivity); } public ViewGroupFocusHelper getFocusIndicatorHelper() { @@ -123,7 +109,7 @@ public class DragLayer extends InsettableFrameLayout { } public boolean isEventOverHotseat(MotionEvent ev) { - return isEventOverView(mLauncher.getHotseat(), ev); + return isEventOverView(mActivity.getHotseat(), ev); } private boolean isEventOverFolder(Folder folder, MotionEvent ev) { @@ -131,12 +117,7 @@ public class DragLayer extends InsettableFrameLayout { } private boolean isEventOverDropTargetBar(MotionEvent ev) { - return isEventOverView(mLauncher.getDropTargetBar(), ev); - } - - public boolean isEventOverView(View view, MotionEvent ev) { - getDescendantRectRelativeToSelf(view, mHitRect); - return mHitRect.contains((int) ev.getX(), (int) ev.getY()); + return isEventOverView(mActivity.getDropTargetBar(), ev); } @Override @@ -149,66 +130,33 @@ public class DragLayer extends InsettableFrameLayout { } @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - int action = ev.getAction(); - - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - if (mTouchCompleteListener != null) { - mTouchCompleteListener.onTouchComplete(); - } - mTouchCompleteListener = null; - } else if (action == MotionEvent.ACTION_DOWN) { - mLauncher.finishAutoCancelActionMode(); - } - return findActiveController(ev); - } - - private boolean findActiveController(MotionEvent ev) { - mActiveController = null; - - AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher); - if (topView != null && topView.onControllerInterceptTouchEvent(ev)) { - mActiveController = topView; - return true; - } - - if (mLauncher.getStateManager().getState().disableInteraction) { + protected boolean findActiveController(MotionEvent ev) { + if (mActivity.getStateManager().getState().disableInteraction) { // You Shall Not Pass!!! + mActiveController = null; return true; } - - if (mDragController.onControllerInterceptTouchEvent(ev)) { - mActiveController = mDragController; - return true; - } - - for (TouchController controller : mControllers) { - if (controller.onControllerInterceptTouchEvent(ev)) { - mActiveController = controller; - return true; - } - } - return false; + return super.findActiveController(ev); } @Override public boolean onInterceptHoverEvent(MotionEvent ev) { - if (mLauncher == null || mLauncher.getWorkspace() == null) { + if (mActivity == null || mActivity.getWorkspace() == null) { return false; } - Folder currentFolder = Folder.getOpen(mLauncher); + Folder currentFolder = Folder.getOpen(mActivity); if (currentFolder == null) { return false; } else { - AccessibilityManager accessibilityManager = (AccessibilityManager) - getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); + AccessibilityManager accessibilityManager = (AccessibilityManager) + getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); if (accessibilityManager.isTouchExplorationEnabled()) { final int action = ev.getAction(); boolean isOverFolderOrSearchBar; switch (action) { case MotionEvent.ACTION_HOVER_ENTER: isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) || - (isInAccessibleDrag() && isEventOverDropTargetBar(ev)); + (isInAccessibleDrag() && isEventOverDropTargetBar(ev)); if (!isOverFolderOrSearchBar) { sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); mHoverPointClosesFolder = true; @@ -218,7 +166,7 @@ public class DragLayer extends InsettableFrameLayout { break; case MotionEvent.ACTION_HOVER_MOVE: isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) || - (isInAccessibleDrag() && isEventOverDropTargetBar(ev)); + (isInAccessibleDrag() && isEventOverDropTargetBar(ev)); if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) { sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); mHoverPointClosesFolder = true; @@ -239,14 +187,22 @@ public class DragLayer extends InsettableFrameLayout { this, AccessibilityEvent.TYPE_VIEW_FOCUSED, getContext().getString(stringId)); } + @Override + public boolean onHoverEvent(MotionEvent ev) { + // If we've received this, we've already done the necessary handling + // in onInterceptHoverEvent. Return true to consume the event. + return false; + } + + private boolean isInAccessibleDrag() { - return mLauncher.getAccessibilityDelegate().isInAccessibleDrag(); + return mActivity.getAccessibilityDelegate().isInAccessibleDrag(); } @Override public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { // Shortcuts can appear above folder - View topView = AbstractFloatingView.getTopOpenView(mLauncher); + View topView = AbstractFloatingView.getTopOpenView(mActivity); if (topView != null) { if (child == topView) { return super.onRequestSendAccessibilityEvent(child, event); @@ -263,13 +219,13 @@ public class DragLayer extends InsettableFrameLayout { @Override public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) { - View topView = AbstractFloatingView.getTopOpenView(mLauncher); + View topView = AbstractFloatingView.getTopOpenView(mActivity); if (topView != null) { // Only add the top view as a child for accessibility when it is open childrenForAccessibility.add(topView); if (isInAccessibleDrag()) { - childrenForAccessibility.add(mLauncher.getDropTargetBar()); + childrenForAccessibility.add(mActivity.getDropTargetBar()); } } else { super.addChildrenForAccessibility(childrenForAccessibility); @@ -277,103 +233,9 @@ public class DragLayer extends InsettableFrameLayout { } @Override - public boolean onHoverEvent(MotionEvent ev) { - // If we've received this, we've already done the necessary handling - // in onInterceptHoverEvent. Return true to consume the event. - return false; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - int action = ev.getAction(); - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - if (mTouchCompleteListener != null) { - mTouchCompleteListener.onTouchComplete(); - } - mTouchCompleteListener = null; - } - - if (mActiveController != null) { - return mActiveController.onControllerTouchEvent(ev); - } else { - // In case no child view handled the touch event, we may not get onIntercept anymore - return findActiveController(ev); - } - } - - /** - * Determine the rect of the descendant in this DragLayer's coordinates - * - * @param descendant The descendant whose coordinates we want to find. - * @param r The rect into which to place the results. - * @return The factor by which this descendant is scaled relative to this DragLayer. - */ - public float getDescendantRectRelativeToSelf(View descendant, Rect r) { - mTmpXY[0] = 0; - mTmpXY[1] = 0; - float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY); - - r.set(mTmpXY[0], mTmpXY[1], - (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()), - (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight())); - return scale; - } - - public float getLocationInDragLayer(View child, int[] loc) { - loc[0] = 0; - loc[1] = 0; - return getDescendantCoordRelativeToSelf(child, loc); - } - - public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) { - return getDescendantCoordRelativeToSelf(descendant, coord, false); - } - - /** - * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's - * coordinates. - * - * @param descendant The descendant to which the passed coordinate is relative. - * @param coord The coordinate that we want mapped. - * @param includeRootScroll Whether or not to account for the scroll of the root descendant: - * sometimes this is relevant as in a child's coordinates within the root descendant. - * @return The factor by which this descendant is scaled relative to this DragLayer. Caution - * this scale factor is assumed to be equal in X and Y, and so if at any point this - * assumption fails, we will need to return a pair of scale factors. - */ - public float getDescendantCoordRelativeToSelf(View descendant, int[] coord, - boolean includeRootScroll) { - return Utilities.getDescendantCoordRelativeToAncestor(descendant, this, - coord, includeRootScroll); - } - - /** - * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}. - */ - public void mapCoordInSelfToDescendant(View descendant, int[] coord) { - Utilities.mapCoordInSelfToDescendant(descendant, this, coord); - } - - public void getViewRectRelativeToSelf(View v, Rect r) { - int[] loc = new int[2]; - getLocationInWindow(loc); - int x = loc[0]; - int y = loc[1]; - - v.getLocationInWindow(loc); - int vX = loc[0]; - int vY = loc[1]; - - int left = vX - x; - int top = vY - y; - r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight()); - } - - @Override public boolean dispatchUnhandledMove(View focused, int direction) { - // Consume the unhandled move if a container is open, to avoid switching pages underneath. - boolean isContainerOpen = AbstractFloatingView.getTopOpenView(mLauncher) != null; - return isContainerOpen || mDragController.dispatchUnhandledMove(focused, direction); + return super.dispatchUnhandledMove(focused, direction) + || mDragController.dispatchUnhandledMove(focused, direction); } @Override @@ -386,91 +248,6 @@ public class DragLayer extends InsettableFrameLayout { } } - @Override - public LayoutParams generateLayoutParams(AttributeSet attrs) { - return new LayoutParams(getContext(), attrs); - } - - @Override - protected LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - } - - // Override to allow type-checking of LayoutParams. - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams; - } - - @Override - protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return new LayoutParams(p); - } - - public static class LayoutParams extends InsettableFrameLayout.LayoutParams { - public int x, y; - public boolean customPosition = false; - - public LayoutParams(Context c, AttributeSet attrs) { - super(c, attrs); - } - - public LayoutParams(int width, int height) { - super(width, height); - } - - public LayoutParams(ViewGroup.LayoutParams lp) { - super(lp); - } - - public void setWidth(int width) { - this.width = width; - } - - public int getWidth() { - return width; - } - - public void setHeight(int height) { - this.height = height; - } - - public int getHeight() { - return height; - } - - public void setX(int x) { - this.x = x; - } - - public int getX() { - return x; - } - - public void setY(int y) { - this.y = y; - } - - public int getY() { - return y; - } - } - - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - int count = getChildCount(); - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams(); - if (flp instanceof LayoutParams) { - final LayoutParams lp = (LayoutParams) flp; - if (lp.customPosition) { - child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height); - } - } - } - } - public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha, float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable, int duration) { @@ -709,14 +486,14 @@ public class DragLayer extends InsettableFrameLayout { public void onViewAdded(View child) { super.onViewAdded(child); updateChildIndices(); - UiFactory.onLauncherStateOrFocusChanged(mLauncher); + UiFactory.onLauncherStateOrFocusChanged(mActivity); } @Override public void onViewRemoved(View child) { super.onViewRemoved(child); updateChildIndices(); - UiFactory.onLauncherStateOrFocusChanged(mLauncher); + UiFactory.onLauncherStateOrFocusChanged(mActivity); } @Override @@ -768,32 +545,4 @@ public class DragLayer extends InsettableFrameLayout { mFocusIndicatorHelper.draw(canvas); super.dispatchDraw(canvas); } - - @Override - protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { - View topView = AbstractFloatingView.getTopOpenView(mLauncher); - if (topView != null) { - return topView.requestFocus(direction, previouslyFocusedRect); - } else { - return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); - } - } - - @Override - public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { - View topView = AbstractFloatingView.getTopOpenView(mLauncher); - if (topView != null) { - topView.addFocusables(views, direction); - } else { - super.addFocusables(views, direction, focusableMode); - } - } - - public void setTouchCompleteListener(TouchCompleteListener listener) { - mTouchCompleteListener = listener; - } - - public interface TouchCompleteListener { - void onTouchComplete(); - } } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 6c94273f6..cb5d872ab 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -568,7 +568,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Override public void onAdd(ShortcutInfo item, int rank) { boolean wasBadged = mBadgeInfo.hasBadge(); - mBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item)); + mBadgeInfo.addBadgeInfo(mLauncher.getBadgeInfoForItem(item)); boolean isBadged = mBadgeInfo.hasBadge(); updateBadgeScale(wasBadged, isBadged); invalidate(); @@ -578,7 +578,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Override public void onRemove(ShortcutInfo item) { boolean wasBadged = mBadgeInfo.hasBadge(); - mBadgeInfo.subtractBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item)); + mBadgeInfo.subtractBadgeInfo(mLauncher.getBadgeInfoForItem(item)); boolean isBadged = mBadgeInfo.hasBadge(); updateBadgeScale(wasBadged, isBadged); invalidate(); diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index e427a81a1..033fdf888 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -745,7 +745,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra private void updateNotificationHeader() { ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag(); - BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo); + BadgeInfo badgeInfo = mLauncher.getBadgeInfoForItem(itemInfo); if (mNotificationItemView != null && badgeInfo != null) { mNotificationItemView.updateHeader( badgeInfo.getNotificationCount(), itemInfo.iconColor); diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index 2cc8dfa76..a20149e1c 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -9,6 +9,7 @@ import android.os.Bundle; import android.view.View; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.R; @@ -27,7 +28,7 @@ import java.util.List; * * Example system shortcuts, defined as inner classes, include Widgets and AppInfo. */ -public abstract class SystemShortcut extends ItemInfo { +public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo { public final int iconResId; public final int labelResId; @@ -36,10 +37,9 @@ public abstract class SystemShortcut extends ItemInfo { this.labelResId = labelResId; } - public abstract View.OnClickListener getOnClickListener(final Launcher launcher, - final ItemInfo itemInfo); + public abstract View.OnClickListener getOnClickListener(T activity, ItemInfo itemInfo); - public static class Widgets extends SystemShortcut { + public static class Widgets extends SystemShortcut<Launcher> { public Widgets() { super(R.drawable.ic_widget, R.string.widget_button_text); @@ -54,17 +54,14 @@ public abstract class SystemShortcut extends ItemInfo { if (widgets == null) { return null; } - return new View.OnClickListener() { - @Override - public void onClick(View view) { - AbstractFloatingView.closeAllOpenViews(launcher); - WidgetsBottomSheet widgetsBottomSheet = - (WidgetsBottomSheet) launcher.getLayoutInflater().inflate( - R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false); - widgetsBottomSheet.populateAndShow(itemInfo); - launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, - ControlType.WIDGETS_BUTTON, view); - } + return (view) -> { + AbstractFloatingView.closeAllOpenViews(launcher); + WidgetsBottomSheet widgetsBottomSheet = + (WidgetsBottomSheet) launcher.getLayoutInflater().inflate( + R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false); + widgetsBottomSheet.populateAndShow(itemInfo); + launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, + ControlType.WIDGETS_BUTTON, view); }; } } @@ -75,18 +72,15 @@ public abstract class SystemShortcut extends ItemInfo { } @Override - public View.OnClickListener getOnClickListener(final Launcher launcher, - final ItemInfo itemInfo) { - return new View.OnClickListener() { - @Override - public void onClick(View view) { - Rect sourceBounds = launcher.getViewBounds(view); - Bundle opts = launcher.getActivityLaunchOptionsAsBundle(view, false); - new PackageManagerHelper(launcher).startDetailsActivityForInfo( - itemInfo, sourceBounds, opts); - launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, - ControlType.APPINFO_TARGET, view); - } + public View.OnClickListener getOnClickListener( + BaseDraggingActivity activity, ItemInfo itemInfo) { + return (view) -> { + Rect sourceBounds = activity.getViewBounds(view); + Bundle opts = activity.getActivityLaunchOptionsAsBundle(view, false); + new PackageManagerHelper(activity).startDetailsActivityForInfo( + itemInfo, sourceBounds, opts); + activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, + ControlType.APPINFO_TARGET, view); }; } } @@ -97,28 +91,29 @@ public abstract class SystemShortcut extends ItemInfo { } @Override - public View.OnClickListener getOnClickListener(final Launcher launcher, - final ItemInfo itemInfo) { + public View.OnClickListener getOnClickListener( + BaseDraggingActivity activity, ItemInfo itemInfo) { boolean supportsWebUI = (itemInfo instanceof ShortcutInfo) && ((ShortcutInfo) itemInfo).hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI); boolean isInstantApp = false; if (itemInfo instanceof com.android.launcher3.AppInfo) { com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo; - isInstantApp = InstantAppResolver.newInstance(launcher).isInstantApp(appInfo); + isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo); } boolean enabled = supportsWebUI || isInstantApp; if (!enabled) { return null; } - return createOnClickListener(launcher, itemInfo); + return createOnClickListener(activity, itemInfo); } - public View.OnClickListener createOnClickListener(Launcher launcher, ItemInfo itemInfo) { + public View.OnClickListener createOnClickListener( + BaseDraggingActivity activity, ItemInfo itemInfo) { return view -> { Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent( itemInfo.getTargetComponent().getPackageName()); - launcher.startActivitySafely(view, intent, itemInfo); - AbstractFloatingView.closeAllOpenViews(launcher); + activity.startActivitySafely(view, intent, itemInfo); + AbstractFloatingView.closeAllOpenViews(activity); }; } } diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java index 601a5ab80..4485427f5 100644 --- a/src/com/android/launcher3/util/InstantAppResolver.java +++ b/src/com/android/launcher3/util/InstantAppResolver.java @@ -22,7 +22,6 @@ import android.content.pm.PackageManager; import android.util.Log; import com.android.launcher3.AppInfo; -import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -47,8 +46,8 @@ public class InstantAppResolver { return false; } - public boolean isInstantApp(Launcher launcher, String packageName) { - PackageManager packageManager = launcher.getPackageManager(); + public boolean isInstantApp(Context context, String packageName) { + PackageManager packageManager = context.getPackageManager(); try { return isInstantApp(packageManager.getPackageInfo(packageName, 0).applicationInfo); } catch (PackageManager.NameNotFoundException e) { diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java new file mode 100644 index 000000000..489e59e75 --- /dev/null +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -0,0 +1,325 @@ +/* + * 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.views; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.widget.FrameLayout; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BaseActivity; +import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.InsettableFrameLayout; +import com.android.launcher3.Utilities; +import com.android.launcher3.util.TouchController; + +import java.util.ArrayList; + +/** + * A viewgroup with utility methods for drag-n-drop and touch interception + */ +public abstract class BaseDragLayer<T extends BaseDraggingActivity> extends InsettableFrameLayout { + + protected final int[] mTmpXY = new int[2]; + protected final Rect mHitRect = new Rect(); + + protected final T mActivity; + + protected TouchController[] mControllers; + protected TouchController mActiveController; + private TouchCompleteListener mTouchCompleteListener; + + public BaseDragLayer(Context context, AttributeSet attrs) { + super(context, attrs); + mActivity = (T) BaseActivity.fromContext(context); + } + + + public boolean isEventOverView(View view, MotionEvent ev) { + getDescendantRectRelativeToSelf(view, mHitRect); + return mHitRect.contains((int) ev.getX(), (int) ev.getY()); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + int action = ev.getAction(); + + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + if (mTouchCompleteListener != null) { + mTouchCompleteListener.onTouchComplete(); + } + mTouchCompleteListener = null; + } else if (action == MotionEvent.ACTION_DOWN) { + mActivity.finishAutoCancelActionMode(); + } + return findActiveController(ev); + } + + protected boolean findActiveController(MotionEvent ev) { + mActiveController = null; + + AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null && topView.onControllerInterceptTouchEvent(ev)) { + mActiveController = topView; + return true; + } + + for (TouchController controller : mControllers) { + if (controller.onControllerInterceptTouchEvent(ev)) { + mActiveController = controller; + return true; + } + } + return false; + } + + @Override + public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + // Shortcuts can appear above folder + View topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null) { + if (child == topView) { + return super.onRequestSendAccessibilityEvent(child, event); + } + // Skip propagating onRequestSendAccessibilityEvent for all other children + // which are not topView + return false; + } + return super.onRequestSendAccessibilityEvent(child, event); + } + + @Override + public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) { + View topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null) { + // Only add the top view as a child for accessibility when it is open + childrenForAccessibility.add(topView); + } else { + super.addChildrenForAccessibility(childrenForAccessibility); + } + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + int action = ev.getAction(); + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + if (mTouchCompleteListener != null) { + mTouchCompleteListener.onTouchComplete(); + } + mTouchCompleteListener = null; + } + + if (mActiveController != null) { + return mActiveController.onControllerTouchEvent(ev); + } else { + // In case no child view handled the touch event, we may not get onIntercept anymore + return findActiveController(ev); + } + } + + /** + * Determine the rect of the descendant in this DragLayer's coordinates + * + * @param descendant The descendant whose coordinates we want to find. + * @param r The rect into which to place the results. + * @return The factor by which this descendant is scaled relative to this DragLayer. + */ + public float getDescendantRectRelativeToSelf(View descendant, Rect r) { + mTmpXY[0] = 0; + mTmpXY[1] = 0; + float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY); + + r.set(mTmpXY[0], mTmpXY[1], + (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()), + (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight())); + return scale; + } + + public float getLocationInDragLayer(View child, int[] loc) { + loc[0] = 0; + loc[1] = 0; + return getDescendantCoordRelativeToSelf(child, loc); + } + + public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) { + return getDescendantCoordRelativeToSelf(descendant, coord, false); + } + + /** + * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's + * coordinates. + * + * @param descendant The descendant to which the passed coordinate is relative. + * @param coord The coordinate that we want mapped. + * @param includeRootScroll Whether or not to account for the scroll of the root descendant: + * sometimes this is relevant as in a child's coordinates within the root descendant. + * @return The factor by which this descendant is scaled relative to this DragLayer. Caution + * this scale factor is assumed to be equal in X and Y, and so if at any point this + * assumption fails, we will need to return a pair of scale factors. + */ + public float getDescendantCoordRelativeToSelf(View descendant, int[] coord, + boolean includeRootScroll) { + return Utilities.getDescendantCoordRelativeToAncestor(descendant, this, + coord, includeRootScroll); + } + + /** + * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}. + */ + public void mapCoordInSelfToDescendant(View descendant, int[] coord) { + Utilities.mapCoordInSelfToDescendant(descendant, this, coord); + } + + public void getViewRectRelativeToSelf(View v, Rect r) { + int[] loc = new int[2]; + getLocationInWindow(loc); + int x = loc[0]; + int y = loc[1]; + + v.getLocationInWindow(loc); + int vX = loc[0]; + int vY = loc[1]; + + int left = vX - x; + int top = vY - y; + r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight()); + } + + @Override + public boolean dispatchUnhandledMove(View focused, int direction) { + // Consume the unhandled move if a container is open, to avoid switching pages underneath. + return AbstractFloatingView.getTopOpenView(mActivity) != null; + } + + @Override + protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { + View topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null) { + return topView.requestFocus(direction, previouslyFocusedRect); + } else { + return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); + } + } + + @Override + public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { + View topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null) { + topView.addFocusables(views, direction); + } else { + super.addFocusables(views, direction, focusableMode); + } + } + + public void setTouchCompleteListener(TouchCompleteListener listener) { + mTouchCompleteListener = listener; + } + + public interface TouchCompleteListener { + void onTouchComplete(); + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + // Override to allow type-checking of LayoutParams. + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams; + } + + @Override + protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p); + } + + public static class LayoutParams extends InsettableFrameLayout.LayoutParams { + public int x, y; + public boolean customPosition = false; + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + public LayoutParams(int width, int height) { + super(width, height); + } + + public LayoutParams(ViewGroup.LayoutParams lp) { + super(lp); + } + + public void setWidth(int width) { + this.width = width; + } + + public int getWidth() { + return width; + } + + public void setHeight(int height) { + this.height = height; + } + + public int getHeight() { + return height; + } + + public void setX(int x) { + this.x = x; + } + + public int getX() { + return x; + } + + public void setY(int y) { + this.y = y; + } + + public int getY() { + return y; + } + } + + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams(); + if (flp instanceof LayoutParams) { + final LayoutParams lp = (LayoutParams) flp; + if (lp.customPosition) { + child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height); + } + } + } + } +} diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java index 9d74218e0..12859c78f 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java @@ -47,7 +47,7 @@ import com.android.launcher3.SimpleOnStylusPressListener; import com.android.launcher3.StylusEventHelper; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragLayer; -import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener; +import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener; import java.util.ArrayList; diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index a9694a74c..1227dfebf 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java @@ -25,7 +25,8 @@ import com.android.launcher3.util.TouchController; public class UiFactory { public static TouchController[] createTouchControllers(Launcher launcher) { - return new TouchController[] {new AllAppsSwipeController(launcher)}; + return new TouchController[] { + launcher.getDragController(), new AllAppsSwipeController(launcher)}; } public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() { |