Merge "Removing static instances of UserManagerCompat and AppWidgetManager" into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index 5def65f..beb13c5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -94,7 +94,7 @@
LOCAL_MIN_SDK_VERSION := 21
LOCAL_PACKAGE_NAME := Launcher3
LOCAL_PRIVILEGED_MODULE := true
-LOCAL_PRODUCT_MODULE := true
+LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_OVERRIDES_PACKAGES := Home Launcher2
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
@@ -125,7 +125,7 @@
LOCAL_MIN_SDK_VERSION := 21
LOCAL_PACKAGE_NAME := Launcher3Go
LOCAL_PRIVILEGED_MODULE := true
-LOCAL_PRODUCT_MODULE := true
+LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
@@ -191,7 +191,7 @@
endif
LOCAL_PACKAGE_NAME := Launcher3QuickStep
LOCAL_PRIVILEGED_MODULE := true
-LOCAL_PRODUCT_MODULE := true
+LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
@@ -241,7 +241,7 @@
LOCAL_PACKAGE_NAME := Launcher3QuickStepGo
LOCAL_PRIVILEGED_MODULE := true
-LOCAL_PRODUCT_MODULE := true
+LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep Launcher3GoIconRecents
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
@@ -287,7 +287,7 @@
LOCAL_PACKAGE_NAME := Launcher3GoIconRecents
LOCAL_PRIVILEGED_MODULE := true
-LOCAL_PRODUCT_MODULE := true
+LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3Go Launcher3QuickStep
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
diff --git a/CleanSpec.mk b/CleanSpec.mk
index b2c5266..f7bda94 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -53,6 +53,16 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Launcher2_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/Launcher2.apk)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/priv-app/Launcher3)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/priv-app/Launcher3Go)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/priv-app/Launcher3QuickStep)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/priv-app/Launcher3QuickStepGo)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/priv-app/Launcher3GoIconRecents)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/priv-app/Launcher3)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/priv-app/Launcher3Go)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/priv-app/Launcher3QuickStep)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/priv-app/Launcher3QuickStepGo)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/priv-app/Launcher3GoIconRecents)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
index 0254340..f7e71f3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
@@ -32,6 +32,7 @@
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.allapps.AllAppsStore;
@@ -45,11 +46,13 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.stream.IntStream;
/**
* Provides prediction ability for the hotseat. Fills gaps in hotseat with predicted items, allows
@@ -73,6 +76,7 @@
private DropTarget.DragObject mDragObject;
private int mHotSeatItemsCount;
+ private int mPredictedSpotsCount = 0;
private Launcher mLauncher;
private Hotseat mHotseat;
@@ -86,6 +90,8 @@
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
+ private static HotseatPredictionController sInstance;
+
public HotseatPredictionController(Launcher launcher) {
mLauncher = launcher;
mHotseat = launcher.getHotseat();
@@ -95,6 +101,7 @@
mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
launcher.getDeviceProfile().inv.addOnChangeListener(this);
mHotseat.addOnAttachStateChangeListener(this);
+ sInstance = this;
}
@Override
@@ -144,6 +151,7 @@
}
preparePredictionInfo(predictedItem, rank);
}
+ mPredictedSpotsCount = predictionIndex;
bindItems(newItems, animate, callback);
}
@@ -421,6 +429,26 @@
}
}
+ /**
+ * Fill in predicted_rank field based on app prediction.
+ * Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT
+ */
+ public static void fillInHybridHotseatRank(
+ @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
+ if (sInstance == null || itemInfo.getTargetComponent() == null
+ || itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ return;
+ }
+ final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
+
+ final List<ComponentKeyMapper> predictedApps = sInstance.mComponentKeyMappers;
+ IntStream.range(0, predictedApps.size())
+ .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
+ .findFirst()
+ .ifPresent((rank) -> target.predictedRank =
+ Integer.parseInt(sInstance.mPredictedSpotsCount + "0" + rank));
+ }
+
private static boolean isPredictedIcon(View view) {
return view instanceof PredictedAppIcon && view.getTag() instanceof WorkspaceItemInfo
&& ((WorkspaceItemInfo) view.getTag()).container
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index f9ee701..e45eded 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -26,6 +26,7 @@
import androidx.annotation.NonNull;
+import com.android.launcher3.HotseatPredictionController;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
import com.android.launcher3.ItemInfo;
@@ -316,6 +317,11 @@
&& itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
return;
}
+ if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ HotseatPredictionController.fillInHybridHotseatRank(itemInfo, target);
+ return;
+ }
+
final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
final List<ComponentKeyMapper> predictedApps = manager.getCurrentState().apps;
IntStream.range(0, predictedApps.size())
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index ed5dba1..bd37e56 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -44,6 +44,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SysUINavigationMode;
@@ -140,6 +141,10 @@
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
} else {
+ if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
+ return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
+ }
+
boolean hasAllAppsHeaderExtra = launcher.getAppsView() != null
&& launcher.getAppsView().getFloatingHeaderView().hasVisibleContent();
return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON |
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index 034f158..40235d7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -283,7 +283,7 @@
@Override
public void onConsumerAboutToBeSwitched() {
if (mInQuickSwitchMode && mGestureState.getEndTarget() != null) {
- mGestureState.setEndTarget(HOME);
+ mGestureState.setEndTarget(NEW_TASK);
mCanceled = true;
mCurrentShift.cancelAnimation();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewActionsFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewActionsFactory.java
new file mode 100644
index 0000000..6d17b27
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewActionsFactory.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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.quickstep;
+
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+/**
+ * Overview actions are shown in overview underneath the task snapshot. This factory class is
+ * overrideable in an overlay. The {@link OverviewActions} class provides the view that should be
+ * shown in the Overview.
+ */
+public class OverviewActionsFactory implements ResourceBasedOverride {
+
+ public static final MainThreadInitializedObject<OverviewActionsFactory> INSTANCE =
+ forOverride(OverviewActionsFactory.class, R.string.overview_actions_factory_class);
+
+ /** Create a new Overview Actions for interacting between the actions and overview. */
+ public OverviewActions createOverviewActions() {
+ return new OverviewActions();
+ }
+
+ /** Overlay overrideable, base class does nothing. */
+ public static class OverviewActions {
+ /** Get the view to show in the overview. */
+ public View getView() {
+ return null;
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index e0e20ee..3f5179f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
+import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
@@ -38,6 +39,7 @@
import com.android.launcher3.LauncherAnimationRunner;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.fallback.FallbackRecentsView;
@@ -217,6 +219,12 @@
mFallbackRecentsView.reset();
}
+ @Override
+ protected void onResume() {
+ super.onResume();
+ AccessibilityManagerCompat.sendStateEventToTest(getBaseContext(), OVERVIEW_STATE_ORDINAL);
+ }
+
public void onTaskLaunched() {
mFallbackRecentsView.resetTaskVisuals();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 07537ed..bafb2ef 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -559,10 +559,13 @@
final boolean shouldDefer;
final BaseSwipeUpHandler.Factory factory;
- if (mDeviceState.isFullyGesturalNavMode()
- && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
+ if (!mOverviewComponentObserver.isHomeAndOverviewSame()) {
shouldDefer = previousGestureState.getFinishingRecentsAnimationTaskId() < 0;
- factory = mFallbackSwipeHandlerFactory;
+ if (mDeviceState.isFullyGesturalNavMode()) {
+ factory = mFallbackSwipeHandlerFactory;
+ } else {
+ factory = this::determineFallbackTwoButtonSwipeHandler;
+ }
} else {
shouldDefer = gestureState.getActivityInterface().deferStartingActivity(mDeviceState,
event);
@@ -575,6 +578,23 @@
mInputMonitorCompat, disableHorizontalSwipe, factory);
}
+ /**
+ * Determines whether to use the LauncherSwipeHandler or FallbackSwipeHandler at runtime.
+ * We need to use the FallbackSwipeHandler to handle quick switch from home, otherwise the
+ * normal LauncherSwipeHandler works.
+ */
+ private BaseSwipeUpHandler determineFallbackTwoButtonSwipeHandler(GestureState gestureState,
+ long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
+ boolean runningOverHome = gestureState.getRunningTask() == null
+ || ActivityManagerWrapper.isHomeTask(gestureState.getRunningTask());
+ boolean isQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture;
+ BaseSwipeUpHandler.Factory factory = runningOverHome && isQuickSwitchMode
+ ? mFallbackSwipeHandlerFactory
+ : mLauncherSwipeHandlerFactory;
+ return factory.newHandler(gestureState, touchTimeMs, continuingLastGesture,
+ isLikelyToStartNewTask);
+ }
+
private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState) {
if (mDeviceState.isFullyGesturalNavMode() && gestureState.getRunningTask() != null) {
return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index b602cea..a8d88b9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -111,6 +111,7 @@
import com.android.quickstep.TaskUtils;
import com.android.quickstep.ViewUtils;
import com.android.quickstep.util.AppWindowAnimationHelper;
+import com.android.quickstep.util.LayoutUtils;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -344,8 +345,7 @@
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
mTaskTopMargin = getResources()
.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
- mTaskBottomMargin = getResources().getDimensionPixelSize(
- R.dimen.task_thumbnail_bottom_margin);
+ mTaskBottomMargin = LayoutUtils.thumbnailBottomMargin(getResources());
mSquaredTouchSlop = squaredTouchSlop(context);
mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 0bfde64..94cec72 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -47,11 +47,14 @@
import android.widget.FrameLayout;
import android.widget.Toast;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -59,11 +62,13 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.ViewPool.Reusable;
+import com.android.quickstep.OverviewActionsFactory;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
+import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.quickstep.views.RecentsView.PageCallbacks;
import com.android.quickstep.views.RecentsView.ScrollState;
@@ -159,6 +164,9 @@
private final float mWindowCornerRadius;
private final BaseDraggingActivity mActivity;
+ private OverviewActionsFactory.OverviewActions mOverviewActions;
+ @Nullable private View mActionsView;
+
private ObjectAnimator mIconAndDimAnimator;
private float mIconScaleAnimStartProgress = 0;
private float mFocusTransitionProgress = 1;
@@ -214,6 +222,7 @@
mCurrentFullscreenParams = new FullscreenDrawParams(mCornerRadius);
mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
+ mOverviewActions = OverviewActionsFactory.INSTANCE.get(context).createOverviewActions();
mOutlineProvider = new TaskOutlineProvider(getResources(), mCurrentFullscreenParams);
setOutlineProvider(mOutlineProvider);
}
@@ -223,6 +232,21 @@
super.onFinishInflate();
mSnapshotView = findViewById(R.id.snapshot);
mIconView = findViewById(R.id.icon);
+
+ TaskView.LayoutParams thumbnailParams = (LayoutParams) mSnapshotView.getLayoutParams();
+ thumbnailParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(getResources());
+ mSnapshotView.setLayoutParams(thumbnailParams);
+
+
+ if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
+ mActionsView = mOverviewActions.getView();
+ if (mActionsView != null) {
+ TaskView.LayoutParams params = new TaskView.LayoutParams(LayoutParams.MATCH_PARENT,
+ getResources().getDimensionPixelSize(R.dimen.overview_actions_height),
+ Gravity.BOTTOM);
+ addView(mActionsView, params);
+ }
+ }
}
public TaskMenuView getMenuView() {
@@ -422,6 +446,10 @@
mIconView.setScaleX(scale);
mIconView.setScaleY(scale);
+ if (mActionsView != null) {
+ mActionsView.setAlpha(scale);
+ }
+
mFooterVerticalOffset = 1.0f - scale;
for (FooterWrapper footer : mFooters) {
if (footer != null) {
@@ -626,7 +654,7 @@
TaskOutlineProvider(Resources res, FullscreenDrawParams fullscreenParams) {
mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
- mMarginBottom = res.getDimensionPixelSize(R.dimen.task_thumbnail_bottom_margin);
+ mMarginBottom = LayoutUtils.thumbnailBottomMargin(res);
mFullscreenParams = fullscreenParams;
}
@@ -783,6 +811,7 @@
/**
* Hides the icon and shows insets when this TaskView is about to be shown fullscreen.
+ *
* @param progress: 0 = show icon and no insets; 1 = don't show icon and show full insets.
*/
public void setFullscreenProgress(float progress) {
@@ -793,6 +822,9 @@
mFullscreenProgress = progress;
boolean isFullscreen = mFullscreenProgress > 0;
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
+ if (mActionsView != null) {
+ mActionsView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
+ }
setClipChildren(!isFullscreen);
setClipToPadding(!isFullscreen);
@@ -873,4 +905,5 @@
mScale = scale;
}
}
+
}
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 7a36416..60cfa0c 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -25,8 +25,7 @@
android:id="@+id/snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginTop="@dimen/task_thumbnail_top_margin"
- android:layout_marginBottom="@dimen/task_thumbnail_bottom_margin"/>
+ android:layout_marginTop="@dimen/task_thumbnail_top_margin"/>
<com.android.quickstep.views.IconView
android:id="@+id/icon"
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 327bb14..24ab487 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -15,6 +15,8 @@
-->
<resources>
<string name="task_overlay_factory_class" translatable="false"></string>
+ <!-- Class name for factory object that creates the overview actions UI when enabled. -->
+ <string name="overview_actions_factory_class" translatable="false" />
<!-- Activity which blocks home gesture -->
<string name="gesture_blocking_activity" translatable="false"></string>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 82833ea..9ff1350 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -18,12 +18,13 @@
<dimen name="task_thumbnail_top_margin">24dp</dimen>
<dimen name="task_thumbnail_half_top_margin">12dp</dimen>
- <!-- Can be overridden in overlays. -->
- <dimen name="task_thumbnail_bottom_margin">0dp</dimen>
<dimen name="task_thumbnail_icon_size">48dp</dimen>
<!-- For screens without rounded corners -->
<dimen name="task_corner_radius_small">2dp</dimen>
+ <!-- Overrideable in overlay that provides the Overview Actions. -->
+ <dimen name="overview_actions_height">0dp</dimen>
+
<dimen name="recents_page_spacing">10dp</dimen>
<dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
<dimen name="overview_peek_distance">96dp</dimen>
@@ -58,6 +59,7 @@
<dimen name="task_card_menu_shadow_height">3dp</dimen>
<dimen name="task_card_menu_horizontal_padding">0dp</dimen>
<dimen name="portrait_task_card_horz_space">136dp</dimen>
+ <dimen name="portrait_task_card_horz_space_big_overview">24dp</dimen>
<dimen name="landscape_task_card_horz_space">200dp</dimen>
<dimen name="multi_window_task_card_horz_space">100dp</dimen>
<!-- Copied from framework resource:
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index e590aea..7ff799e 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -48,6 +48,7 @@
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.shared.system.TaskDescriptionCompat;
import java.util.function.Consumer;
@@ -140,7 +141,7 @@
// Load icon
// TODO: Load icon resource (b/143363444)
- Bitmap icon = desc.getIcon();
+ Bitmap icon = TaskDescriptionCompat.getIcon(desc, key.userId);
if (icon != null) {
entry.icon = new FastBitmapDrawable(getBitmapInfo(
new BitmapDrawable(mContext.getResources(), icon),
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index c47bb4a..d49ff89 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -26,6 +26,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.SysUINavigationMode;
import java.lang.annotation.Retention;
@@ -57,9 +58,16 @@
} else {
Resources res = context.getResources();
- extraSpace = getDefaultSwipeHeight(context, dp) + dp.verticalDragHandleSizePx
- + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size)
- + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
+ if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
+ //TODO: this needs to account for the swipe gesture height and accessibility
+ // UI when shown.
+ extraSpace = 0;
+ } else {
+ extraSpace = getDefaultSwipeHeight(context, dp) + dp.verticalDragHandleSizePx
+ + res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_extra_vertical_size)
+ + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
+ }
}
calculateTaskSize(context, dp, extraSpace, MULTI_WINDOW_STRATEGY_HALF_SCREEN, outRect);
}
@@ -99,13 +107,20 @@
} else {
taskWidth = dp.availableWidthPx;
taskHeight = dp.availableHeightPx;
- paddingHorz = res.getDimension(dp.isVerticalBarLayout()
- ? R.dimen.landscape_task_card_horz_space
- : R.dimen.portrait_task_card_horz_space);
+
+ final int paddingResId;
+ if (dp.isVerticalBarLayout()) {
+ paddingResId = R.dimen.landscape_task_card_horz_space;
+ } else if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
+ paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
+ } else {
+ paddingResId = R.dimen.portrait_task_card_horz_space;
+ }
+ paddingHorz = res.getDimension(paddingResId);
}
- float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
- float bottomMargin = res.getDimension(R.dimen.task_thumbnail_bottom_margin);
+ float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
+ float bottomMargin = thumbnailBottomMargin(res);
float paddingVert = res.getDimension(R.dimen.task_card_vert_space);
// Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
@@ -136,4 +151,16 @@
R.dimen.task_card_vert_space);
return shelfHeight + spaceBetweenShelfAndRecents;
}
+
+ /**
+ * Get the margin that the task thumbnail view should use.
+ * @return the margin in pixels.
+ */
+ public static int thumbnailBottomMargin(Resources resources) {
+ if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
+ return resources.getDimensionPixelSize(R.dimen.overview_actions_height);
+ } else {
+ return 0;
+ }
+ }
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index ae30380..80c7056 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -126,6 +126,13 @@
public static final TogglableFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = new TogglableFlag(
"ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
+ public static final TogglableFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = new TogglableFlag(
+ "ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", false,
+ "Show launcher preview in grid picker");
+
+ public static final TogglableFlag ENABLE_OVERVIEW_ACTIONS = new TogglableFlag(
+ "ENABLE_OVERVIEW_ACTIONS", false, "Show app actions in Overview");
+
public static void initialize(Context context) {
// Avoid the disk read for user builds
if (Utilities.IS_DEBUG_DEVICE) {
diff --git a/src/com/android/launcher3/config/FlagTogglerPrefUi.java b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
index 200938d..a7e3732 100644
--- a/src/com/android/launcher3/config/FlagTogglerPrefUi.java
+++ b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
@@ -25,15 +25,15 @@
import android.view.MenuItem;
import android.widget.Toast;
+import androidx.preference.PreferenceDataStore;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.SwitchPreference;
+
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag;
import com.android.launcher3.uioverrides.TogglableFlag;
-import androidx.preference.PreferenceDataStore;
-import androidx.preference.PreferenceFragment;
-import androidx.preference.PreferenceGroup;
-import androidx.preference.SwitchPreference;
-
/**
* Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
*/
@@ -41,7 +41,7 @@
private static final String TAG = "FlagTogglerPrefFrag";
- private final PreferenceFragment mFragment;
+ private final PreferenceFragmentCompat mFragment;
private final Context mContext;
private final SharedPreferences mSharedPreferences;
@@ -72,7 +72,7 @@
}
};
- public FlagTogglerPrefUi(PreferenceFragment fragment) {
+ public FlagTogglerPrefUi(PreferenceFragmentCompat fragment) {
mFragment = fragment;
mContext = fragment.getActivity();
mSharedPreferences = mContext.getSharedPreferences(
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 2badb6e..0c5535f 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -19,6 +19,10 @@
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static android.view.View.VISIBLE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER;
+import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
+import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
+
import android.annotation.TargetApi;
import android.app.Fragment;
import android.content.Context;
@@ -48,6 +52,10 @@
import com.android.launcher3.Hotseat;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -58,11 +66,20 @@
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.LoaderResults;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
+import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* Utility class for generating the preview of Launcher for a given InvariantDeviceProfile.
@@ -241,22 +258,59 @@
}
private void renderScreenShot(Canvas canvas) {
- // Add hotseat icons
- for (int i = 0; i < mIdp.numHotseatIcons; i++) {
- WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
- info.container = Favorites.CONTAINER_HOTSEAT;
- info.screenId = i;
- inflateAndAddIcon(info);
- }
+ if (ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get()) {
+ final LauncherModel launcherModel = LauncherAppState.getInstance(
+ mContext).getModel();
+ final WorkspaceItemsInfoFetcher fetcher = new WorkspaceItemsInfoFetcher();
+ launcherModel.enqueueModelUpdateTask(fetcher);
+ ArrayList<ItemInfo> workspaceItems;
+ try {
+ workspaceItems = fetcher.mTask.get(5, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Log.d(TAG, "Error fetching workspace items info", e);
+ return;
+ }
- // Add workspace icons
- for (int i = 0; i < mIdp.numColumns; i++) {
- WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
- info.container = Favorites.CONTAINER_DESKTOP;
- info.screenId = 0;
- info.cellX = i;
- info.cellY = mIdp.numRows - 1;
- inflateAndAddIcon(info);
+ // Separate the items that are on the current screen, and all the other remaining
+ // items
+ ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
+ ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
+
+ filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceItems,
+ currentWorkspaceItems, otherWorkspaceItems);
+ sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
+
+ for (ItemInfo itemInfo : currentWorkspaceItems) {
+ switch (itemInfo.itemType) {
+ case Favorites.ITEM_TYPE_APPLICATION:
+ case Favorites.ITEM_TYPE_SHORTCUT:
+ case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
+ break;
+ case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
+ // TODO: for folder implementation here.
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ // Add hotseat icons
+ for (int i = 0; i < mIdp.numHotseatIcons; i++) {
+ WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
+ info.container = Favorites.CONTAINER_HOTSEAT;
+ info.screenId = i;
+ inflateAndAddIcon(info);
+ }
+ // Add workspace icons
+ for (int i = 0; i < mIdp.numColumns; i++) {
+ WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
+ info.container = Favorites.CONTAINER_DESKTOP;
+ info.screenId = 0;
+ info.cellX = i;
+ info.cellY = mIdp.numRows - 1;
+ inflateAndAddIcon(info);
+ }
}
// Add first page QSB
@@ -286,6 +340,42 @@
}
}
+ private static class WorkspaceItemsInfoFetcher implements Callable<ArrayList<ItemInfo>>,
+ LauncherModel.ModelUpdateTask {
+
+ private final FutureTask<ArrayList<ItemInfo>> mTask = new FutureTask<>(this);
+
+ private LauncherAppState mApp;
+ private LauncherModel mModel;
+ private BgDataModel mBgDataModel;
+ private AllAppsList mAllAppsList;
+
+ @Override
+ public void init(LauncherAppState app, LauncherModel model, BgDataModel dataModel,
+ AllAppsList allAppsList, Executor uiExecutor) {
+ mApp = app;
+ mModel = model;
+ mBgDataModel = dataModel;
+ mAllAppsList = allAppsList;
+ }
+
+ @Override
+ public void run() {
+ mTask.run();
+ }
+
+ @Override
+ public ArrayList<ItemInfo> call() throws Exception {
+ if (!mModel.isModelLoaded()) {
+ Log.d(TAG, "Workspace not loaded, loading now");
+ mModel.startLoaderForResults(
+ new LoaderResults(mApp, mBgDataModel, mAllAppsList, 0, null));
+ return new ArrayList<>();
+ }
+ return mBgDataModel.workspaceItems;
+ }
+ }
+
private static void measureView(View view, int width, int height) {
view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
view.layout(0, 0, width, height);
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index a00a6bd..76c2951 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -16,6 +16,8 @@
package com.android.launcher3.model;
+import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
+import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.os.Looper;
@@ -27,20 +29,15 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherSettings;
import com.android.launcher3.PagedView;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.LooperIdleLock;
import com.android.launcher3.util.ViewOnDrawExecutor;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
import java.util.concurrent.Executor;
/**
@@ -123,8 +120,9 @@
otherWorkspaceItems);
filterCurrentWorkspaceItems(currentScreenId, appWidgets, currentAppWidgets,
otherAppWidgets);
- sortWorkspaceItemsSpatially(currentWorkspaceItems);
- sortWorkspaceItemsSpatially(otherWorkspaceItems);
+ final InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
+ sortWorkspaceItemsSpatially(idp, currentWorkspaceItems);
+ sortWorkspaceItemsSpatially(idp, otherWorkspaceItems);
// Tell the workspace that we're about to start binding items
executeCallbacksTask(c -> {
@@ -169,89 +167,6 @@
}
}
-
- /** Filters the set of items who are directly or indirectly (via another container) on the
- * specified screen. */
- public static <T extends ItemInfo> void filterCurrentWorkspaceItems(int currentScreenId,
- ArrayList<T> allWorkspaceItems,
- ArrayList<T> currentScreenItems,
- ArrayList<T> otherScreenItems) {
- // Purge any null ItemInfos
- Iterator<T> iter = allWorkspaceItems.iterator();
- while (iter.hasNext()) {
- ItemInfo i = iter.next();
- if (i == null) {
- iter.remove();
- }
- }
-
- // Order the set of items by their containers first, this allows use to walk through the
- // list sequentially, build up a list of containers that are in the specified screen,
- // as well as all items in those containers.
- IntSet itemsOnScreen = new IntSet();
- Collections.sort(allWorkspaceItems,
- (lhs, rhs) -> Integer.compare(lhs.container, rhs.container));
-
- for (T info : allWorkspaceItems) {
- if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- if (info.screenId == currentScreenId) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- otherScreenItems.add(info);
- }
- } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- if (itemsOnScreen.contains(info.container)) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- otherScreenItems.add(info);
- }
- }
- }
- }
-
- /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
- * right) */
- protected void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
- final InvariantDeviceProfile profile = mApp.getInvariantDeviceProfile();
- final int screenCols = profile.numColumns;
- final int screenCellCount = profile.numColumns * profile.numRows;
- Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
- @Override
- public int compare(ItemInfo lhs, ItemInfo rhs) {
- if (lhs.container == rhs.container) {
- // Within containers, order by their spatial position in that container
- switch (lhs.container) {
- case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
- int lr = (lhs.screenId * screenCellCount +
- lhs.cellY * screenCols + lhs.cellX);
- int rr = (rhs.screenId * screenCellCount +
- rhs.cellY * screenCols + rhs.cellX);
- return Integer.compare(lr, rr);
- }
- case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
- // We currently use the screen id as the rank
- return Integer.compare(lhs.screenId, rhs.screenId);
- }
- default:
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
- throw new RuntimeException("Unexpected container type when " +
- "sorting workspace items.");
- }
- return 0;
- }
- } else {
- // Between containers, order by hotseat, desktop
- return Integer.compare(lhs.container, rhs.container);
- }
- }
- });
- }
-
protected void bindWorkspaceItems(final ArrayList<ItemInfo> workspaceItems,
final Executor executor) {
// Bind the workspace items
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 642edbb..cec3007 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -19,7 +19,7 @@
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
-import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
+import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.isSystemApp;
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
new file mode 100644
index 0000000..628dd95
--- /dev/null
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 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.model;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.IntSet;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * Utils class for {@link com.android.launcher3.LauncherModel}.
+ */
+public class ModelUtils {
+
+ /**
+ * Filters the set of items who are directly or indirectly (via another container) on the
+ * specified screen.
+ */
+ public static <T extends ItemInfo> void filterCurrentWorkspaceItems(int currentScreenId,
+ ArrayList<T> allWorkspaceItems,
+ ArrayList<T> currentScreenItems,
+ ArrayList<T> otherScreenItems) {
+ // Purge any null ItemInfos
+ Iterator<T> iter = allWorkspaceItems.iterator();
+ while (iter.hasNext()) {
+ ItemInfo i = iter.next();
+ if (i == null) {
+ iter.remove();
+ }
+ }
+ // Order the set of items by their containers first, this allows use to walk through the
+ // list sequentially, build up a list of containers that are in the specified screen,
+ // as well as all items in those containers.
+ IntSet itemsOnScreen = new IntSet();
+ Collections.sort(allWorkspaceItems,
+ (lhs, rhs) -> Integer.compare(lhs.container, rhs.container));
+ for (T info : allWorkspaceItems) {
+ if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ if (info.screenId == currentScreenId) {
+ currentScreenItems.add(info);
+ itemsOnScreen.add(info.id);
+ } else {
+ otherScreenItems.add(info);
+ }
+ } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ currentScreenItems.add(info);
+ itemsOnScreen.add(info.id);
+ } else {
+ if (itemsOnScreen.contains(info.container)) {
+ currentScreenItems.add(info);
+ itemsOnScreen.add(info.id);
+ } else {
+ otherScreenItems.add(info);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to right)
+ */
+ public static void sortWorkspaceItemsSpatially(InvariantDeviceProfile profile,
+ ArrayList<ItemInfo> workspaceItems) {
+ final int screenCols = profile.numColumns;
+ final int screenCellCount = profile.numColumns * profile.numRows;
+ Collections.sort(workspaceItems, (lhs, rhs) -> {
+ if (lhs.container == rhs.container) {
+ // Within containers, order by their spatial position in that container
+ switch (lhs.container) {
+ case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
+ int lr = (lhs.screenId * screenCellCount + lhs.cellY * screenCols
+ + lhs.cellX);
+ int rr = (rhs.screenId * screenCellCount + +rhs.cellY * screenCols
+ + rhs.cellX);
+ return Integer.compare(lr, rr);
+ }
+ case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
+ // We currently use the screen id as the rank
+ return Integer.compare(lhs.screenId, rhs.screenId);
+ }
+ default:
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ throw new RuntimeException(
+ "Unexpected container type when sorting workspace items.");
+ }
+ return 0;
+ }
+ } else {
+ // Between containers, order by hotseat, desktop
+ return Integer.compare(lhs.container, rhs.container);
+ }
+ });
+ }
+}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index c69ace9..bdf3a69 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -103,7 +103,7 @@
*/
public void addOrMoveItemInDatabase(ItemInfo item,
int container, int screenId, int cellX, int cellY) {
- if (item.container == ItemInfo.NO_ID) {
+ if (item.id == ItemInfo.NO_ID) {
// From all apps
addItemToDatabase(item, container, screenId, cellX, cellY);
} else {
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index 3668313..049fda9 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -44,7 +44,7 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceDataStore;
-import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;
@@ -64,7 +64,7 @@
* See {@link FeatureFlags}.
*/
@TargetApi(Build.VERSION_CODES.O)
-public class DeveloperOptionsFragment extends PreferenceFragment {
+public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
private static final String ACTION_PLUGIN_SETTINGS = "com.android.systemui.action.PLUGIN_SETTINGS";
private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index f4c2852..12085c8 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -16,6 +16,8 @@
package com.android.launcher3.settings;
+import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
+
import static com.android.launcher3.SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY;
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
@@ -29,6 +31,7 @@
import android.provider.Settings;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
@@ -248,6 +251,8 @@
if (highlighter != null) {
getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS);
mPreferenceHighlighted = true;
+ } else {
+ requestAccessibilityFocus(getListView());
}
}
}
@@ -268,6 +273,15 @@
return position >= 0 ? new PreferenceHighlighter(list, position) : null;
}
+ private void requestAccessibilityFocus(@NonNull final RecyclerView rv) {
+ rv.post(() -> {
+ if (!rv.hasFocus() && rv.getChildCount() > 0) {
+ rv.getChildAt(0)
+ .performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
+ }
+ });
+ }
+
@Override
public void onDestroy() {
if (mNotificationDotsObserver != null) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 6583d32..d9ae778 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -17,6 +17,7 @@
package com.android.launcher3.tapl;
import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import android.graphics.Point;
import android.os.SystemClock;
@@ -54,13 +55,13 @@
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to switch from background to overview")) {
verifyActiveContainer();
- goToOverviewUnchecked(BACKGROUND_APP_STATE_ORDINAL);
+ goToOverviewUnchecked();
return mLauncher.isFallbackOverview() ?
new BaseOverview(mLauncher) : new Overview(mLauncher);
}
}
- protected void goToOverviewUnchecked(int expectedState) {
+ protected void goToOverviewUnchecked() {
switch (mLauncher.getNavigationModel()) {
case ZERO_BUTTON: {
final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
@@ -81,9 +82,11 @@
start,
end),
event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(event.getClassName()),
- "Pause wasn't detected");
- mLauncher.sendPointer(
- downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end);
+ () -> "Pause wasn't detected");
+ mLauncher.runToState(
+ () -> mLauncher.sendPointer(
+ downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end),
+ OVERVIEW_STATE_ORDINAL);
break;
}
@@ -105,17 +108,14 @@
startY = endY = mLauncher.getDevice().getDisplayHeight() / 2;
}
- if (mLauncher.isFallbackOverview()) {
- mLauncher.linearGesture(startX, startY, endX, endY, 10, false);
- new BaseOverview(mLauncher);
- } else {
- mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
- }
+ mLauncher.swipeToState(startX, startY, endX, endY, 10, OVERVIEW_STATE_ORDINAL);
break;
}
case THREE_BUTTON:
- mLauncher.waitForSystemUiObject("recent_apps").click();
+ mLauncher.runToState(
+ () -> mLauncher.waitForSystemUiObject("recent_apps").click(),
+ OVERVIEW_STATE_ORDINAL);
break;
}
}
@@ -167,7 +167,7 @@
case THREE_BUTTON:
// Double press the recents button.
UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
- recentsButton.click();
+ mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
mLauncher.getOverview();
recentsButton.click();
break;
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index e0fe933..1e4d937 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -16,7 +16,6 @@
package com.android.launcher3.tapl;
-import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
import androidx.annotation.NonNull;
@@ -52,7 +51,7 @@
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to switch from home to overview")) {
verifyActiveContainer();
- goToOverviewUnchecked(OVERVIEW_STATE_ORDINAL);
+ goToOverviewUnchecked();
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"performed the switch action")) {
return new Overview(mLauncher);
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index df80a51..6881197 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -56,7 +56,7 @@
mLauncher.executeAndWaitForEvent(
() -> mObject.click(),
event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
- "Launching an app didn't open a new window: " + mObject.getText());
+ () -> "Launching an app didn't open a new window: " + mObject.getText());
mLauncher.assertTrue(
"App didn't start: " + selector,
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 727d757..2fea1b7 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -22,7 +22,6 @@
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
-import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
import android.app.ActivityManager;
@@ -78,6 +77,8 @@
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
/**
* The main tapl object. The only object that can be explicitly constructed by the using code. It
@@ -510,7 +511,7 @@
}
Parcelable executeAndWaitForEvent(Runnable command,
- UiAutomation.AccessibilityEventFilter eventFilter, String message) {
+ UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message) {
try {
final AccessibilityEvent event =
mInstrumentation.getUiAutomation().executeAndWaitForEvent(
@@ -518,7 +519,7 @@
assertNotNull("executeAndWaitForEvent returned null (this can't happen)", event);
return event.getParcelableData();
} catch (TimeoutException e) {
- fail(message);
+ fail(message.get());
return null;
}
}
@@ -557,15 +558,12 @@
log("Hierarchy before swiping up to home");
dumpViewHierarchy();
log(action = "swiping up to home from " + getVisibleStateMessage());
- final int finalState = mDevice.hasObject(By.pkg(getLauncherPackageName()))
- || isFallbackOverview()
- ? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL;
try (LauncherInstrumentation.Closable c = addContextLayer(action)) {
swipeToState(
displaySize.x / 2, displaySize.y - 1,
displaySize.x / 2, 0,
- ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, finalState);
+ ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, NORMAL_STATE_ORDINAL);
}
}
} else {
@@ -576,7 +574,7 @@
waitForSystemUiObject("home").click();
},
event -> true,
- "Pressing Home didn't produce any events");
+ () -> "Pressing Home didn't produce any events");
mDevice.waitForIdle();
}
try (LauncherInstrumentation.Closable c = addContextLayer(
@@ -771,14 +769,38 @@
return mDevice;
}
+ private static String eventListToString(List<Integer> actualEvents) {
+ if (actualEvents.isEmpty()) return "no events";
+
+ return "["
+ + actualEvents.stream()
+ .map(state -> TestProtocol.stateOrdinalToString(state))
+ .collect(Collectors.joining(", "))
+ + "]";
+ }
+
+ void runToState(Runnable command, int expectedState) {
+ final List<Integer> actualEvents = new ArrayList<>();
+ executeAndWaitForEvent(
+ command,
+ event -> isSwitchToStateEvent(event, expectedState, actualEvents),
+ () -> "Failed to receive an event for the swipe end: expected "
+ + TestProtocol.stateOrdinalToString(expectedState)
+ + ", actual: " + eventListToString(actualEvents));
+ }
+
+ private boolean isSwitchToStateEvent(
+ AccessibilityEvent event, int expectedState, List<Integer> actualEvents) {
+ if (!TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName())) return false;
+
+ final Bundle parcel = (Bundle) event.getParcelableData();
+ final int actualState = parcel.getInt(TestProtocol.STATE_FIELD);
+ actualEvents.add(actualState);
+ return actualState == expectedState;
+ }
+
void swipeToState(int startX, int startY, int endX, int endY, int steps, int expectedState) {
- final Bundle parcel = (Bundle) executeAndWaitForEvent(
- () -> linearGesture(startX, startY, endX, endY, steps, false),
- event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
- "Swipe failed to receive an event for the swipe end");
- assertEquals("Swipe switched launcher to a wrong state;",
- TestProtocol.stateOrdinalToString(expectedState),
- TestProtocol.stateOrdinalToString(parcel.getInt(TestProtocol.STATE_FIELD)));
+ runToState(() -> linearGesture(startX, startY, endX, endY, steps, false), expectedState);
}
int getBottomGestureSize() {
@@ -864,7 +886,7 @@
executeAndWaitForEvent(
() -> linearGesture(startX, startY, endX, endY, steps, slowDown),
event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
- "Didn't receive a scroll end message: " + startX + ", " + startY
+ () -> "Didn't receive a scroll end message: " + startX + ", " + startY
+ ", " + endX + ", " + endY);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 2ee424b..46f8ba5 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -67,8 +67,8 @@
mLauncher.executeAndWaitForEvent(
() -> mTask.click(),
event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
- "Launching task didn't open a new window: " +
- mTask.getParent().getContentDescription());
+ () -> "Launching task didn't open a new window: "
+ + mTask.getParent().getContentDescription());
}
return new Background(mLauncher);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 81d343d..8a53ef1 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -17,6 +17,8 @@
package com.android.launcher3.tapl;
import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
import static junit.framework.TestCase.assertTrue;
@@ -165,14 +167,21 @@
LauncherInstrumentation.log("dragIconToWorkspace: begin");
final Point launchableCenter = launchable.getObject().getVisibleCenter();
final long downTime = SystemClock.uptimeMillis();
- launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, launchableCenter);
- LauncherInstrumentation.log("dragIconToWorkspace: sent down");
- launcher.waitForLauncherObject(longPressIndicator);
- LauncherInstrumentation.log("dragIconToWorkspace: indicator");
- launcher.movePointer(launchableCenter, dest, 10, downTime, true);
+ launcher.runToState(
+ () -> {
+ launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
+ launchableCenter);
+ LauncherInstrumentation.log("dragIconToWorkspace: sent down");
+ launcher.waitForLauncherObject(longPressIndicator);
+ LauncherInstrumentation.log("dragIconToWorkspace: indicator");
+ launcher.movePointer(launchableCenter, dest, 10, downTime, true);
+ },
+ SPRING_LOADED_STATE_ORDINAL);
LauncherInstrumentation.log("dragIconToWorkspace: moved pointer");
- launcher.sendPointer(
- downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest);
+ launcher.runToState(
+ () -> launcher.sendPointer(
+ downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest),
+ NORMAL_STATE_ORDINAL);
LauncherInstrumentation.log("dragIconToWorkspace: end");
launcher.waitUntilGone("drop_target_bar");
}