Merge "Adding logs to monitor OverviewCommandHelper" into main
diff --git a/aconfig/launcher_search.aconfig b/aconfig/launcher_search.aconfig
index b243922..b98eee6 100644
--- a/aconfig/launcher_search.aconfig
+++ b/aconfig/launcher_search.aconfig
@@ -45,8 +45,11 @@
flag {
- name: "private_space_floating_mask_view"
+ name: "private_space_add_floating_mask_view"
namespace: "launcher_search"
description: "This flag enables the floating mask view as part of the Private Space animation. "
bug: "339850589"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
diff --git a/quickstep/res/layout/taskbar_divider_popup_menu.xml b/quickstep/res/layout/taskbar_divider_popup_menu.xml
index 7f4f76c..43dc8d8 100644
--- a/quickstep/res/layout/taskbar_divider_popup_menu.xml
+++ b/quickstep/res/layout/taskbar_divider_popup_menu.xml
@@ -51,7 +51,6 @@
android:layout_height="wrap_content"
android:id="@+id/taskbar_pinning_switch"
android:background="@null"
- android:clickable="false"
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
android:paddingStart="12dp"
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index 12f1e63..d25e644 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -98,16 +98,21 @@
popupContainer.getDescendantRectRelativeToSelf(dividerView, outPos)
}
- @SuppressLint("UseSwitchCompatOrMaterialCode")
+ @SuppressLint("UseSwitchCompatOrMaterialCode", "ClickableViewAccessibility")
override fun onFinishInflate() {
super.onFinishInflate()
val taskbarSwitchOption = requireViewById<LinearLayout>(R.id.taskbar_switch_option)
val alwaysShowTaskbarSwitch = requireViewById<Switch>(R.id.taskbar_pinning_switch)
val taskbarVisibilityIcon = requireViewById<View>(R.id.taskbar_pinning_visibility_icon)
+
alwaysShowTaskbarSwitch.isChecked = alwaysShowTaskbarOn
+ alwaysShowTaskbarSwitch.setOnTouchListener { view, event ->
+ (view.parent as View).onTouchEvent(event)
+ }
+ alwaysShowTaskbarSwitch.setOnClickListener { view -> (view.parent as View).performClick() }
+
if (ActivityContext.lookupContext<TaskbarActivityContext>(context).isGestureNav) {
taskbarSwitchOption.setOnClickListener {
- alwaysShowTaskbarSwitch.isClickable = true
alwaysShowTaskbarSwitch.isChecked = !alwaysShowTaskbarOn
onClickAlwaysShowTaskbarSwitchOption()
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 96f4a5f..e1ddb6a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -236,7 +236,7 @@
provider.insetsSize = getInsetsForGravityWithCutout(contentHeight, gravity, endRotation)
} else if (provider.type == mandatorySystemGestures()) {
if (context.isThreeButtonNav) {
- // Leave null to inset by the window frame
+ provider.insetsSize = Insets.of(0, 0, 0, 0)
} else {
val gestureHeight =
ResourceUtils.getNavbarSize(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 74d2d60..8fdb460 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -100,7 +100,10 @@
// If we're in an app and any of these flags are enabled, taskbar should be stashed.
private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_SYSUI
| FLAG_STASHED_IN_APP_SETUP | FLAG_STASHED_IN_TASKBAR_ALL_APPS
- | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO;
+ | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO | FLAG_STASHED_IME;
+
+ // If we're in overview and any of these flags are enabled, taskbar should be stashed.
+ private static final int FLAGS_STASHED_IN_OVERVIEW = FLAG_STASHED_IME;
// If any of these flags are enabled, inset apps by our stashed height instead of our unstashed
// height. This way the reported insets are consistent even during transitions out of the app.
@@ -111,7 +114,7 @@
// If any of these flags are enabled, the taskbar must be stashed.
private static final int FLAGS_FORCE_STASHED = FLAG_STASHED_SYSUI | FLAG_STASHED_DEVICE_LOCKED
- | FLAG_STASHED_IN_TASKBAR_ALL_APPS | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IME;
+ | FLAG_STASHED_IN_TASKBAR_ALL_APPS | FLAG_STASHED_SMALL_SCREEN;
/**
* How long to stash/unstash when manually invoked via long press.
@@ -244,8 +247,13 @@
boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
+ boolean inOverview = hasAnyFlag(flags, FLAG_IN_OVERVIEW);
+ boolean stashedInOverview = hasAnyFlag(flags, FLAGS_STASHED_IN_OVERVIEW);
boolean forceStashed = hasAnyFlag(flags, FLAGS_FORCE_STASHED);
- return (inApp && stashedInApp) || (!inApp && stashedLauncherState) || forceStashed;
+ return (inApp && stashedInApp)
+ || (!inApp && stashedLauncherState)
+ || (inOverview && stashedInOverview)
+ || forceStashed;
};
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
mIsStashedPredicate);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index f614dc6..45a9fa1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -98,7 +98,6 @@
mBarView = barView;
mSystemUiProxy = SystemUiProxy.INSTANCE.get(mActivity);
mBubbleBarAlpha = new MultiValueAlpha(mBarView, 1 /* num alpha channels */);
- mBubbleBarAlpha.setUpdateVisibility(true);
mIconSize = activity.getResources().getDimensionPixelSize(
R.dimen.bubblebar_icon_size);
}
@@ -408,7 +407,19 @@
b.getView().setOnClickListener(mBubbleClickListener);
mBubbleDragController.setupBubbleView(b.getView());
+ if (b instanceof BubbleBarOverflow) {
+ return;
+ }
+
if (suppressAnimation || !(b instanceof BubbleBarBubble bubble)) {
+ // the bubble bar and handle are initialized as part of the first bubble animation.
+ // if the animation is suppressed, immediately stash or show the bubble bar to
+ // ensure they've been initialized.
+ if (mTaskbarStashController.isInApp()) {
+ mBubbleStashController.stashBubbleBarImmediate();
+ } else {
+ mBubbleStashController.showBubbleBarImmediate();
+ }
return;
}
animateBubbleNotification(bubble, isExpanding);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
index 0e6fa3c..a6096e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
@@ -169,7 +169,8 @@
private void setupMagnetizedObject(@NonNull View magnetizedView) {
mMagnetizedObject = new MagnetizedObject<>(mActivity.getApplicationContext(),
- magnetizedView, DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y) {
+ magnetizedView, BubbleDragController.DRAG_TRANSLATION_X,
+ DynamicAnimation.TRANSLATION_Y) {
@Override
public float getWidth(@NonNull View underlyingObject) {
return underlyingObject.getWidth() * underlyingObject.getScaleX();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
index 287e906..7aed2d2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
@@ -60,7 +60,6 @@
private final float mBubbleFocusedScale;
private final float mBubbleCapturedScale;
private final float mDismissCapturedScale;
- private final FloatPropertyCompat<View> mTranslationXProperty;
/**
* Should be initialised for each dragged view
@@ -82,28 +81,9 @@
if (view instanceof BubbleBarView) {
mBubbleFocusedScale = SCALE_BUBBLE_BAR_FOCUSED;
mBubbleCapturedScale = mDismissCapturedScale;
- mTranslationXProperty = DynamicAnimation.TRANSLATION_X;
} else {
mBubbleFocusedScale = SCALE_BUBBLE_FOCUSED;
mBubbleCapturedScale = SCALE_BUBBLE_CAPTURED;
- // Wrap BubbleView.DRAG_TRANSLATION_X as it can't be cast to FloatPropertyCompat<View>
- mTranslationXProperty = new FloatPropertyCompat<>(
- BubbleView.DRAG_TRANSLATION_X.getName()) {
- @Override
- public float getValue(View object) {
- if (object instanceof BubbleView bubbleView) {
- return BubbleView.DRAG_TRANSLATION_X.get(bubbleView);
- }
- return 0;
- }
-
- @Override
- public void setValue(View object, float value) {
- if (object instanceof BubbleView bubbleView) {
- BubbleView.DRAG_TRANSLATION_X.setValue(bubbleView, value);
- }
- }
- };
}
}
@@ -140,7 +120,7 @@
mBubbleAnimator
.spring(DynamicAnimation.SCALE_X, 1f)
.spring(DynamicAnimation.SCALE_Y, 1f)
- .spring(mTranslationXProperty, restingPosition.x, velocity.x,
+ .spring(BubbleDragController.DRAG_TRANSLATION_X, restingPosition.x, velocity.x,
mTranslationConfig)
.spring(DynamicAnimation.TRANSLATION_Y, restingPosition.y, velocity.y,
mTranslationConfig)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 2ebc3e8..fbd1b88 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -24,6 +24,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.wm.shell.common.bubbles.BaseBubblePinController.LocationChangeListener;
@@ -38,6 +39,37 @@
* Restores initial position of dragged view if released outside of the dismiss target.
*/
public class BubbleDragController {
+
+ /**
+ * Property to update dragged bubble x-translation value.
+ * <p>
+ * When applied to {@link BubbleView}, will use set the translation through
+ * {@link BubbleView#getDragTranslationX()} and {@link BubbleView#setDragTranslationX(float)}
+ * methods.
+ * <p>
+ * When applied to {@link BubbleBarView}, will use {@link View#getTranslationX()} and
+ * {@link View#setTranslationX(float)}.
+ */
+ public static final FloatPropertyCompat<View> DRAG_TRANSLATION_X = new FloatPropertyCompat<>(
+ "dragTranslationX") {
+ @Override
+ public float getValue(View view) {
+ if (view instanceof BubbleView bubbleView) {
+ return bubbleView.getDragTranslationX();
+ }
+ return view.getTranslationX();
+ }
+
+ @Override
+ public void setValue(View view, float value) {
+ if (view instanceof BubbleView bubbleView) {
+ bubbleView.setDragTranslationX(value);
+ } else {
+ view.setTranslationX(value);
+ }
+ }
+ };
+
private final TaskbarActivityContext mActivity;
private BubbleBarController mBubbleBarController;
private BubbleBarViewController mBubbleBarViewController;
@@ -62,8 +94,10 @@
mBubblePinController = bubbleControllers.bubblePinController;
mBubbleDismissController.setListener(
stuck -> {
- mBubbleBarPinController.setDropTargetHidden(stuck);
- mBubblePinController.setDropTargetHidden(stuck);
+ if (stuck) {
+ mBubbleBarPinController.onStuckToDismissTarget();
+ mBubblePinController.onStuckToDismissTarget();
+ }
});
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
index 5d01b9b..74ddf90 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -418,6 +418,7 @@
/** Stashes the bubble bar immediately without animation. */
public void stashBubbleBarImmediate() {
mHandleViewController.setTranslationYForSwipe(0);
+ mBubbleStashedHandleAlpha.setValue(1);
mIconAlphaForStash.setValue(0);
mIconTranslationYForStash.updateValue(getStashTranslation());
mIconScaleForStash.updateValue(STASHED_BAR_SCALE);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index 61a6bce..2e37dc7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -22,13 +22,11 @@
import android.graphics.Outline;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.FloatProperty;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
-import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.launcher3.R;
@@ -49,25 +47,6 @@
public static final int DEFAULT_PATH_SIZE = 100;
/**
- * Property to update drag translation value.
- *
- * @see BubbleView#getDragTranslationX()
- * @see BubbleView#setDragTranslationX(float)
- */
- public static final FloatProperty<BubbleView> DRAG_TRANSLATION_X = new FloatProperty<>(
- "dragTranslationX") {
- @Override
- public void setValue(@NonNull BubbleView bubbleView, float value) {
- bubbleView.setDragTranslationX(value);
- }
-
- @Override
- public Float get(BubbleView bubbleView) {
- return bubbleView.getDragTranslationX();
- }
- };
-
- /**
* Flags that suppress the visibility of the 'new' dot or the app badge, for one reason or
* another. If any of these flags are set, the dot will not be shown.
* If {@link SuppressionFlag#BEHIND_STACK} then the app badge will not be shown.
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 2625919..fa80dc2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -102,6 +102,11 @@
}
@Override
+ public int getTitle() {
+ return R.string.all_apps_label;
+ }
+
+ @Override
public float getVerticalProgress(Launcher launcher) {
return 0f;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 7173298..6822f1b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -182,6 +182,11 @@
return launcher.getString(R.string.accessibility_recent_apps);
}
+ @Override
+ public int getTitle() {
+ return R.string.accessibility_recent_apps;
+ }
+
public static float getDefaultSwipeHeight(Launcher launcher) {
return LayoutUtils.getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 811b9fd..08c2e1c 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -308,7 +308,8 @@
return;
}
LauncherOverlayManager om = launcher.getOverlayManager();
- if (!launcher.isStarted() || launcher.isForceInvisible()) {
+ if (!SystemUiProxy.INSTANCE.get(launcher).getHomeVisibilityState().isHomeVisible()
+ || launcher.isForceInvisible()) {
om.hideOverlay(false /* animate */);
} else {
om.hideOverlay(150);
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index dfbae65..b4b8c5b 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -198,6 +198,12 @@
.unstashBubbleBarIfStashed();
});
return response;
+ case TestProtocol.REQUEST_INJECT_FAKE_TRACKPAD:
+ runOnTISBinder(tisBinder -> tisBinder.injectFakeTrackpadForTesting());
+ return response;
+ case TestProtocol.REQUEST_EJECT_FAKE_TRACKPAD:
+ runOnTISBinder(tisBinder -> tisBinder.ejectFakeTrackpadForTesting());
+ return response;
}
return super.call(method, arg, extras);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 97a0b3f..3df62da 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -371,6 +371,9 @@
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
ACTIVITY_TRACKER.handleCreate(this);
+
+ // Set screen title for Talkback
+ setTitle(R.string.accessibility_recent_apps);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 4b5c826..fd141c3 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -21,6 +21,7 @@
import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.app.ActivityOptions;
@@ -107,10 +108,14 @@
public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
TaskContainer taskContainer) {
TaskView taskView = taskContainer.getTaskView();
+ int actionId = taskContainer.getStagePosition() == STAGE_POSITION_BOTTOM_OR_RIGHT
+ ? R.id.action_app_info_bottom_right
+ : R.id.action_app_info_top_left;
+
AppInfo.SplitAccessibilityInfo accessibilityInfo =
new AppInfo.SplitAccessibilityInfo(taskView.containsMultipleTasks(),
TaskUtils.getTitle(taskView.getContext(), taskContainer.getTask()),
- taskContainer.getA11yNodeId()
+ actionId
);
return Collections.singletonList(new AppInfo(container, taskContainer.getItemInfo(),
taskView, accessibilityInfo));
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index b153396..58bb8fc 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -85,6 +85,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ConstantItem;
@@ -399,6 +400,25 @@
return tis.mTaskbarManager;
}
+ @VisibleForTesting
+ public void injectFakeTrackpadForTesting() {
+ TouchInteractionService tis = mTis.get();
+ if (tis == null) return;
+ tis.mTrackpadsConnected.add(1000);
+ tis.initInputMonitor("tapl testing");
+ }
+
+ @VisibleForTesting
+ public void ejectFakeTrackpadForTesting() {
+ TouchInteractionService tis = mTis.get();
+ if (tis == null) return;
+ tis.mTrackpadsConnected.clear();
+ // This method destroys the current input monitor if set up, and only init a new one
+ // in 3-button mode if {@code mTrackpadsConnected} is not empty. So in other words,
+ // it will destroy the input monitor.
+ tis.initInputMonitor("tapl testing");
+ }
+
/**
* Sets whether a predictive back-to-home animation is in progress in the device state
*/
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index e4e2eb2..6f9cbfd 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -27,6 +27,8 @@
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_NONE;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.isPersistentSnapPosition;
@@ -186,6 +188,10 @@
}
@PersistentSnapPosition int snapPosition = gtv.getSnapPosition();
+ if (snapPosition == SNAP_TO_NONE) {
+ // Free snap mode is enabled, just save it as 50/50 split.
+ snapPosition = SNAP_TO_50_50;
+ }
if (!isPersistentSnapPosition(snapPosition)) {
// If we received an illegal snap position, log an error and do not create the app pair
Log.wtf(TAG, "Tried to save an app pair with illegal snapPosition "
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index cb1ee0c..6377bf4 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -23,6 +23,7 @@
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.AppUsageLimit;
@@ -38,6 +39,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -49,6 +51,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
+import com.android.quickstep.TaskUtils;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.systemui.shared.recents.model.Task;
@@ -68,13 +71,15 @@
private static final int SPLIT_GRID_BANNER_LARGE = 1;
/** Used for grid task view, only showing icon */
private static final int SPLIT_GRID_BANNER_SMALL = 2;
+
@IntDef(value = {
SPLIT_BANNER_FULLSCREEN,
SPLIT_GRID_BANNER_LARGE,
SPLIT_GRID_BANNER_SMALL,
})
@Retention(RetentionPolicy.SOURCE)
- @interface SplitBannerConfig{}
+ @interface SplitBannerConfig {
+ }
static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
static final int MINUTE_MS = 60000;
@@ -398,4 +403,36 @@
mBanner.setVisibility(visibility);
}
+
+ private int getAccessibilityActionId() {
+ return (mSplitBounds != null
+ && mSplitBounds.rightBottomTaskId == mTask.key.id)
+ ? R.id.action_digital_wellbeing_bottom_right
+ : R.id.action_digital_wellbeing_top_left;
+ }
+
+ @Nullable
+ public AccessibilityNodeInfo.AccessibilityAction getDWBAccessibilityAction() {
+ if (!hasLimit()) {
+ return null;
+ }
+
+ Context context = mContainer.asContext();
+ String label =
+ (mTaskView.containsMultipleTasks())
+ ? context.getString(
+ R.string.split_app_usage_settings,
+ TaskUtils.getTitle(context, mTask)
+ ) : context.getString(R.string.accessibility_app_usage_settings);
+ return new AccessibilityNodeInfo.AccessibilityAction(getAccessibilityActionId(), label);
+ }
+
+ public boolean handleAccessibilityAction(int action) {
+ if (getAccessibilityActionId() == action) {
+ openAppUsageSettings(mTaskView);
+ return true;
+ } else {
+ return false;
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index d729bdc..604d072 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -20,6 +20,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
@@ -49,6 +50,7 @@
*/
public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayout
implements OnClickListener, Insettable {
+ public static final String TAG = "OverviewActionsView";
private final Rect mInsets = new Rect();
@IntDef(flag = true, value = {
@@ -254,6 +256,8 @@
* pair.
*/
public void updateForGroupedTask(boolean isGroupedTask, boolean canSaveAppPair) {
+ Log.d(TAG, "updateForGroupedTask() called with: isGroupedTask = [" + isGroupedTask
+ + "], canSaveAppPair = [" + canSaveAppPair + "]");
mIsGroupedTask = isGroupedTask;
mCanSaveAppPair = canSaveAppPair;
updateActionButtonsVisibility();
@@ -273,6 +277,8 @@
assert mDp != null;
boolean showSingleTaskActions = !mIsGroupedTask;
boolean showGroupActions = mIsGroupedTask && mDp.isTablet && mCanSaveAppPair;
+ Log.d(TAG, "updateActionButtonsVisibility() called: showSingleTaskActions = ["
+ + showSingleTaskActions + ", showGroupActions = [" + showGroupActions + "]");
getActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showSingleTaskActions ? 1 : 0);
getGroupActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showGroupActions ? 1 : 0);
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 71093af..cae23a1 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -38,6 +38,7 @@
import android.view.ViewGroup
import android.view.ViewStub
import android.view.accessibility.AccessibilityNodeInfo
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.FrameLayout
import android.widget.Toast
import androidx.annotation.IntDef
@@ -67,7 +68,6 @@
import com.android.launcher3.util.RunnableList
import com.android.launcher3.util.SafeCloseable
import com.android.launcher3.util.SplitConfigurationOptions
-import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
@@ -133,19 +133,26 @@
val taskIds: IntArray
/** Returns a copy of integer array containing taskIds of all tasks in the TaskView. */
get() = taskContainers.map { it.task.key.id }.toIntArray()
+
val thumbnailViews: Array<TaskThumbnailViewDeprecated>
get() = taskContainers.map { it.thumbnailViewDeprecated }.toTypedArray()
+
val isGridTask: Boolean
/** Returns whether the task is part of overview grid and not being focused. */
get() = container.deviceProfile.isTablet && !isFocusedTask
+
val isRunningTask: Boolean
get() = this === recentsView?.runningTaskView
+
val isFocusedTask: Boolean
get() = this === recentsView?.focusedTaskView
+
val taskCornerRadius: Float
get() = currentFullscreenParams.cornerRadius
+
val recentsView: RecentsView<*, *>?
get() = parent as? RecentsView<*, *>
+
val pagedOrientationHandler: RecentsPagedOrientationHandler
get() = orientedState.orientationHandler
@@ -153,10 +160,12 @@
val firstTask: Task
/** Returns the first task bound to this TaskView. */
get() = taskContainers[0].task
+
@get:Deprecated("Use [taskContainers] instead.")
val firstThumbnailViewDeprecated: TaskThumbnailViewDeprecated
/** Returns the first thumbnailView of the TaskView. */
get() = taskContainers[0].thumbnailViewDeprecated
+
@get:Deprecated("Use [taskContainers] instead.")
val firstItemInfo: ItemInfo
get() = taskContainers[0].itemInfo
@@ -173,6 +182,7 @@
* not change according to a temporary state.
*/
get() = Utilities.mapRange(gridProgress, nonGridScale, 1f)
+
protected val persistentTranslationX: Float
/**
* Returns addition of translationX that is persistent (e.g. fullscreen and grid), and does
@@ -182,42 +192,50 @@
(getNonGridTrans(nonGridTranslationX) +
getGridTrans(this.gridTranslationX) +
getNonGridTrans(nonGridPivotTranslationX))
+
protected val persistentTranslationY: Float
/**
* Returns addition of translationY that is persistent (e.g. fullscreen and grid), and does
* not change according to a temporary state (e.g. task offset).
*/
get() = boxTranslationY + getGridTrans(gridTranslationY)
+
protected val primarySplitTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getPrimaryValue(
SPLIT_SELECT_TRANSLATION_X,
SPLIT_SELECT_TRANSLATION_Y
)
+
protected val secondarySplitTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getSecondaryValue(
SPLIT_SELECT_TRANSLATION_X,
SPLIT_SELECT_TRANSLATION_Y
)
+
protected val primaryDismissTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getPrimaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
+
protected val secondaryDismissTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getSecondaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
+
protected val primaryTaskOffsetTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getPrimaryValue(
TASK_OFFSET_TRANSLATION_X,
TASK_OFFSET_TRANSLATION_Y
)
+
protected val secondaryTaskOffsetTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getSecondaryValue(
TASK_OFFSET_TRANSLATION_X,
TASK_OFFSET_TRANSLATION_Y
)
+
protected val taskResistanceTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getSecondaryValue(
@@ -234,6 +252,7 @@
/** Returns a list of all TaskContainers in the TaskView. */
lateinit var taskContainers: List<TaskContainer>
protected set
+
lateinit var orientedState: RecentsOrientedState
var taskViewId = UNBOUND_TASK_VIEW_ID
@@ -264,46 +283,55 @@
field = value
onModalnessUpdated(field)
}
+
protected var taskThumbnailSplashAlpha = 0f
set(value) {
field = value
applyThumbnailSplashAlpha()
}
+
protected var nonGridScale = 1f
set(value) {
field = value
applyScale()
}
+
private var dismissScale = 1f
set(value) {
field = value
applyScale()
}
+
private var dismissTranslationX = 0f
set(value) {
field = value
applyTranslationX()
}
+
private var dismissTranslationY = 0f
set(value) {
field = value
applyTranslationY()
}
+
private var taskOffsetTranslationX = 0f
set(value) {
field = value
applyTranslationX()
}
+
private var taskOffsetTranslationY = 0f
set(value) {
field = value
applyTranslationY()
}
+
private var taskResistanceTranslationX = 0f
set(value) {
field = value
applyTranslationX()
}
+
private var taskResistanceTranslationY = 0f
set(value) {
field = value
@@ -321,6 +349,7 @@
field = value
applyTranslationX()
}
+
var gridTranslationY = 0f
protected set(value) {
field = value
@@ -339,6 +368,7 @@
field = value
applyTranslationX()
}
+
protected var nonGridPivotTranslationX = 0f
set(value) {
field = value
@@ -350,16 +380,19 @@
field = value
applyTranslationY()
}
+
private var splitSelectTranslationX = 0f
set(value) {
field = value
applyTranslationX()
}
+
protected var stableAlpha = 1f
set(value) {
field = value
alpha = stableAlpha
}
+
protected var shouldShowScreenshot = false
get() = !isRunningTask || field
/** Enable or disable showing border on hover and focus change */
@@ -375,6 +408,7 @@
hoverBorderAnimator?.setBorderVisibility(visible = field && isHovered, animated = true)
focusBorderAnimator?.setBorderVisibility(visible = field && isFocused, animated = true)
}
+
protected var iconScaleAnimStartProgress = 0f
private var focusTransitionProgress = 1f
@@ -522,11 +556,12 @@
super.onInitializeAccessibilityNodeInfo(info)
with(info) {
addAction(
- AccessibilityNodeInfo.AccessibilityAction(
- R.string.accessibility_close,
+ AccessibilityAction(
+ R.id.action_close,
context.getText(R.string.accessibility_close)
)
)
+
taskContainers.forEach {
TraceHelper.allowIpcs("TV.a11yInfo") {
TaskOverlayFactory.getEnabledShortcuts(this@TaskView, it).forEach { shortcut ->
@@ -534,15 +569,12 @@
}
}
}
- // TODO(b/341672022): handle multiple digitalWellBeingToast accessibility actions
- if (taskContainers[0].digitalWellBeingToast?.hasLimit() == true) {
- addAction(
- AccessibilityNodeInfo.AccessibilityAction(
- R.string.accessibility_app_usage_settings,
- context.getText(R.string.accessibility_app_usage_settings)
- )
- )
+
+ // Add DWB accessibility action at the end of the list
+ taskContainers.forEach {
+ it.digitalWellBeingToast?.getDWBAccessibilityAction()?.let(::addAction)
}
+
recentsView?.let {
collectionItemInfo =
AccessibilityNodeInfo.CollectionItemInfo.obtain(
@@ -557,16 +589,17 @@
}
override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
- if (action == R.string.accessibility_close) {
+ // TODO(b/343708271): Add support for multiple tasks per action.
+ if (action == R.id.action_close) {
recentsView?.dismissTask(this, true /*animateTaskView*/, true /*removeTask*/)
return true
}
- if (action == R.string.accessibility_app_usage_settings) {
- // TODO(b/341672022): handle multiple digitalWellBeingToast accessibility actions
- taskContainers[0].digitalWellBeingToast?.openAppUsageSettings(this)
- return true
- }
+
taskContainers.forEach {
+ if (it.digitalWellBeingToast?.handleAccessibilityAction(action) == true) {
+ return true
+ }
+
TaskOverlayFactory.getEnabledShortcuts(this, it).forEach { shortcut ->
if (shortcut.hasHandlerForAction(action)) {
shortcut.onClick(this)
@@ -574,6 +607,7 @@
}
}
}
+
return super.performAccessibilityAction(action, arguments)
}
@@ -1555,11 +1589,6 @@
) {
val overlay: TaskOverlay<*> = taskOverlayFactory.createOverlay(this)
- @IdRes
- val a11yNodeId: Int =
- if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) R.id.split_bottomRight_appInfo
- else R.id.split_topLeft_appInfo
-
val snapshotView: View
get() = thumbnailView ?: thumbnailViewDeprecated
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
index 106e590..2c23f86 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
@@ -37,6 +37,7 @@
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,14 +48,20 @@
private static final String READ_DEVICE_CONFIG_PERMISSION =
"android.permission.READ_DEVICE_CONFIG";
+ @Before
+ public void setup() {
+ mLauncher.injectFakeTrackpad();
+ }
+
@After
public void tearDown() {
+ mLauncher.ejectFakeTrackpad();
mLauncher.setTrackpadGestureType(TrackpadGestureType.NONE);
}
@Test
@PortraitLandscape
- @NavigationModeSwitch(mode = ZERO_BUTTON)
+ @NavigationModeSwitch
public void goHome() throws Exception {
assumeTrue(mLauncher.isTablet());
@@ -87,7 +94,7 @@
@Test
@PortraitLandscape
- @NavigationModeSwitch(mode = ZERO_BUTTON)
+ @NavigationModeSwitch
@ScreenRecordRule.ScreenRecord // b/336606166
@TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/336606166
public void switchToOverview() throws Exception {
@@ -100,7 +107,7 @@
@Test
@PortraitLandscape
- @NavigationModeSwitch(mode = ZERO_BUTTON)
+ @NavigationModeSwitch
public void testAllAppsFromHome() throws Exception {
assumeTrue(mLauncher.isTablet());
@@ -110,8 +117,8 @@
}
@Test
- @NavigationModeSwitch(mode = ZERO_BUTTON)
@PortraitLandscape
+ @NavigationModeSwitch
public void testQuickSwitchFromHome() throws Exception {
assumeTrue(mLauncher.isTablet());
diff --git a/res/layout/private_space_header.xml b/res/layout/private_space_header.xml
index cefe394..9c0f129 100644
--- a/res/layout/private_space_header.xml
+++ b/res/layout/private_space_header.xml
@@ -37,7 +37,7 @@
android:gravity="center_vertical"
android:layout_alignParentEnd="true"
android:animateLayoutChanges="false">
- <ImageButton
+ <com.android.launcher3.allapps.PrivateSpaceSettingsButton
android:id="@+id/ps_settings_button"
android:layout_width="@dimen/ps_header_image_height"
android:layout_height="@dimen/ps_header_image_height"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2741158..aa83c01 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -320,6 +320,8 @@
<dimen name="bg_popup_item_height">52dp</dimen>
<dimen name="bg_popup_item_vertical_padding">12dp</dimen>
<dimen name="pre_drag_view_scale">6dp</dimen>
+ <!-- Minimum size of the widget dragged view to keep it visible under the finger. -->
+ <dimen name="widget_drag_view_min_scale_down_size">70dp</dimen>
<!-- an icon with shortcuts must be dragged this far before the container is removed. -->
<dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen>
<!-- Possibly related to b/235886078, icon needs to be scaled up to match expected visual size of 32 dp -->
diff --git a/res/values/id.xml b/res/values/id.xml
index 7bb9396..28496b5 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -19,9 +19,6 @@
<item type="id" name="view_type_widgets_space" />
<item type="id" name="view_type_widgets_list" />
<item type="id" name="view_type_widgets_header" />
- <!-- Used for A11y actions in staged split to identify each task uniquely -->
- <item type="id" name="split_topLeft_appInfo" />
- <item type="id" name="split_bottomRight_appInfo" />
<!-- Accessibility actions -->
<item type="id" name="action_remove" />
@@ -37,6 +34,12 @@
<item type="id" name="action_remote_action_shortcut" />
<item type="id" name="action_dismiss_prediction" />
<item type="id" name="action_pin_prediction"/>
+ <item type="id" name="action_close"/>
+ <!-- Used for A11y actions in staged split to identify each task uniquely -->
+ <item type="id" name="action_app_info_top_left" />
+ <item type="id" name="action_app_info_bottom_right" />
+ <item type="id" name="action_digital_wellbeing_top_left" />
+ <item type="id" name="action_digital_wellbeing_bottom_right" />
<!-- QSB IDs. DO not change -->
<item type="id" name="search_container_workspace" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ef0f0d8..207d246 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -41,6 +41,7 @@
<!-- Title for an option to enter split screen mode for a given app -->
<string name="recent_task_option_split_screen">Split screen</string>
<string name="split_app_info_accessibility">App info for %1$s</string>
+ <string name="split_app_usage_settings">Usage settings for %1$s</string>
<!-- App pairs -->
<string name="save_app_pair">Save app pair</string>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b89d05e..2b30dc4 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -96,9 +96,7 @@
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
-import static com.android.launcher3.testing.shared.TestProtocol.CLOCK_ICON_DRAWABLE_LEAKING;
import static com.android.launcher3.testing.shared.TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE;
-import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
import static com.android.launcher3.util.SettingsCache.TOUCHPAD_NATURAL_SCROLLING;
@@ -163,6 +161,7 @@
import androidx.annotation.StringRes;
import androidx.annotation.UiThread;
import androidx.annotation.VisibleForTesting;
+import androidx.core.os.BuildCompat;
import androidx.window.embedding.RuleController;
import com.android.launcher3.DropTarget.DragObject;
@@ -423,7 +422,6 @@
@Override
@TargetApi(Build.VERSION_CODES.S)
protected void onCreate(Bundle savedInstanceState) {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onCreate: instance=" + this);
mStartupLatencyLogger = createStartupLatencyLogger(
sIsNewProcess
? LockedUserState.get(this).isUserUnlockedAtLauncherStartup()
@@ -589,7 +587,8 @@
setTitle(R.string.home_screen);
mStartupLatencyLogger.logEnd(LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE);
- if (com.android.launcher3.Flags.enableTwoPaneLauncherSettings()) {
+ if (BuildCompat.isAtLeastV()
+ && com.android.launcher3.Flags.enableTwoPaneLauncherSettings()) {
RuleController.getInstance(this).setRules(
RuleController.parseRules(this, R.xml.split_configuration));
}
@@ -1082,7 +1081,6 @@
@Override
protected void onStart() {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onStart: instance=" + this);
TraceHelper.INSTANCE.beginSection(ON_START_EVT);
super.onStart();
if (!mDeferOverlayCallbacks) {
@@ -1096,7 +1094,6 @@
@Override
@CallSuper
protected void onDeferredResumed() {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onDeferredResumed: instance=" + this);
logStopAndResume(true /* isResume */);
// Process any items that were added while Launcher was away.
@@ -1268,11 +1265,7 @@
}
// Set screen title for Talkback
- if (state == ALL_APPS) {
- setTitle(R.string.all_apps_label);
- } else {
- setTitle(R.string.home_screen);
- }
+ setTitle(state.getTitle());
}
/**
@@ -1284,7 +1277,6 @@
@Override
protected void onResume() {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onResume: instance=" + this);
TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT);
super.onResume();
@@ -1300,7 +1292,6 @@
@Override
protected void onPause() {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onPause: instance=" + this);
// Ensure that items added to Launcher are queued until Launcher returns
ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED);
@@ -1783,7 +1774,6 @@
@Override
public void onDestroy() {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onDestroy: instance=" + this);
super.onDestroy();
ACTIVITY_TRACKER.onActivityDestroyed(this);
@@ -2793,7 +2783,7 @@
}
private void updateDisallowBack() {
- if (Flags.enableDesktopWindowingMode()) {
+ if (BuildCompat.isAtLeastV() && Flags.enableDesktopWindowingMode()) {
// TODO(b/330183377) disable back in launcher when when we productionize
return;
}
@@ -3150,4 +3140,4 @@
}
// End of Getters and Setters
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index a4ae1c8..3b8ff62 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -39,6 +39,7 @@
import android.util.Log;
import androidx.annotation.Nullable;
+import androidx.core.os.BuildCompat;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.IconCache;
@@ -107,7 +108,7 @@
mOnTerminateCallback.add(() ->
mContext.getSystemService(LauncherApps.class).unregisterCallback(callbacks));
- if (Flags.enableSupportForArchiving()) {
+ if (BuildCompat.isAtLeastV() && Flags.enableSupportForArchiving()) {
ArchiveCompatibilityParams params = new ArchiveCompatibilityParams();
params.setEnableUnarchivalConfirmation(false);
launcherApps.setArchiveCompatibility(params);
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 72a3c53..d2d56f2 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -38,6 +38,7 @@
import android.view.animation.Interpolator;
import androidx.annotation.FloatRange;
+import androidx.annotation.StringRes;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
@@ -369,6 +370,10 @@
return launcher.getWorkspace().getCurrentPageDescription();
}
+ public @StringRes int getTitle() {
+ return R.string.home_screen;
+ }
+
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
if ((this != NORMAL && this != HINT_STATE)
|| !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index a67a362..98ca420 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -300,7 +300,7 @@
case VIEW_TYPE_PRIVATE_SPACE_HEADER:
RelativeLayout psHeaderLayout = holder.itemView.findViewById(
R.id.ps_header_layout);
- mApps.getPrivateProfileManager().addPrivateSpaceHeaderViewElements(psHeaderLayout);
+ mApps.getPrivateProfileManager().bindPrivateSpaceHeaderViewElements(psHeaderLayout);
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
int roundRegions = ROUND_TOP_LEFT | ROUND_TOP_RIGHT;
if (mApps.getPrivateProfileManager().getCurrentState() == STATE_DISABLED) {
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index a620490..32b5cfa 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -30,7 +30,6 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_TAP;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP;
@@ -52,7 +51,6 @@
import android.os.UserManager;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -104,6 +102,7 @@
private static final int LOCK_TEXT_OPACITY_DELAY = 500;
private static final int MASK_VIEW_DELAY = 400;
private static final int NO_DELAY = 0;
+ private static final int CONTAINER_OPACITY_DURATION = 150;
private final ActivityAllAppsContainerView<?> mAllApps;
private final Predicate<UserHandle> mPrivateProfileMatcher;
private final int mPsHeaderHeight;
@@ -196,23 +195,6 @@
mAllApps.mAH.get(MAIN).mAdapter.notifyItemInserted(adapterItems.size() - 1);
}
- /**
- * Disables quiet mode for Private Space User Profile.
- * When called from search, a runnable is set and executed in the {@link #reset()} method, when
- * Launcher receives update about profile availability.
- * The runnable is only executed once, and reset after execution.
- * In case the method is called again, before the previously set runnable was executed,
- * the runnable will be updated.
- */
- public void unlockPrivateProfile() {
- setQuietMode(false);
- }
-
- /** Enables quiet mode for Private Space User Profile. */
- void lockPrivateProfile() {
- setQuietMode(true);
- }
-
/** Whether private profile should be hidden on Launcher. */
public boolean isPrivateSpaceHidden() {
return getCurrentState() == STATE_DISABLED && SettingsCache.INSTANCE
@@ -245,30 +227,6 @@
resetPrivateSpaceDecorator(updatedState);
}
- /**
- * Opens the Private Space Settings Page.
- *
- * @param view the view that was clicked to open the settings page and which will be the same
- * view to animate back. Otherwise if there is no view, simply start the activity.
- */
- public void openPrivateSpaceSettings(View view) {
- if (mPrivateSpaceSettingsAvailable) {
- Context context = mAllApps.getContext();
- Intent intent = ApiWrapper.INSTANCE.get(context).getPrivateSpaceSettingsIntent();
- if (view == null) {
- context.startActivity(intent);
- return;
- }
- ActivityContext activityContext = ActivityContext.lookupContext(context);
- AppInfo itemInfo = new AppInfo();
- itemInfo.id = CONTAINER_PRIVATESPACE;
- itemInfo.componentName = intent.getComponent();
- itemInfo.container = CONTAINER_PRIVATESPACE;
- view.setTag(itemInfo);
- activityContext.startActivitySafely(view, intent, itemInfo);
- }
- }
-
/** Returns whether or not Private Space Settings Page is available. */
public boolean isPrivateSpaceSettingsAvailable() {
return mPrivateSpaceSettingsAvailable;
@@ -377,7 +335,7 @@
}
/** Add Private Space Header view elements based upon {@link UserProfileState} */
- public void addPrivateSpaceHeaderViewElements(RelativeLayout parent) {
+ public void bindPrivateSpaceHeaderViewElements(RelativeLayout parent) {
mPSHeader = parent;
if (mOnPSHeaderAdded != null) {
MAIN_EXECUTOR.execute(mOnPSHeaderAdded);
@@ -395,27 +353,27 @@
//Add quietMode image and action for lock/unlock button
ViewGroup lockButton = mPSHeader.findViewById(R.id.ps_lock_unlock_button);
assert lockButton != null;
- addLockButton(lockButton);
+ updateLockButton(lockButton);
//Trigger lock/unlock action from header.
- addHeaderOnClickListener(mPSHeader);
+ updateHeaderOnClickListener(mPSHeader);
//Add image and action for private space settings button
- ImageButton settingsButton = mPSHeader.findViewById(R.id.ps_settings_button);
+ PrivateSpaceSettingsButton settingsButton = mPSHeader.findViewById(R.id.ps_settings_button);
assert settingsButton != null;
- addPrivateSpaceSettingsButton(settingsButton);
+ updatePrivateSpaceSettingsButton(settingsButton);
//Add image for private space transitioning view
ImageView transitionView = parent.findViewById(R.id.ps_transition_image);
assert transitionView != null;
- addTransitionImage(transitionView);
+ updateTransitionImage(transitionView);
}
/**
* Adds the quietModeButton and attach onClickListener for the header to animate different
* states when clicked.
*/
- private void addLockButton(ViewGroup lockButton) {
+ private void updateLockButton(ViewGroup lockButton) {
TextView lockText = lockButton.findViewById(R.id.lock_text);
switch (getCurrentState()) {
case STATE_ENABLED -> {
@@ -434,7 +392,7 @@
}
}
- private void addHeaderOnClickListener(RelativeLayout header) {
+ private void updateHeaderOnClickListener(RelativeLayout header) {
if (getCurrentState() == STATE_DISABLED) {
header.setOnClickListener(view -> lockingAction(/* lock */ false));
header.setClickable(true);
@@ -452,28 +410,19 @@
/** Sets the enablement of the profile when header or button is clicked. */
private void lockingAction(boolean lock) {
logEvents(lock ? LAUNCHER_PRIVATE_SPACE_LOCK_TAP : LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP);
- if (lock) {
- lockPrivateProfile();
- } else {
- unlockPrivateProfile();
- }
+ setQuietMode(lock);
}
- private void addPrivateSpaceSettingsButton(ImageButton settingsButton) {
+ private void updatePrivateSpaceSettingsButton(PrivateSpaceSettingsButton settingsButton) {
if (getCurrentState() == STATE_ENABLED
&& isPrivateSpaceSettingsAvailable()) {
settingsButton.setVisibility(VISIBLE);
- settingsButton.setOnClickListener(
- view -> {
- logEvents(LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP);
- openPrivateSpaceSettings(view);
- });
} else {
settingsButton.setVisibility(GONE);
}
}
- private void addTransitionImage(ImageView transitionImage) {
+ private void updateTransitionImage(ImageView transitionImage) {
if (getCurrentState() == STATE_TRANSITION) {
transitionImage.setVisibility(VISIBLE);
} else {
@@ -497,7 +446,10 @@
return LinearSmoothScroller.SNAP_TO_END;
}
};
- smoothScroller.setTargetPosition(i);
+ // If privateSpaceHidden() then the entire container decorator will be invisible and
+ // we can directly move to an element above the header. There should always be one
+ // element, as PS is present in the bottom of All Apps.
+ smoothScroller.setTargetPosition(isPrivateSpaceHidden() ? i - 1 : i);
RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
if (layoutManager != null) {
startAnimationScroll(allAppsRecyclerView, layoutManager, smoothScroller);
@@ -619,8 +571,11 @@
float newAlpha = (float) valueAnimator.getAnimatedValue();
for (int i = 0; i < allAppsAdapterItems.size(); i++) {
BaseAllAppsAdapter.AdapterItem currentItem = allAppsAdapterItems.get(i);
+ // When not hidden: Fade all PS items except header.
+ // When hidden: Fade all items.
if (isPrivateSpaceItem(currentItem) &&
- currentItem.viewType != VIEW_TYPE_PRIVATE_SPACE_HEADER) {
+ (currentItem.viewType != VIEW_TYPE_PRIVATE_SPACE_HEADER
+ || isPrivateSpaceHidden())) {
RecyclerView.ViewHolder viewHolder =
allAppsRecyclerView.findViewHolderForAdapterPosition(i);
if (viewHolder != null) {
@@ -702,10 +657,9 @@
translateFloatingMaskView(false));
} else {
if (isPrivateSpaceHidden()) {
- animatorSet.playSequentially(translateFloatingMaskView(false),
- animateAlphaOfIcons(false),
- animateCollapseAnimation(),
- fadeOutHeaderAlpha());
+ animatorSet.playSequentially(animateAlphaOfIcons(false),
+ animateAlphaOfPrivateSpaceContainer(),
+ animateCollapseAnimation());
} else {
animatorSet.playSequentially(translateFloatingMaskView(true),
animateAlphaOfIcons(false),
@@ -715,29 +669,30 @@
animatorSet.start();
}
- /** Fades out the private space container. */
- private ValueAnimator fadeOutHeaderAlpha() {
- if (mPSHeader == null) {
- return new ValueAnimator();
- }
- float from = 1;
- float to = 0;
- ValueAnimator alphaAnim = ObjectAnimator.ofFloat(from, to);
- alphaAnim.setDuration(EXPAND_COLLAPSE_DURATION);
- alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- if (mPSHeader != null) {
- mPSHeader.setAlpha((float) valueAnimator.getAnimatedValue());
+ /** Fades out the private space container (defined by its items' decorators). */
+ private ValueAnimator animateAlphaOfPrivateSpaceContainer() {
+ int from = 255; // 100% opacity.
+ int to = 0; // No opacity.
+ ValueAnimator alphaAnim = ObjectAnimator.ofInt(from, to);
+ AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
+ List<BaseAllAppsAdapter.AdapterItem> allAppsAdapterItems =
+ allAppsRecyclerView.getApps().getAdapterItems();
+ alphaAnim.setDuration(CONTAINER_OPACITY_DURATION);
+ alphaAnim.addUpdateListener(valueAnimator -> {
+ for (BaseAllAppsAdapter.AdapterItem currentItem : allAppsAdapterItems) {
+ if (isPrivateSpaceItem(currentItem)) {
+ currentItem.setDecorationFillAlpha((int) valueAnimator.getAnimatedValue());
}
}
+ // Invalidate the parent view, to redraw the decorations with changed alpha.
+ allAppsRecyclerView.invalidate();
});
return alphaAnim;
}
/** Fades out the private space container. */
private ValueAnimator translateFloatingMaskView(boolean animateIn) {
- if (!Flags.privateSpaceFloatingMaskView() || mFloatingMaskView == null) {
+ if (!Flags.privateSpaceAddFloatingMaskView() || mFloatingMaskView == null) {
return new ValueAnimator();
}
// Translate base on the height amount. Translates out on expand and in on collapse.
@@ -848,7 +803,7 @@
}
private void attachFloatingMaskView(boolean expand) {
- if (!Flags.privateSpaceFloatingMaskView()) {
+ if (!Flags.privateSpaceAddFloatingMaskView()) {
return;
}
mFloatingMaskView = (FloatingMaskView) mAllApps.getLayoutInflater().inflate(
diff --git a/src/com/android/launcher3/allapps/PrivateSpaceSettingsButton.java b/src/com/android/launcher3/allapps/PrivateSpaceSettingsButton.java
new file mode 100644
index 0000000..43e42ff
--- /dev/null
+++ b/src/com/android/launcher3/allapps/PrivateSpaceSettingsButton.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 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.allapps;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PRIVATESPACE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.util.ApiWrapper;
+import com.android.launcher3.views.ActivityContext;
+
+public class PrivateSpaceSettingsButton extends ImageButton implements View.OnClickListener {
+
+ private final ActivityContext mActivityContext;
+ private final StatsLogManager mStatsLogManager;
+ private final Intent mPrivateSpaceSettingsIntent;
+
+ public PrivateSpaceSettingsButton(Context context) {
+ this(context, null, 0);
+ }
+
+ public PrivateSpaceSettingsButton(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PrivateSpaceSettingsButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mActivityContext = ActivityContext.lookupContext(context);
+ mStatsLogManager = mActivityContext.getStatsLogManager();
+ mPrivateSpaceSettingsIntent =
+ ApiWrapper.INSTANCE.get(context).getPrivateSpaceSettingsIntent();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View view) {
+ mStatsLogManager.logger().log(LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP);
+ AppInfo privateSpaceSettingsItemInfo = createPrivateSpaceSettingsAppInfo();
+ view.setTag(privateSpaceSettingsItemInfo);
+ mActivityContext.startActivitySafely(
+ view,
+ mPrivateSpaceSettingsIntent,
+ privateSpaceSettingsItemInfo);
+ }
+
+ AppInfo createPrivateSpaceSettingsAppInfo() {
+ AppInfo itemInfo = new AppInfo();
+ itemInfo.id = CONTAINER_PRIVATESPACE;
+ if (mPrivateSpaceSettingsIntent != null) {
+ itemInfo.componentName = mPrivateSpaceSettingsIntent.getComponent();
+ }
+ itemInfo.container = CONTAINER_PRIVATESPACE;
+ return itemInfo;
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index ec0a222..85eb39b 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -91,7 +91,8 @@
*/
@TargetApi(Build.VERSION_CODES.O)
public class AddItemActivity extends BaseActivity
- implements OnLongClickListener, OnTouchListener, AbstractSlideInView.OnCloseListener {
+ implements OnLongClickListener, OnTouchListener, AbstractSlideInView.OnCloseListener,
+ WidgetCell.PreviewReadyListener {
private static final int SHADOW_SIZE = 10;
@@ -142,6 +143,7 @@
mDragLayer = findViewById(R.id.add_item_drag_layer);
mDragLayer.recreateControllers();
mWidgetCell = findViewById(R.id.widget_cell);
+ mWidgetCell.addPreviewReadyListener(this);
mAccessibilityManager =
getApplicationContext().getSystemService(AccessibilityManager.class);
@@ -454,4 +456,11 @@
.withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
.log(command);
}
+
+ @Override
+ public void onPreviewAvailable() {
+ // Set the preview height based on "the only" widget's preview.
+ mWidgetCell.setParentAlignedPreviewHeight(mWidgetCell.getPreviewContentHeight());
+ mWidgetCell.post(mWidgetCell::requestLayout);
+ }
}
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
index f3708a2..29fc613 100644
--- a/src/com/android/launcher3/dragndrop/LauncherDragController.java
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -28,6 +28,7 @@
import android.view.View;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DragSource;
@@ -36,6 +37,7 @@
import com.android.launcher3.R;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.widget.util.WidgetDragScaleUtils;
/**
* Drag controller for Launcher activity
@@ -43,7 +45,6 @@
public class LauncherDragController extends DragController<Launcher> {
private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
-
private final FlingToDeleteHelper mFlingToDeleteHelper;
public LauncherDragController(Launcher launcher) {
@@ -92,8 +93,13 @@
&& !mOptions.preDragCondition.shouldStartDrag(0);
final Resources res = mActivity.getResources();
- final float scaleDps = mIsInPreDrag
- ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
+
+ final float scalePx;
+ if (originalView.getViewType() == DraggableView.DRAGGABLE_WIDGET) {
+ scalePx = mIsInPreDrag ? 0f : getWidgetDragScalePx(drawable, view, dragInfo);
+ } else {
+ scalePx = mIsInPreDrag ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
+ }
final DragView dragView = mDragObject.dragView = drawable != null
? new LauncherDragView(
mActivity,
@@ -102,7 +108,7 @@
registrationY,
initialDragViewScale,
dragViewScaleOnDrop,
- scaleDps)
+ scalePx)
: new LauncherDragView(
mActivity,
view,
@@ -112,7 +118,7 @@
registrationY,
initialDragViewScale,
dragViewScaleOnDrop,
- scaleDps);
+ scalePx);
dragView.setItemInfo(dragInfo);
mDragObject.dragComplete = false;
@@ -157,6 +163,29 @@
return dragView;
}
+
+ /**
+ * Returns the scale in terms of pixels (to be applied on width) to scale the preview
+ * during drag and drop.
+ */
+ @VisibleForTesting
+ float getWidgetDragScalePx(@Nullable Drawable drawable, @Nullable View view,
+ ItemInfo dragInfo) {
+ float draggedViewWidthPx = 0;
+ float draggedViewHeightPx = 0;
+
+ if (view != null) {
+ draggedViewWidthPx = view.getMeasuredWidth();
+ draggedViewHeightPx = view.getMeasuredHeight();
+ } else if (drawable != null) {
+ draggedViewWidthPx = drawable.getIntrinsicWidth();
+ draggedViewHeightPx = drawable.getIntrinsicHeight();
+ }
+
+ return WidgetDragScaleUtils.getWidgetDragScalePx(mActivity, mActivity.getDeviceProfile(),
+ draggedViewWidthPx, draggedViewHeightPx, dragInfo);
+ }
+
@Override
protected void exitDrag() {
if (!mActivity.isInState(EDIT_MODE)) {
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 04d8ac0..44e448e 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -57,6 +57,7 @@
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.cache.BaseIconCache;
import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.IconRequestInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -641,4 +642,10 @@
void reapplyItemInfo(ItemInfoWithIcon info);
}
+
+ /** Log persistently to FileLog.d for debugging. */
+ @Override
+ protected void logdPersistently(String tag, String message, @Nullable Exception e) {
+ FileLog.d(tag, message, e);
+ }
}
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 8e53aff..d6b41b0 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -61,7 +61,7 @@
*/
public class QsbContainerView extends FrameLayout {
- public static final String SEARCH_PROVIDER_SETTINGS_KEY = "SEARCH_PROVIDER_PACKAGE_NAME";
+ public static final String SEARCH_ENGINE_SETTINGS_KEY = "selected_search_engine";
/**
* Returns the package name for user configured search provider or from searchManager
@@ -71,8 +71,8 @@
@WorkerThread
@Nullable
public static String getSearchWidgetPackageName(@NonNull Context context) {
- String providerPkg = Settings.Global.getString(context.getContentResolver(),
- SEARCH_PROVIDER_SETTINGS_KEY);
+ String providerPkg = Settings.Secure.getString(context.getContentResolver(),
+ SEARCH_ENGINE_SETTINGS_KEY);
if (providerPkg == null) {
SearchManager searchManager = context.getSystemService(SearchManager.class);
ComponentName componentName = searchManager.getGlobalSearchActivity();
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index cdbd0c0..df8f635 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -109,13 +109,6 @@
private float mLastTouchY;
private boolean mIsDragging;
- /**
- * Tracks whether a keyboard hide request has been sent due to downward scrolling.
- * <p>
- * Set to true when scrolling down and reset when scrolling up to prevents redundant hide
- * requests during continuous downward scrolls.
- */
- private boolean mRequestedHideKeyboard;
private boolean mIsThumbDetached;
private final boolean mCanThumbDetach;
private boolean mIgnoreDragGesture;
@@ -248,7 +241,6 @@
public boolean handleTouchEvent(MotionEvent ev, Point offset) {
int x = (int) ev.getX() - offset.x;
int y = (int) ev.getY() - offset.y;
- ActivityContext activityContext = ActivityContext.lookupContext(getContext());
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
@@ -256,7 +248,6 @@
mDownX = x;
mDownY = mLastY = y;
mDownTimeStampMillis = ev.getDownTime();
- mRequestedHideKeyboard = false;
if ((Math.abs(mDy) < mDeltaThreshold &&
mRv.getScrollState() != SCROLL_STATE_IDLE)) {
@@ -269,15 +260,6 @@
}
break;
case MotionEvent.ACTION_MOVE:
- if (y > mLastY) {
- if (!mRequestedHideKeyboard) {
- activityContext.hideKeyboard();
- }
- mRequestedHideKeyboard = true;
- } else {
- mRequestedHideKeyboard = false;
- }
-
mLastY = y;
int absDeltaY = Math.abs(y - mDownY);
int absDeltaX = Math.abs(x - mDownX);
@@ -312,6 +294,7 @@
}
private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
+ ActivityContext.lookupContext(getContext()).hideKeyboard();
mIsDragging = true;
if (mCanThumbDetach) {
mIsThumbDetached = true;
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index eac2ce7..2bb485a 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -103,6 +103,8 @@
private Size mWidgetSize;
private final DatabaseWidgetPreviewLoader mWidgetPreviewLoader;
+ @Nullable
+ private PreviewReadyListener mPreviewReadyListener = null;
protected CancellableTask mActiveRequest;
private boolean mAnimatePreview = true;
@@ -118,7 +120,8 @@
private CancellableTask mIconLoadRequest;
private boolean mIsShowingAddButton = false;
-
+ // Height enforced by the parent to align all widget cells displayed by it.
+ private int mParentAlignedPreviewHeight;
public WidgetCell(Context context) {
this(context, null);
}
@@ -190,6 +193,8 @@
mWidgetDims.setText(null);
mWidgetDescription.setText(null);
mWidgetDescription.setVisibility(GONE);
+ mPreviewReadyListener = null;
+ mParentAlignedPreviewHeight = 0;
showDescription(true);
showDimensions(true);
@@ -338,8 +343,8 @@
private void updateAppWidgetHostScale(NavigableAppWidgetHostView view) {
// Scale the content such that all of the content is visible
- int contentWidth = view.getWidth();
- int contentHeight = view.getHeight();
+ float contentWidth = view.getWidth();
+ float contentHeight = view.getHeight();
if (view.getChildCount() == 1) {
View content = view.getChildAt(0);
@@ -359,6 +364,12 @@
mAppWidgetHostViewScale = Math.min(pWidth / contentWidth, pHeight / contentHeight);
}
view.setScaleToFit(mAppWidgetHostViewScale);
+
+ // layout based previews maybe ready at this point to inspect their inner height.
+ if (mPreviewReadyListener != null) {
+ mPreviewReadyListener.onPreviewAvailable();
+ mPreviewReadyListener = null;
+ }
}
public WidgetImageView getWidgetView() {
@@ -384,6 +395,12 @@
removeView(mAppWidgetHostViewPreview);
mAppWidgetHostViewPreview = null;
}
+
+ // Drawables of the image previews are available at this point to measure.
+ if (mPreviewReadyListener != null) {
+ mPreviewReadyListener.onPreviewAvailable();
+ mPreviewReadyListener = null;
+ }
}
if (mAnimatePreview) {
@@ -489,14 +506,20 @@
// mPreviewContainerScale ensures the needed scaling with respect to original widget size.
mAppWidgetHostViewScale = mPreviewContainerScale;
containerLp.width = mPreviewContainerSize.getWidth();
- containerLp.height = mPreviewContainerSize.getHeight();
+ int height = mPreviewContainerSize.getHeight();
// If we don't have enough available width, scale the preview container to fit.
if (containerLp.width > maxWidth) {
containerLp.width = maxWidth;
mAppWidgetHostViewScale = (float) containerLp.width / mPreviewContainerSize.getWidth();
- containerLp.height = Math.round(
- mPreviewContainerSize.getHeight() * mAppWidgetHostViewScale);
+ height = Math.round(mPreviewContainerSize.getHeight() * mAppWidgetHostViewScale);
+ }
+
+ // Use parent aligned height in set.
+ if (mParentAlignedPreviewHeight > 0) {
+ containerLp.height = Math.min(height, mParentAlignedPreviewHeight);
+ } else {
+ containerLp.height = height;
}
// No need to call mWidgetImageContainer.setLayoutParams as we are in measure pass
@@ -513,6 +536,42 @@
}
/**
+ * Sets the height of the preview as adjusted by the parent to have this cell's content aligned
+ * with other cells displayed by the parent.
+ */
+ public void setParentAlignedPreviewHeight(int previewHeight) {
+ mParentAlignedPreviewHeight = previewHeight;
+ }
+
+ /**
+ * Returns the height of the preview without any empty space.
+ * In case of appwidget host views, it returns the height of first child. This way, if preview
+ * view provided by an app doesn't fill bounds, this will return actual height without white
+ * space.
+ */
+ public int getPreviewContentHeight() {
+ // By default assume scaled height.
+ int height = Math.round(mPreviewContainerScale * mWidgetSize.getHeight());
+
+ if (mWidgetImage != null && mWidgetImage.getDrawable() != null) {
+ // getBitmapBounds returns the scaled bounds.
+ Rect bitmapBounds = mWidgetImage.getBitmapBounds();
+ height = bitmapBounds.height();
+ } else if (mAppWidgetHostViewPreview != null
+ && mAppWidgetHostViewPreview.getChildCount() == 1) {
+ int contentHeight = Math.round(
+ mPreviewContainerScale * mWidgetSize.getHeight());
+ int previewInnerHeight = Math.round(
+ mAppWidgetHostViewScale * mAppWidgetHostViewPreview.getChildAt(
+ 0).getMeasuredHeight());
+ // Use either of the inner scaled height or the scaled widget height
+ height = Math.min(contentHeight, previewInnerHeight);
+ }
+
+ return height;
+ }
+
+ /**
* Loads a high resolution package icon to show next to the widget title.
*/
public void loadHighResPackageIcon() {
@@ -651,4 +710,19 @@
}
return false;
}
+
+ /**
+ * Listener to notify when previews are available.
+ */
+ public void addPreviewReadyListener(PreviewReadyListener previewReadyListener) {
+ mPreviewReadyListener = previewReadyListener;
+ }
+
+ /**
+ * Listener interface for subscribers to listen to preview's availability.
+ */
+ public interface PreviewReadyListener {
+ /** Handler on to invoke when previews are available. */
+ void onPreviewAvailable();
+ }
}
diff --git a/src/com/android/launcher3/widget/WidgetTableRow.java b/src/com/android/launcher3/widget/WidgetTableRow.java
new file mode 100644
index 0000000..a5312ce
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetTableRow.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.TableRow;
+
+/**
+ * A row of {@link WidgetCell}s that can be displayed in a table.
+ */
+public class WidgetTableRow extends TableRow implements WidgetCell.PreviewReadyListener {
+ private int mNumOfReadyCells;
+ private int mNumOfCells;
+ private int mResizeDelay;
+
+ public WidgetTableRow(Context context) {
+ super(context);
+ }
+ public WidgetTableRow(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void onPreviewAvailable() {
+ mNumOfReadyCells++;
+
+ // Once all previews are loaded, find max visible height and adjust the preview containers.
+ if (mNumOfReadyCells == mNumOfCells) {
+ resize();
+ }
+ }
+
+ private void resize() {
+ int previewHeight = 0;
+ // get the maximum height of each widget preview
+ for (int i = 0; i < getChildCount(); i++) {
+ WidgetCell widgetCell = (WidgetCell) getChildAt(i);
+ previewHeight = Math.max(widgetCell.getPreviewContentHeight(), previewHeight);
+ }
+ if (mResizeDelay > 0) {
+ postDelayed(() -> setAlpha(1f), mResizeDelay);
+ }
+ if (previewHeight > 0) {
+ for (int i = 0; i < getChildCount(); i++) {
+ WidgetCell widgetCell = (WidgetCell) getChildAt(i);
+ widgetCell.setParentAlignedPreviewHeight(previewHeight);
+ widgetCell.postDelayed(widgetCell::requestLayout, mResizeDelay);
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ }
+
+ /**
+ * Sets up the row to display the provided number of numOfCells.
+ *
+ * @param numOfCells number of numOfCells in the row
+ * @param resizeDelayMs time to wait in millis before making any layout size adjustments e.g. we
+ * want to wait for list expand collapse animation before resizing the
+ * cell previews.
+ */
+ public void setupRow(int numOfCells, int resizeDelayMs) {
+ mNumOfCells = numOfCells;
+ mNumOfReadyCells = 0;
+
+ mResizeDelay = resizeDelayMs;
+ // For delayed resize, reveal contents only after resize is done.
+ if (mResizeDelay > 0) {
+ setAlpha(0);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 4ea2426..894099d 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -31,7 +31,6 @@
import android.view.animation.Interpolator;
import android.widget.ScrollView;
import android.widget.TableLayout;
-import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.Px;
@@ -137,8 +136,9 @@
mActivityContext.getDeviceProfile(), mMaxHorizontalSpan,
mWidgetCellHorizontalPadding)
.forEach(row -> {
- TableRow tableRow = new TableRow(getContext());
+ WidgetTableRow tableRow = new WidgetTableRow(getContext());
tableRow.setGravity(Gravity.TOP);
+ tableRow.setupRow(row.size(), /*resizeDelayMs=*/ 0);
row.forEach(widgetItem -> {
WidgetCell widget = addItemCell(tableRow);
widget.applyFromCellItem(widgetItem);
@@ -163,9 +163,10 @@
return super.onControllerInterceptTouchEvent(ev);
}
- protected WidgetCell addItemCell(ViewGroup parent) {
+ protected WidgetCell addItemCell(WidgetTableRow parent) {
WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext())
.inflate(R.layout.widget_cell, parent, false);
+ widget.addPreviewReadyListener(parent);
widget.setOnClickListener(this);
View previewContainer = widget.findViewById(R.id.widget_preview_container);
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
index 4f73e66..9260af9 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -53,6 +53,7 @@
*/
public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
private @Px float mAvailableHeight = Float.MAX_VALUE;
+ private @Px float mAvailableWidth = 0;
private static final String INITIALLY_DISPLAYED_WIDGETS_STATE_KEY =
"widgetRecommendationsView:mDisplayedWidgets";
private static final int MAX_CATEGORIES = 3;
@@ -152,6 +153,7 @@
final @Px float availableHeight, final @Px int availableWidth,
final @Px int cellPadding) {
this.mAvailableHeight = availableHeight;
+ this.mAvailableWidth = availableWidth;
clear();
Set<ComponentName> displayedWidgets = maybeDisplayInTable(recommendedWidgets,
@@ -187,6 +189,7 @@
DeviceProfile deviceProfile, final @Px float availableHeight,
final @Px int availableWidth, final @Px int cellPadding, final int requestedPage) {
this.mAvailableHeight = availableHeight;
+ this.mAvailableWidth = availableWidth;
Context context = getContext();
// For purpose of recommendations section, we don't want paging dots to be halved in two
// pane display, so, we always provide isTwoPanels = "false".
@@ -280,17 +283,24 @@
boolean hasMultiplePages = getChildCount() > 0;
if (hasMultiplePages) {
- int finalWidth = MeasureSpec.getSize(widthMeasureSpec);
int desiredHeight = 0;
+ int desiredWidth = Math.round(mAvailableWidth);
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
- measureChild(child, widthMeasureSpec, heightMeasureSpec);
+ // Measure children based on available height and width.
+ measureChild(child,
+ MeasureSpec.makeMeasureSpec(desiredWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(Math.round(mAvailableHeight),
+ MeasureSpec.AT_MOST));
// Use height of tallest child as we have limited height.
- desiredHeight = Math.max(desiredHeight, child.getMeasuredHeight());
+ int childHeight = child.getMeasuredHeight();
+ desiredHeight = Math.max(desiredHeight, childHeight);
}
int finalHeight = resolveSizeAndState(desiredHeight, heightMeasureSpec, 0);
+ int finalWidth = resolveSizeAndState(desiredWidth, widthMeasureSpec, 0);
+
setMeasuredDimension(finalWidth, finalHeight);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 6aaa7d2..3be6faa 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
-import static com.android.launcher3.widget.picker.WidgetsListAdapter.VIEW_TYPE_WIDGETS_LIST;
import android.animation.Animator;
import android.content.Context;
@@ -1023,35 +1022,7 @@
default:
break;
}
- mWidgetsListItemAnimator = new DefaultItemAnimator() {
- @Override
- public boolean animateChange(RecyclerView.ViewHolder oldHolder,
- RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft,
- int toTop) {
- // As we expand an item, the content / widgets list that appears (with add
- // event) also gets change events as its previews load asynchronously. The
- // super implementation of animateChange cancels the animations on it - breaking
- // the "add animation". Instead, here, we skip "change" animation for content
- // list - because we want it to either appear or disappear. And, the previews
- // themselves have their own animation when loaded, so, we don't need change
- // animations for them anyway. Below, we do-nothing.
- if (oldHolder.getItemViewType() == VIEW_TYPE_WIDGETS_LIST) {
- dispatchChangeStarting(oldHolder, true);
- dispatchChangeFinished(oldHolder, true);
- return true;
- }
- return super.animateChange(oldHolder, newHolder, fromLeft, fromTop, toLeft,
- toTop);
- }
- };
- // Disable change animations because it disrupts the item focus upon adapter item
- // change.
- mWidgetsListItemAnimator.setSupportsChangeAnimations(false);
- // Make the moves a bit faster, so that the amount of time for which user sees the
- // bottom-sheet background before "add" animation starts is less making it smoother.
- mWidgetsListItemAnimator.setChangeDuration(90);
- mWidgetsListItemAnimator.setMoveDuration(90);
- mWidgetsListItemAnimator.setAddDuration(300);
+ mWidgetsListItemAnimator = new WidgetsListItemAnimator();
}
private int getEmptySpaceHeight() {
@@ -1065,7 +1036,7 @@
mWidgetsRecyclerView.setClipChildren(false);
mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
mWidgetsRecyclerView.bindFastScrollbar(mFastScroller);
- mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator);
+ mWidgetsRecyclerView.setItemAnimator(isTwoPane() ? null : mWidgetsListItemAnimator);
mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
if (!isTwoPane()) {
mWidgetsRecyclerView.setEdgeEffectFactory(
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index d373a3b..d164dd0 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.widget.picker;
+import static android.animation.ValueAnimator.areAnimatorsEnabled;
+
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
@@ -201,7 +203,7 @@
public void reapplyItemInfo(ItemInfoWithIcon info) {
if (getTag() == info) {
mIconLoadRequest = null;
- mEnableIconUpdateAnimation = true;
+ mEnableIconUpdateAnimation = areAnimatorsEnabled();
// Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
info.bitmap.icon.prepareToDraw();
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListItemAnimator.java b/src/com/android/launcher3/widget/picker/WidgetsListItemAnimator.java
new file mode 100644
index 0000000..854700f
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListItemAnimator.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 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.widget.picker;
+
+import static com.android.launcher3.widget.picker.WidgetsListAdapter.VIEW_TYPE_WIDGETS_LIST;
+
+import androidx.recyclerview.widget.DefaultItemAnimator;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class WidgetsListItemAnimator extends DefaultItemAnimator {
+ public static final int CHANGE_DURATION_MS = 90;
+ public static final int MOVE_DURATION_MS = 90;
+ public static final int ADD_DURATION_MS = 120;
+
+ public WidgetsListItemAnimator() {
+ super();
+
+ // Disable change animations because it disrupts the item focus upon adapter item
+ // change.
+ setSupportsChangeAnimations(false);
+ // Make the moves a bit faster, so that the amount of time for which user sees the
+ // bottom-sheet background before "add" animation starts is less making it smoother.
+ setChangeDuration(CHANGE_DURATION_MS);
+ setMoveDuration(MOVE_DURATION_MS);
+ setAddDuration(ADD_DURATION_MS);
+ }
+ @Override
+ public boolean animateChange(RecyclerView.ViewHolder oldHolder,
+ RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft,
+ int toTop) {
+ // As we expand an item, the content / widgets list that appears (with add
+ // event) also gets change events as its previews load asynchronously. The
+ // super implementation of animateChange cancels the animations on it - breaking
+ // the "add animation". Instead, here, we skip "change" animation for content
+ // list - because we want it to either appear or disappear. And, the previews
+ // themselves have their own animation when loaded, so, we don't need change
+ // animations for them anyway. Below, we do-nothing.
+ if (oldHolder.getItemViewType() == VIEW_TYPE_WIDGETS_LIST) {
+ dispatchChangeStarting(oldHolder, true);
+ dispatchChangeFinished(oldHolder, true);
+ return true;
+ }
+ return super.animateChange(oldHolder, newHolder, fromLeft, fromTop, toLeft,
+ toTop);
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 56352cc..45d733a 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -15,6 +15,11 @@
*/
package com.android.launcher3.widget.picker;
+import static com.android.launcher3.widget.picker.WidgetsListItemAnimator.CHANGE_DURATION_MS;
+import static com.android.launcher3.widget.picker.WidgetsListItemAnimator.MOVE_DURATION_MS;
+
+import static android.animation.ValueAnimator.areAnimatorsEnabled;
+
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
@@ -26,7 +31,6 @@
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.TableLayout;
-import android.widget.TableRow;
import androidx.annotation.NonNull;
import androidx.annotation.Px;
@@ -36,6 +40,7 @@
import com.android.launcher3.recyclerview.ViewHolderBinder;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetTableRow;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.util.WidgetsTableUtils;
@@ -111,17 +116,15 @@
for (int i = 0; i < widgetItemsTable.size(); i++) {
List<WidgetItem> widgetItemsPerRow = widgetItemsTable.get(i);
for (int j = 0; j < widgetItemsPerRow.size(); j++) {
- TableRow row = (TableRow) table.getChildAt(i);
+ WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
row.setVisibility(View.VISIBLE);
WidgetCell widget = (WidgetCell) row.getChildAt(j);
widget.clear();
+ widget.addPreviewReadyListener(row);
WidgetItem widgetItem = widgetItemsPerRow.get(j);
widget.setVisibility(View.VISIBLE);
- // When preview loads, notify adapter to rebind the item and possibly animate
- widget.applyFromCellItem(widgetItem,
- bitmap -> holder.onPreviewLoaded(Pair.create(widgetItem, bitmap)),
- holder.previewCache.get(widgetItem));
+ widget.applyFromCellItem(widgetItem);
widget.requestLayout();
}
}
@@ -143,14 +146,19 @@
for (int i = 0; i < widgetItemsTable.size(); i++) {
List<WidgetItem> widgetItems = widgetItemsTable.get(i);
- TableRow tableRow;
+ WidgetTableRow tableRow;
if (i < table.getChildCount()) {
- tableRow = (TableRow) table.getChildAt(i);
+ tableRow = (WidgetTableRow) table.getChildAt(i);
} else {
- tableRow = new TableRow(table.getContext());
+ tableRow = new WidgetTableRow(table.getContext());
tableRow.setGravity(Gravity.TOP);
table.addView(tableRow);
}
+ // Pass resize delay to let the "move" and "change" animations run before resizing the
+ // row.
+ tableRow.setupRow(widgetItems.size(),
+ /*resizeDelayMs=*/
+ areAnimatorsEnabled() ? (CHANGE_DURATION_MS + MOVE_DURATION_MS) : 0);
if (tableRow.getChildCount() > widgetItems.size()) {
for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
tableRow.getChildAt(j).setVisibility(View.GONE);
@@ -161,6 +169,7 @@
R.layout.widget_cell, tableRow, false);
// set up touch.
widget.setOnClickListener(mIconClickListener);
+ widget.addPreviewReadyListener(tableRow);
View preview = widget.findViewById(R.id.widget_preview_container);
preview.setOnClickListener(mIconClickListener);
preview.setOnLongClickListener(mIconLongClickListener);
@@ -176,7 +185,7 @@
int numOfRows = holder.tableContainer.getChildCount();
holder.previewCache.clear();
for (int i = 0; i < numOfRows; i++) {
- TableRow tableRow = (TableRow) holder.tableContainer.getChildAt(i);
+ WidgetTableRow tableRow = (WidgetTableRow) holder.tableContainer.getChildAt(i);
int numOfCols = tableRow.getChildCount();
for (int j = 0; j < numOfCols; j++) {
WidgetCell widget = (WidgetCell) tableRow.getChildAt(j);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 6dbad5c..1ed3d88 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -27,9 +27,7 @@
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.TableLayout;
-import android.widget.TableRow;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
@@ -38,6 +36,7 @@
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetTableRow;
import com.android.launcher3.widget.picker.util.WidgetPreviewContainerSize;
import java.util.ArrayList;
@@ -105,7 +104,8 @@
for (int i = 0; i < recommendationTable.size(); i++) {
List<WidgetItem> widgetItems = recommendationTable.get(i);
- TableRow tableRow = new TableRow(getContext());
+ WidgetTableRow tableRow = new WidgetTableRow(getContext());
+ tableRow.setupRow(widgetItems.size(), /*resizeDelayMs=*/ 0);
tableRow.setGravity(Gravity.TOP);
for (WidgetItem widgetItem : widgetItems) {
WidgetCell widgetCell = addItemCell(tableRow);
@@ -121,9 +121,10 @@
setVisibility(VISIBLE);
}
- private WidgetCell addItemCell(ViewGroup parent) {
+ private WidgetCell addItemCell(WidgetTableRow parent) {
WidgetCell widget = (WidgetCell) LayoutInflater.from(
getContext()).inflate(R.layout.widget_cell, parent, false);
+ widget.addPreviewReadyListener(parent);
widget.setOnClickListener(mWidgetCellOnClickListener);
View previewContainer = widget.findViewById(R.id.widget_preview_container);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index f835e18..5d71db6 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -67,7 +67,7 @@
// This ratio defines the max percentage of content area that the recommendations can display
// with respect to the bottom sheet's height.
- private static final float RECOMMENDATION_SECTION_HEIGHT_RATIO_TWO_PANE = 0.60f;
+ private static final float RECOMMENDATION_SECTION_HEIGHT_RATIO_TWO_PANE = 0.70f;
private FrameLayout mSuggestedWidgetsContainer;
private WidgetsListHeader mSuggestedWidgetsHeader;
private PackageUserKey mSuggestedWidgetsPackageUserKey;
diff --git a/src/com/android/launcher3/widget/util/WidgetDragScaleUtils.java b/src/com/android/launcher3/widget/util/WidgetDragScaleUtils.java
new file mode 100644
index 0000000..b8e7248
--- /dev/null
+++ b/src/com/android/launcher3/widget/util/WidgetDragScaleUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 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.widget.util;
+
+import static com.android.launcher3.widget.util.WidgetSizes.getWidgetSizePx;
+
+import android.content.Context;
+import android.util.Size;
+
+import androidx.annotation.Px;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfo;
+
+/** Utility classes to evaluate widget scale during drag and drops. **/
+public final class WidgetDragScaleUtils {
+ // Widgets are 5% scaled down relative to their size to have shadow display well inside the
+ // drop target frame (if its possible to scale it down within visible area under the finger).
+ private static final float WIDGET_SCALE_DOWN = 0.05f;
+
+ /**
+ * Returns the scale to be applied to given dragged view to scale it down relative to the
+ * spring loaded workspace. Applies additional scale down offset to get it a little inside
+ * the drop target frame. If the relative scale is smaller than minimum size needed to keep the
+ * view visible under the finger, scale down is performed only until the minimum size.
+ */
+ @Px
+ public static float getWidgetDragScalePx(Context context, DeviceProfile deviceProfile,
+ @Px float draggedViewWidthPx, @Px float draggedViewHeightPx, ItemInfo itemInfo) {
+ int minSize = context.getResources().getDimensionPixelSize(
+ R.dimen.widget_drag_view_min_scale_down_size);
+ Size widgetSizesPx = getWidgetSizePx(deviceProfile, itemInfo.spanX, itemInfo.spanY);
+
+ // We add workspace spring load scale, since the widget's drop target is also scaled, so
+ // the widget size is essentially that smaller.
+ float desiredWidgetScale = deviceProfile.getWorkspaceSpringLoadScale(context)
+ - WIDGET_SCALE_DOWN;
+ float desiredWidgetWidthPx = Math.max(minSize,
+ (desiredWidgetScale * widgetSizesPx.getWidth()));
+ float desiredWidgetHeightPx = Math.max(minSize,
+ desiredWidgetScale * widgetSizesPx.getHeight());
+
+ final float bitmapAspectRatio = draggedViewWidthPx / draggedViewHeightPx;
+ final float containerAspectRatio = desiredWidgetWidthPx / desiredWidgetHeightPx;
+
+ // This downscales large views to fit inside drop target frame. Smaller drawable views may
+ // be up-scaled if they are smaller than the min size;
+ final float scale = bitmapAspectRatio >= containerAspectRatio ? desiredWidgetWidthPx
+ / draggedViewWidthPx : desiredWidgetHeightPx / draggedViewHeightPx;
+ // scale in terms of dp to be applied to the drag shadow during drag and drop
+ return (draggedViewWidthPx * scale) - draggedViewWidthPx;
+ }
+}
diff --git a/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
index b62dbd1..9865516 100644
--- a/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -53,6 +53,11 @@
}
@Override
+ public int getTitle() {
+ return R.string.all_apps_label;
+ }
+
+ @Override
public int getVisibleElements(Launcher launcher) {
return ALL_APPS_CONTENT;
}
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index c3b7a2a..59d0de6 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -169,13 +169,11 @@
public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation";
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
- public static final String TWO_NEXUS_LAUNCHER_ACTIVITY_WHILE_UNLOCKING = "b/273347463";
public static final String ICON_MISSING = "b/282963545";
public static final String UIOBJECT_STALE_ELEMENT = "b/319501259";
public static final String TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE = "b/326908466";
public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890";
public static final String OVERVIEW_SELECT_TOOLTIP_MISALIGNED = "b/332485341";
- public static final String CLOCK_ICON_DRAWABLE_LEAKING = "b/319168409";
public static final String REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW = "enable-grid-only-overview";
public static final String REQUEST_FLAG_ENABLE_APP_PAIRS = "enable-app-pairs";
@@ -183,6 +181,9 @@
public static final String REQUEST_UNSTASH_BUBBLE_BAR_IF_STASHED =
"unstash-bubble-bar-if-stashed";
+ public static final String REQUEST_INJECT_FAKE_TRACKPAD = "inject-fake-trackpad";
+ public static final String REQUEST_EJECT_FAKE_TRACKPAD = "eject-fake-trackpad";
+
/** Logs {@link Log#d(String, String)} if {@link #sDebugTracing} is true. */
public static void testLogD(String tag, String message) {
if (!sDebugTracing) {
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index e378733..3405635 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -57,7 +57,7 @@
*
* For an implementation that mocks InvariantDeviceProfile, use [FakeInvariantDeviceProfileTest]
*/
-@AllowedDevices(allowed = [DeviceProduct.CF_PHONE])
+@AllowedDevices(allowed = [DeviceProduct.CF_PHONE, DeviceProduct.HOST_SIDE_X86_64])
@IgnoreLimit(ignoreLimit = BuildConfig.IS_STUDIO_BUILD)
abstract class AbstractDeviceProfileTest {
protected val testContext: Context = InstrumentationRegistry.getInstrumentation().context
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/util/WidgetDragScaleUtilsTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/util/WidgetDragScaleUtilsTest.kt
new file mode 100644
index 0000000..63833e4
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/util/WidgetDragScaleUtilsTest.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 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.widget.util
+
+import android.content.Context
+import android.graphics.Point
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.R
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.util.ActivityContextWrapper
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WidgetDragScaleUtilsTest {
+ private lateinit var context: Context
+ private lateinit var itemInfo: ItemInfo
+ private lateinit var deviceProfile: DeviceProfile
+
+ @Before
+ fun setup() {
+ context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+
+ itemInfo = ItemInfo()
+
+ deviceProfile =
+ Mockito.spy(LauncherAppState.getIDP(context).getDeviceProfile(context).copy(context))
+
+ doReturn(0.8f)
+ .whenever(deviceProfile).getWorkspaceSpringLoadScale(any(Context::class.java))
+ deviceProfile.cellLayoutBorderSpacePx = Point(CELL_SPACING, CELL_SPACING)
+ deviceProfile.widgetPadding.setEmpty()
+ }
+
+ @Test
+ fun getWidgetDragScalePx_largeDraggedView_downScaled() {
+ val minSize = context.resources.getDimensionPixelSize(
+ R.dimen.widget_drag_view_min_scale_down_size)
+ whenever(deviceProfile.cellSize).thenReturn(Point(minSize * 2, minSize * 2))
+
+ itemInfo.spanX = 2
+ itemInfo.spanY = 2
+
+ val widgetSize = WidgetSizes.getWidgetSizePx(deviceProfile, itemInfo.spanX, itemInfo.spanY)
+ // Assume dragged view was a drawable which was larger than widget's size.
+ val draggedViewWidthPx = widgetSize.width + 0.5f * widgetSize.width
+ val draggedViewHeightPx = widgetSize.height + 0.5f * widgetSize.height
+ // Returns negative scale pixels - i.e. downscaled
+ assertThat(
+ WidgetDragScaleUtils.getWidgetDragScalePx(
+ context,
+ deviceProfile,
+ draggedViewWidthPx,
+ draggedViewHeightPx,
+ itemInfo
+ )
+ )
+ .isLessThan(0)
+ }
+
+ @Test
+ fun getWidgetDragScalePx_draggedViewSameAsWidgetSize_downScaled() {
+ val minSize = context.resources.getDimensionPixelSize(
+ R.dimen.widget_drag_view_min_scale_down_size)
+ whenever(deviceProfile.cellSize).thenReturn(Point(minSize * 2, minSize * 2))
+ itemInfo.spanX = 4
+ itemInfo.spanY = 2
+
+ val widgetSize = WidgetSizes.getWidgetSizePx(deviceProfile, itemInfo.spanX, itemInfo.spanY)
+ // Assume dragged view was a drawable which was larger than widget's size.
+ val draggedViewWidthPx = widgetSize.width.toFloat()
+ val draggedViewHeightPx = widgetSize.height.toFloat()
+ // Returns negative scale pixels - i.e. downscaled
+ // Even if dragged view was of same size as widget's drop target, to accommodate the spring
+ // load scaling of workspace and additionally getting the view inside of drop target frame,
+ // widget would be downscaled.
+ assertThat(
+ WidgetDragScaleUtils.getWidgetDragScalePx(
+ context,
+ deviceProfile,
+ draggedViewWidthPx,
+ draggedViewHeightPx,
+ itemInfo
+ )
+ )
+ .isLessThan(0)
+ }
+
+ @Test
+ fun getWidgetDragScalePx_draggedViewSmallerThanMinSize_scaledSizeIsAtLeastMinSize() {
+ val minSizePx =
+ context.resources.getDimensionPixelSize(R.dimen.widget_drag_view_min_scale_down_size)
+ // Assume min size is greater than cell size, so that, we know the upscale of dragged view
+ // is due to min size enforcement.
+ whenever(deviceProfile.cellSize).thenReturn(Point(minSizePx / 2, minSizePx / 2))
+ itemInfo.spanX = 1
+ itemInfo.spanY = 1
+
+ val draggedViewWidthPx = minSizePx - 15f
+ val draggedViewHeightPx = minSizePx - 15f
+
+ // Returns positive scale pixels - i.e. up-scaled
+ val finalScalePx =
+ WidgetDragScaleUtils.getWidgetDragScalePx(
+ context,
+ deviceProfile,
+ draggedViewWidthPx,
+ draggedViewHeightPx,
+ itemInfo
+ )
+
+ val effectiveWidthPx = draggedViewWidthPx + finalScalePx
+ val scaleFactor = (draggedViewWidthPx + finalScalePx) / draggedViewWidthPx
+ val effectiveHeightPx = scaleFactor * draggedViewHeightPx
+ // Both original height and width were smaller than min size, scaling them down below min
+ // size would have made them not visible under the finger. Here, as expected, widget is
+ // at least as large as min size.
+ assertThat(effectiveWidthPx).isAtLeast(minSizePx)
+ assertThat(effectiveHeightPx).isAtLeast(minSizePx)
+ }
+
+ companion object {
+ const val CELL_SPACING = 10
+ }
+}
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index 4cd2a07..6ce1cb7 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -130,7 +130,7 @@
public void lockPrivateProfile_requestsQuietModeAsTrue() throws Exception {
when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED)).thenReturn(false);
- mPrivateProfileManager.lockPrivateProfile();
+ mPrivateProfileManager.setQuietMode(true /* lock */);
awaitTasksCompleted();
Mockito.verify(mUserManager).requestQuietModeEnabled(true, PRIVATE_HANDLE);
@@ -140,7 +140,7 @@
public void unlockPrivateProfile_requestsQuietModeAsFalse() throws Exception {
when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED)).thenReturn(true);
- mPrivateProfileManager.unlockPrivateProfile();
+ mPrivateProfileManager.setQuietMode(false /* unlock */);
awaitTasksCompleted();
Mockito.verify(mUserManager).requestQuietModeEnabled(false, PRIVATE_HANDLE);
@@ -176,7 +176,7 @@
doNothing().when(privateProfileManager).expandPrivateSpace();
when(privateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
- privateProfileManager.unlockPrivateProfile();
+ privateProfileManager.setQuietMode(false /* unlock */);
privateProfileManager.reset();
awaitTasksCompleted();
@@ -194,7 +194,7 @@
doNothing().when(privateProfileManager).expandPrivateSpace();
when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
- privateProfileManager.lockPrivateProfile();
+ privateProfileManager.setQuietMode(true /* lock */);
privateProfileManager.reset();
awaitTasksCompleted();
@@ -208,7 +208,7 @@
ArgumentCaptor<Intent> acIntent = ArgumentCaptor.forClass(Intent.class);
mPrivateProfileManager.setPrivateSpaceSettingsAvailable(true);
- mPrivateProfileManager.openPrivateSpaceSettings(null);
+ mContext.startActivity(expectedIntent);
Mockito.verify(mContext).startActivity(acIntent.capture());
assertEquals("Intent Action is different",
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
index 512b2ac..eac7f63 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
@@ -133,7 +133,7 @@
Bitmap unlockButton = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
when(privateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
- privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+ privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
awaitTasksCompleted();
int totalContainerHeaderView = 0;
@@ -168,7 +168,7 @@
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
when(privateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(true);
- privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+ privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
awaitTasksCompleted();
int totalContainerHeaderView = 0;
@@ -210,7 +210,7 @@
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
when(privateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(false);
- privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+ privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
awaitTasksCompleted();
int totalContainerHeaderView = 0;
@@ -248,7 +248,7 @@
Bitmap transitionImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_transition_image));
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
when(privateProfileManager.getCurrentState()).thenReturn(STATE_TRANSITION);
- privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+ privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
awaitTasksCompleted();
int totalContainerHeaderView = 0;
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java
new file mode 100644
index 0000000..9537e1c
--- /dev/null
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 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.allapps;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PRIVATESPACE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.util.ActivityContextWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class PrivateSpaceSettingsButtonTest {
+
+ private PrivateSpaceSettingsButton mVut;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context context = new ActivityContextWrapper(getApplicationContext());
+ mVut = new PrivateSpaceSettingsButton(context);
+ }
+
+ @Test
+ public void privateSpaceSettingsAppInfo_hasCorrectIdAndContainer() {
+ AppInfo appInfo = mVut.createPrivateSpaceSettingsAppInfo();
+
+ assertThat(appInfo.id).isEqualTo(CONTAINER_PRIVATESPACE);
+ assertThat(appInfo.container).isEqualTo(CONTAINER_PRIVATESPACE);
+ }
+}
diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
index f3f6fa5..05a1224 100644
--- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
@@ -120,7 +120,6 @@
@Test
@PortraitLandscape
@PlatinumTest(focusArea = "launcher")
- @ScreenRecordRule.ScreenRecord // b/322228038
public void testAllAppsFromHome() {
// Test opening all apps
assertNotNull("switchToAllApps() returned null",
diff --git a/tests/src/com/android/launcher3/dragging/TaplDragTest.java b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
index d43402b..41abcf8 100644
--- a/tests/src/com/android/launcher3/dragging/TaplDragTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
@@ -41,6 +41,7 @@
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.rule.ScreenRecordRule;
import org.junit.Test;
@@ -194,6 +195,7 @@
@PlatinumTest(focusArea = "launcher")
@Test
@PortraitLandscape
+ @ScreenRecordRule.ScreenRecord // b/343953783
public void testDragAppIcon() {
final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
diff --git a/tests/multivalentTests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
similarity index 100%
rename from tests/multivalentTests/src/com/android/launcher3/folder/FolderNameProviderTest.java
rename to tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
index a672c01..e92d641 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
@@ -113,6 +113,8 @@
@Test
@PortraitLandscape
+ @ScreenRecordRule.ScreenRecord // b/329935119
+ @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/329935119
public void testSinglePageDragIconWhenMultiplePageScrollingIsPossible() {
Workspace workspace = mLauncher.getWorkspace();
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index d85f630..f02a0c2 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -2318,6 +2318,14 @@
getTestInfo(TestProtocol.REQUEST_UNSTASH_BUBBLE_BAR_IF_STASHED);
}
+ public void injectFakeTrackpad() {
+ getTestInfo(TestProtocol.REQUEST_INJECT_FAKE_TRACKPAD);
+ }
+
+ public void ejectFakeTrackpad() {
+ getTestInfo(TestProtocol.REQUEST_EJECT_FAKE_TRACKPAD);
+ }
+
/** Blocks the taskbar from automatically stashing based on time. */
public void enableBlockTimeout(boolean enable) {
getTestInfo(enable