Merge "Fix flaky RequestPinItemTests" into tm-qpr-dev
diff --git a/OWNERS b/OWNERS
index dd9fbf9..2d7a014 100644
--- a/OWNERS
+++ b/OWNERS
@@ -29,6 +29,7 @@
sihua@google.com
sunnygoyal@google.com
tracyzhou@google.com
+tsuharesu@google.com
twickham@google.com
vadimt@google.com
victortulias@google.com
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index c638ba9..02460e4 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -132,6 +132,8 @@
// Next value 44
enum Attribute {
+ option allow_alias = true;
+
UNKNOWN = 0;
DEFAULT_LAYOUT = 1; // icon automatically placed in workspace, folder, hotseat
BACKUP_RESTORE = 2; // icon layout restored from backup
@@ -166,7 +168,8 @@
ALL_APPS_SEARCH_RESULT_SLICE = 19;
ALL_APPS_SEARCH_RESULT_WIDGETS = 20;
ALL_APPS_SEARCH_RESULT_PLAY = 21;
- ALL_APPS_SEARCH_RESULT_SUGGEST = 22;
+ ALL_APPS_SEARCH_RESULT_FALLBACK = 22;
+ ALL_APPS_SEARCH_RESULT_SUGGEST = 22 [deprecated = true];
ALL_APPS_SEARCH_RESULT_ASSISTANT = 23;
ALL_APPS_SEARCH_RESULT_CHROMETAB = 24;
ALL_APPS_SEARCH_RESULT_NAVVYSITE = 25;
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
index f08cabe..7ea92b5 100644
--- a/quickstep/res/layout/activity_allset.xml
+++ b/quickstep/res/layout/activity_allset.xml
@@ -38,6 +38,7 @@
app:lottie_loop="true" />
<androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/text_content_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/allset_page_margin_horizontal"
diff --git a/quickstep/res/values-night/colors.xml b/quickstep/res/values-night/colors.xml
index af6e064..6474c48 100644
--- a/quickstep/res/values-night/colors.xml
+++ b/quickstep/res/values-night/colors.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
+<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<color name="gesture_tutorial_back_arrow_color">#99000000</color>
@@ -24,4 +24,6 @@
<color name="all_set_page_background">#FF000000</color>
+ <!-- Turn on work apps button -->
+ <color name="work_turn_on_stroke">?androidprv:attr/colorAccentSecondaryVariant</color>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 55df38f..7ac3979 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
+<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<color name="chip_hint_foreground_color">#fff</color>
<color name="chip_scrim_start_color">#39000000</color>
@@ -95,4 +95,6 @@
<color name="lottie_yellow400">#fcc934</color>
<color name="lottie_yellow600">#f9ab00</color>
+ <!-- Turn on work apps button -->
+ <color name="work_turn_on_stroke">?androidprv:attr/colorAccentPrimaryVariant</color>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index a91507c..d581582 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -49,4 +49,6 @@
<item name="config_wallpaperMaxScale" format="float" type="dimen">
@*android:dimen/config_wallpaperMaxScale
</item>
+
+ <string name="setup_wizard_pkg" translatable="false" />
</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 9ff2cfc..5ddf2a8 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -299,7 +299,7 @@
ItemInfo tag = (ItemInfo) v.getTag();
if (tag != null && tag.shouldUseBackgroundAnimation()) {
ContainerAnimationRunner containerAnimationRunner =
- ContainerAnimationRunner.from(v, mStartingWindowListener);
+ ContainerAnimationRunner.from(v, mStartingWindowListener, onEndCallback);
if (containerAnimationRunner != null) {
delegateRunner = containerAnimationRunner;
}
@@ -1757,7 +1757,7 @@
@Nullable
private static ContainerAnimationRunner from(
- View v, StartingWindowListener startingWindowListener) {
+ View v, StartingWindowListener startingWindowListener, RunnableList onEndCallback) {
View viewToUse = findViewWithBackground(v);
if (viewToUse == null) {
viewToUse = v;
@@ -1784,8 +1784,15 @@
ActivityLaunchAnimator.Callback callback = task -> ColorUtils.setAlphaComponent(
startingWindowListener.getBackgroundColor(), 255);
+ ActivityLaunchAnimator.Listener listener = new ActivityLaunchAnimator.Listener() {
+ @Override
+ public void onLaunchAnimationEnd() {
+ onEndCallback.executeAllAndDestroy();
+ }
+ };
+
return new ContainerAnimationRunner(
- new ActivityLaunchAnimator.AnimationDelegate(controller, callback));
+ new ActivityLaunchAnimator.AnimationDelegate(controller, callback, listener));
}
/** Finds the closest parent of [view] (inclusive) with a background drawable. */
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index 9f3be69..0dde1bd 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -118,7 +118,7 @@
private void sendEvent(AppTarget target, LauncherAtom.ItemInfo locationInfo, int eventId,
int targetPredictor) {
// TODO: remove the running test check when b/231648228 is fixed.
- if (target != null && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (target != null && !Utilities.isRunningInTestHarness()) {
AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
.setLaunchLocation(getContainer(locationInfo))
.build();
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index b00c4cb..793c68e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -25,7 +25,6 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
-import android.annotation.ColorInt;
import android.os.RemoteException;
import android.util.Log;
import android.view.TaskTransitionSpec;
@@ -198,15 +197,7 @@
*/
public Animator createAnimToLauncher(@NonNull LauncherState toState,
@NonNull RecentsAnimationCallbacks callbacks, long duration) {
- AnimatorSet set = new AnimatorSet();
- Animator taskbarState = mTaskbarLauncherStateController
- .createAnimToLauncher(toState, callbacks, duration);
- long halfDuration = Math.round(duration * 0.5f);
- Animator translation =
- mControllers.taskbarTranslationController.createAnimToLauncher(halfDuration);
-
- set.playTogether(taskbarState, translation);
- return set;
+ return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration);
}
public boolean isDraggingItem() {
@@ -231,17 +222,10 @@
WindowManagerGlobal.getWindowManagerService().clearTaskTransitionSpec();
} else {
// Adjust task transition spec to account for taskbar being visible
- @ColorInt int taskAnimationBackgroundColor =
- DisplayController.isTransientTaskbar(mLauncher)
- ? mLauncher.getColor(R.color.transient_taskbar_background)
- : mLauncher.getColor(R.color.taskbar_background);
-
- TaskTransitionSpec customTaskAnimationSpec = new TaskTransitionSpec(
- taskAnimationBackgroundColor,
- Set.of(ITYPE_EXTRA_NAVIGATION_BAR)
- );
- WindowManagerGlobal.getWindowManagerService()
- .setTaskTransitionSpec(customTaskAnimationSpec);
+ WindowManagerGlobal.getWindowManagerService().setTaskTransitionSpec(
+ new TaskTransitionSpec(
+ mLauncher.getColor(R.color.taskbar_background),
+ Set.of(ITYPE_EXTRA_NAVIGATION_BAR)));
}
} catch (RemoteException e) {
// This shouldn't happen but if it does task animations won't look good until the
@@ -280,7 +264,7 @@
* Returns {@code true} if a Taskbar education should be shown on application launch.
*/
public boolean shouldShowEduOnAppLaunch() {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
return false;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 728c91f..bafd5b4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -125,8 +125,10 @@
private static final int FLAG_SMALL_SCREEN = 1 << 13;
private static final int FLAG_SLIDE_IN_VIEW_VISIBLE = 1 << 14;
- /** Flags where a UI could be over a slide in view, so the color override should be disabled. */
- private static final int FLAGS_SLIDE_IN_VIEW_ICON_COLOR_OVERRIDE_DISABLED =
+ /**
+ * Flags where a UI could be over Taskbar surfaces, so the color override should be disabled.
+ */
+ private static final int FLAGS_ON_BACKGROUND_COLOR_OVERRIDE_DISABLED =
FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_VOICE_INTERACTION_WINDOW_SHOWING;
private static final String NAV_BUTTONS_SEPARATE_WINDOW_TITLE = "Taskbar Nav Buttons";
@@ -148,8 +150,8 @@
private final ViewGroup mStartContextualContainer;
private final int mLightIconColor;
private final int mDarkIconColor;
- /** Color to use for navigation bar buttons, if a slide in view is visible. */
- private final int mSlideInViewIconColor;
+ /** Color to use for navigation bar buttons, if they are on on a Taskbar surface background. */
+ private final int mOnBackgroundIconColor;
private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat(
this::updateNavButtonTranslationY);
@@ -160,13 +162,18 @@
// Used for System UI state updates that should translate the nav button for in-app display.
private final AnimatedFloat mNavButtonInAppDisplayProgressForSysui = new AnimatedFloat(
this::updateNavButtonInAppDisplayProgressForSysui);
+ /** Expected nav button dark intensity communicated via the framework. */
private final AnimatedFloat mTaskbarNavButtonDarkIntensity = new AnimatedFloat(
- this::updateNavButtonDarkIntensity);
- private final AnimatedFloat mNavButtonDarkIntensityMultiplier = new AnimatedFloat(
- this::updateNavButtonDarkIntensity);
- /** Overrides the navigation button color to {@code mSlideInViewIconColor} when {@code 1}. */
- private final AnimatedFloat mSlideInViewNavButtonColorOverride = new AnimatedFloat(
- this::updateNavButtonDarkIntensity);
+ this::updateNavButtonColor);
+ /** {@code 1} if the Taskbar background color is fully opaque. */
+ private final AnimatedFloat mOnTaskbarBackgroundNavButtonColorOverride = new AnimatedFloat(
+ this::updateNavButtonColor);
+ /** {@code 1} if a Taskbar slide in overlay is visible over Taskbar. */
+ private final AnimatedFloat mSlideInViewVisibleNavButtonColorOverride = new AnimatedFloat(
+ this::updateNavButtonColor);
+ /** Disables the {@link #mOnBackgroundIconColor} override if {@code 0}. */
+ private final AnimatedFloat mOnBackgroundNavButtonColorOverrideMultiplier = new AnimatedFloat(
+ this::updateNavButtonColor);
private final RotationButtonListener mRotationButtonListener = new RotationButtonListener();
private final Rect mFloatingRotationButtonBounds = new Rect();
@@ -199,8 +206,7 @@
mLightIconColor = context.getColor(R.color.taskbar_nav_icon_light_color);
mDarkIconColor = context.getColor(R.color.taskbar_nav_icon_dark_color);
- // Can precompute color since dark theme change recreates taskbar.
- mSlideInViewIconColor = Utilities.isDarkTheme(context) ? mLightIconColor : mDarkIconColor;
+ mOnBackgroundIconColor = Utilities.isDarkTheme(context) ? mLightIconColor : mDarkIconColor;
}
/**
@@ -266,10 +272,15 @@
flags -> (flags & FLAG_IME_VISIBLE) != 0 && !isInKidsMode, AnimatedFloat.VALUE,
transForIme, defaultButtonTransY));
+ // Start at 1 because relevant flags are unset at init.
+ mOnBackgroundNavButtonColorOverrideMultiplier.value = 1;
mPropertyHolders.add(new StatePropertyHolder(
- mSlideInViewNavButtonColorOverride,
- flags -> ((flags & FLAG_SLIDE_IN_VIEW_VISIBLE) != 0)
- && ((flags & FLAGS_SLIDE_IN_VIEW_ICON_COLOR_OVERRIDE_DISABLED) == 0)));
+ mOnBackgroundNavButtonColorOverrideMultiplier,
+ flags -> (flags & FLAGS_ON_BACKGROUND_COLOR_OVERRIDE_DISABLED) == 0));
+
+ mPropertyHolders.add(new StatePropertyHolder(
+ mSlideInViewVisibleNavButtonColorOverride,
+ flags -> (flags & FLAG_SLIDE_IN_VIEW_VISIBLE) != 0));
if (alwaysShowButtons) {
initButtons(mNavButtonContainer, mEndContextualContainer,
@@ -569,9 +580,9 @@
return mTaskbarNavButtonDarkIntensity;
}
- /** Use to determine whether to use the dark intensity requested by the underlying app */
- public AnimatedFloat getNavButtonDarkIntensityMultiplier() {
- return mNavButtonDarkIntensityMultiplier;
+ /** Use to override the nav button color with {@link #mOnBackgroundIconColor}. */
+ public AnimatedFloat getOnTaskbarBackgroundNavButtonColorOverride() {
+ return mOnTaskbarBackgroundNavButtonColorOverride;
}
/**
@@ -617,14 +628,20 @@
+ inAppDisplayAdjustmentTranslationY);
}
- private void updateNavButtonDarkIntensity() {
- float darkIntensity = mTaskbarNavButtonDarkIntensity.value
- * mNavButtonDarkIntensityMultiplier.value;
- ArgbEvaluator argbEvaluator = ArgbEvaluator.getInstance();
- int iconColor = (int) argbEvaluator.evaluate(
- darkIntensity, mLightIconColor, mDarkIconColor);
- iconColor = (int) argbEvaluator.evaluate(
- mSlideInViewNavButtonColorOverride.value, iconColor, mSlideInViewIconColor);
+ private void updateNavButtonColor() {
+ final ArgbEvaluator argbEvaluator = ArgbEvaluator.getInstance();
+ final int sysUiNavButtonIconColor = (int) argbEvaluator.evaluate(
+ mTaskbarNavButtonDarkIntensity.value,
+ mLightIconColor,
+ mDarkIconColor);
+ // Override the color from framework if nav buttons are over an opaque Taskbar surface.
+ final int iconColor = (int) argbEvaluator.evaluate(
+ mOnBackgroundNavButtonColorOverrideMultiplier.value
+ * Math.max(
+ mOnTaskbarBackgroundNavButtonColorOverride.value,
+ mSlideInViewVisibleNavButtonColorOverride.value),
+ sysUiNavButtonIconColor,
+ mOnBackgroundIconColor);
for (ImageView button : mAllButtons) {
button.setImageTintList(ColorStateList.valueOf(iconColor));
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 5c4f3e1..2864ac7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -24,7 +24,7 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
-import static com.android.launcher3.Utilities.IS_RUNNING_IN_TEST_HARNESS;
+import static com.android.launcher3.Utilities.isRunningInTestHarness;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
@@ -337,8 +337,7 @@
int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SLIPPERY
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
- if (DisplayController.isTransientTaskbar(this)
- && !IS_RUNNING_IN_TEST_HARNESS) {
+ if (DisplayController.isTransientTaskbar(this) && !isRunningInTestHarness()) {
windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
}
@@ -877,7 +876,11 @@
* as if the user tapped on it (preserving the split pair). Otherwise, launch it normally
* (potentially breaking a split pair).
*/
- private void launchFromTaskbarPreservingSplitIfVisible(RecentsView recents, ItemInfo info) {
+ private void launchFromTaskbarPreservingSplitIfVisible(@Nullable RecentsView recents,
+ ItemInfo info) {
+ if (recents == null) {
+ return;
+ }
ComponentKey componentToBeLaunched = new ComponentKey(info.getTargetComponent(), info.user);
recents.getSplitSelectController().findLastActiveTaskAndRunCallback(
componentToBeLaunched,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index e00bc59..3375877 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -66,8 +66,6 @@
paint.style = Paint.Style.FILL
if (isTransientTaskbar) {
- paint.color = context.getColor(R.color.transient_taskbar_background)
-
val res = context.resources
bottomMargin = res.getDimensionPixelSize(R.dimen.transient_taskbar_margin)
shadowBlur = res.getDimension(R.dimen.transient_taskbar_shadow_blur)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 7c4071f..7c3d14d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -59,10 +59,9 @@
// Initialized in init.
private TaskbarControllers mControllers;
- private AnimatedFloat mNavButtonDarkIntensityMultiplier;
+ private AnimatedFloat mOnBackgroundNavButtonColorIntensity;
private float mLastSetBackgroundAlpha;
- private boolean mIsBackgroundDrawnElsewhere;
public TaskbarDragLayerController(TaskbarActivityContext activity,
TaskbarDragLayer taskbarDragLayer) {
@@ -77,8 +76,8 @@
mControllers = controllers;
mTaskbarDragLayer.init(new TaskbarDragLayerCallbacks());
- mNavButtonDarkIntensityMultiplier = mControllers.navbarButtonsViewController
- .getNavButtonDarkIntensityMultiplier();
+ mOnBackgroundNavButtonColorIntensity = mControllers.navbarButtonsViewController
+ .getOnTaskbarBackgroundNavButtonColorOverride();
mBgTaskbar.value = 1;
mKeyguardBgTaskbar.value = 1;
@@ -152,7 +151,7 @@
mLastSetBackgroundAlpha = mBgOverride.value * Math.max(bgNavbar, bgTaskbar);
mTaskbarDragLayer.setTaskbarBackgroundAlpha(mLastSetBackgroundAlpha);
- updateNavBarDarkIntensityMultiplier();
+ updateOnBackgroundNavButtonColorIntensity();
}
/**
@@ -165,7 +164,7 @@
private void updateBackgroundOffset() {
mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
- updateNavBarDarkIntensityMultiplier();
+ updateOnBackgroundNavButtonColorIntensity();
}
@Override
@@ -174,23 +173,16 @@
}
/**
- * Set if another controller is temporarily handling background drawing. In this case we:
- * - Override our background alpha to be 0.
- * - Keep the nav bar dark intensity assuming taskbar background is at full alpha.
+ * Set if another controller is temporarily handling background drawing. In this case we
+ * override our background alpha to be {@code 0}.
*/
public void setIsBackgroundDrawnElsewhere(boolean isBackgroundDrawnElsewhere) {
- mIsBackgroundDrawnElsewhere = isBackgroundDrawnElsewhere;
- mBgOverride.updateValue(mIsBackgroundDrawnElsewhere ? 0 : 1);
- updateNavBarDarkIntensityMultiplier();
+ mBgOverride.updateValue(isBackgroundDrawnElsewhere ? 0 : 1);
}
- private void updateNavBarDarkIntensityMultiplier() {
- // Zero out the app-requested dark intensity when we're drawing our own background.
- float effectiveBgAlpha = mLastSetBackgroundAlpha * (1 - mBgOffset.value);
- if (mIsBackgroundDrawnElsewhere) {
- effectiveBgAlpha = 1;
- }
- mNavButtonDarkIntensityMultiplier.updateValue(1 - effectiveBgAlpha);
+ private void updateOnBackgroundNavButtonColorIntensity() {
+ mOnBackgroundNavButtonColorIntensity.updateValue(
+ mLastSetBackgroundAlpha * (1 - mBgOffset.value));
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index 00dfaf2..bc582e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -28,7 +28,6 @@
import com.airbnb.lottie.model.KeyPath
import com.android.launcher3.R
import com.android.launcher3.Utilities
-import com.android.launcher3.Utilities.IS_RUNNING_IN_TEST_HARNESS
import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_EDU_TOOLTIP
import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_EDU_OPEN
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
@@ -56,7 +55,8 @@
class TaskbarEduTooltipController(val activityContext: TaskbarActivityContext) :
LoggableTaskbarController {
- private val isTooltipEnabled = !IS_RUNNING_IN_TEST_HARNESS && ENABLE_TASKBAR_EDU_TOOLTIP.get()
+ private val isTooltipEnabled: Boolean
+ get() = !Utilities.isRunningInTestHarness() && ENABLE_TASKBAR_EDU_TOOLTIP.get()
private val isOpen: Boolean
get() = tooltip?.isOpen ?: false
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index b586487..5ac0570 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -433,6 +433,14 @@
});
animatorSet.play(stashAnimator);
}
+
+ if (isAnimatingToLauncher() || mLauncherState == LauncherState.NORMAL) {
+ // Translate back to 0 at a shorter or same duration as the icon alignment animation.
+ // This ensures there is no jump after switching to hotseat, e.g. when swiping up from
+ // overview to home. Currently we do duration / 2 just to make it feel snappier.
+ animatorSet.play(mControllers.taskbarTranslationController
+ .createAnimToResetTranslation(duration / 2));
+ }
}
private boolean isInLauncher() {
@@ -460,7 +468,7 @@
updateIconAlphaForHome(taskbarWillBeVisible ? 1 : 0);
// Sync the first frame where we swap taskbar and hotseat.
- if (firstFrameVisChanged && mCanSyncViews && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (firstFrameVisChanged && mCanSyncViews && !Utilities.isRunningInTestHarness()) {
ViewRootSync.synchronizeNextDraw(mLauncher.getHotseat(),
mControllers.taskbarActivityContext.getDragLayer(),
() -> {});
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 115db25..babafd5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -297,7 +297,7 @@
}
return supportsVisualStashing()
&& isInApp()
- && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests)
+ && (!Utilities.isRunningInTestHarness() || mEnableManualStashingDuringTests)
&& !DisplayController.isTransientTaskbar(mActivity);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index a6b2a8a..062b4ce 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -135,9 +135,9 @@
}
/**
- * Returns an animation to reset the taskbar translation for animation back to launcher.
+ * Returns an animation to reset the taskbar translation to {@code 0}.
*/
- public ObjectAnimator createAnimToLauncher(long duration) {
+ public ObjectAnimator createAnimToResetTranslation(long duration) {
ObjectAnimator animator = ObjectAnimator.ofFloat(mTranslationYForSwipe, VALUE, 0);
animator.setInterpolator(Interpolators.LINEAR);
animator.setDuration(duration);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 0b275a8..3d5089f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -142,9 +142,8 @@
: R.drawable.ic_taskbar_all_apps_button));
mAllAppsButton.setScaleX(mIsRtl ? -1 : 1);
mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
- mAllAppsButton.setForegroundTint(mActivityContext.getColor(isTransientTaskbar
- ? R.color.all_apps_button_color
- : R.color.all_apps_button_color_dark));
+ mAllAppsButton.setForegroundTint(
+ mActivityContext.getColor(R.color.all_apps_button_color));
if (FeatureFlags.ENABLE_TASKBAR_PINNING.get()) {
mTaskbarDivider = LayoutInflater.from(context).inflate(R.layout.taskbar_divider,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index ac92374..3143f23 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.anim.AnimatedFloat.VALUE;
@@ -27,13 +28,15 @@
import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_ALIGNMENT_ANIM;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_REVEAL_ANIM;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.graphics.Rect;
-import android.util.FloatProperty;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@@ -43,10 +46,10 @@
import androidx.core.graphics.ColorUtils;
import androidx.core.view.OneShotPreDrawListener;
-import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
+import com.android.launcher3.Reorderable;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AlphaUpdateListener;
import com.android.launcher3.anim.AnimatedFloat;
@@ -56,7 +59,6 @@
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
@@ -65,6 +67,7 @@
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.MultiPropertyFactory;
+import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.util.MultiValueAlpha;
import java.io.PrintWriter;
@@ -338,18 +341,34 @@
croppedTransX = newLeft - iconLeft;
}
- as.play(ObjectAnimator.ofFloat(child, ICON_REVEAL_TRANSLATE_X, isStashed
- ? new float[] {croppedTransX}
- : new float[] {croppedTransX, 0}));
-
float croppedTransY = child.getHeight() - stashedBounds.height();
- as.play(ObjectAnimator.ofFloat(child, ICON_REVEAL_TRANSLATE_Y, isStashed
- ? new float[] {croppedTransY}
- : new float[] {croppedTransY, 0}));
- as.addListener(forEndCallback(() -> {
- ICON_REVEAL_TRANSLATE_X.set(child, 0f);
- ICON_REVEAL_TRANSLATE_Y.set(child, 0f);
- }));
+ if (child instanceof Reorderable) {
+ MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate();
+
+ as.play(ObjectAnimator.ofFloat(mtd.getTranslationX(INDEX_TASKBAR_REVEAL_ANIM),
+ MULTI_PROPERTY_VALUE, isStashed
+ ? new float[] {croppedTransX}
+ : new float[] {croppedTransX, 0}));
+ as.play(ObjectAnimator.ofFloat(mtd.getTranslationY(INDEX_TASKBAR_REVEAL_ANIM),
+ MULTI_PROPERTY_VALUE, isStashed
+ ? new float[] {croppedTransY}
+ : new float[] {croppedTransY, 0}));
+ as.addListener(forEndCallback(() ->
+ mtd.setTranslation(INDEX_TASKBAR_REVEAL_ANIM, 0, 0)));
+ } else {
+ as.play(ObjectAnimator.ofFloat(child,
+ VIEW_TRANSLATE_X, isStashed
+ ? new float[] {croppedTransX}
+ : new float[] {croppedTransX, 0}));
+ as.play(ObjectAnimator.ofFloat(child,
+ VIEW_TRANSLATE_Y, isStashed
+ ? new float[] {croppedTransY}
+ : new float[] {croppedTransY, 0}));
+ as.addListener(forEndCallback(() -> {
+ child.setTranslationX(0);
+ child.setTranslationY(0);
+ }));
+ }
}
return as;
}
@@ -435,7 +454,7 @@
float childCenter = (child.getLeft() + child.getRight()) / 2f;
float halfQsbIconWidthDiff =
(launcherDp.hotseatQsbWidth - taskbarDp.iconSizePx) / 2f;
- setter.addFloat(child, ICON_TRANSLATE_X,
+ setter.addFloat(child, VIEW_TRANSLATE_X,
isRtl ? -halfQsbIconWidthDiff : halfQsbIconWidthDiff,
hotseatIconCenter - childCenter, interpolator);
@@ -479,10 +498,18 @@
+ hotseatCellSize / 2f;
float childCenter = (child.getLeft() + child.getRight()) / 2f;
- setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, interpolator);
+ if (child instanceof Reorderable) {
+ MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate();
- setter.setFloat(child, ICON_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
-
+ setter.setFloat(mtd.getTranslationX(INDEX_TASKBAR_ALIGNMENT_ANIM),
+ MULTI_PROPERTY_VALUE, hotseatIconCenter - childCenter, interpolator);
+ setter.setFloat(mtd.getTranslationY(INDEX_TASKBAR_ALIGNMENT_ANIM),
+ MULTI_PROPERTY_VALUE, mTaskbarBottomMargin, interpolator);
+ } else {
+ setter.setFloat(child, VIEW_TRANSLATE_X,
+ hotseatIconCenter - childCenter, interpolator);
+ setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
+ }
setter.setFloat(child, SCALE_PROPERTY, scaleUp, interpolator);
}
@@ -667,107 +694,4 @@
mControllers.uiController.onIconLayoutBoundsChanged();
}
}
-
- public static final FloatProperty<View> ICON_TRANSLATE_X =
- new FloatProperty<View>("taskbarAlignmentTranslateX") {
-
- @Override
- public void setValue(View view, float v) {
- if (view instanceof BubbleTextView) {
- ((BubbleTextView) view).setTranslationXForTaskbarAlignmentAnimation(v);
- } else if (view instanceof FolderIcon) {
- ((FolderIcon) view).setTranslationXForTaskbarAlignmentAnimation(v);
- } else {
- view.setTranslationX(v);
- }
- }
-
- @Override
- public Float get(View view) {
- if (view instanceof BubbleTextView) {
- return ((BubbleTextView) view)
- .getTranslationXForTaskbarAlignmentAnimation();
- } else if (view instanceof FolderIcon) {
- return ((FolderIcon) view).getTranslationXForTaskbarAlignmentAnimation();
- }
- return view.getTranslationX();
- }
- };
-
- public static final FloatProperty<View> ICON_TRANSLATE_Y =
- new FloatProperty<View>("taskbarAlignmentTranslateY") {
-
- @Override
- public void setValue(View view, float v) {
- if (view instanceof BubbleTextView) {
- ((BubbleTextView) view).setTranslationYForTaskbarAlignmentAnimation(v);
- } else if (view instanceof FolderIcon) {
- ((FolderIcon) view).setTranslationYForTaskbarAlignmentAnimation(v);
- } else {
- view.setTranslationY(v);
- }
- }
-
- @Override
- public Float get(View view) {
- if (view instanceof BubbleTextView) {
- return ((BubbleTextView) view)
- .getTranslationYForTaskbarAlignmentAnimation();
- } else if (view instanceof FolderIcon) {
- return ((FolderIcon) view).getTranslationYForTaskbarAlignmentAnimation();
- }
- return view.getTranslationY();
- }
- };
-
- public static final FloatProperty<View> ICON_REVEAL_TRANSLATE_X =
- new FloatProperty<View>("taskbarRevealTranslateX") {
-
- @Override
- public void setValue(View view, float v) {
- if (view instanceof BubbleTextView) {
- ((BubbleTextView) view).setTranslationXForTaskbarRevealAnimation(v);
- } else if (view instanceof FolderIcon) {
- ((FolderIcon) view).setTranslationXForTaskbarRevealAnimation(v);
- } else {
- view.setTranslationX(v);
- }
- }
-
- @Override
- public Float get(View view) {
- if (view instanceof BubbleTextView) {
- return ((BubbleTextView) view).getTranslationXForTaskbarRevealAnimation();
- } else if (view instanceof FolderIcon) {
- return ((FolderIcon) view).getTranslationXForTaskbarRevealAnimation();
- }
- return view.getTranslationX();
- }
- };
-
- public static final FloatProperty<View> ICON_REVEAL_TRANSLATE_Y =
- new FloatProperty<View>("taskbarRevealTranslateY") {
-
- @Override
- public void setValue(View view, float v) {
- if (view instanceof BubbleTextView) {
- ((BubbleTextView) view).setTranslationYForTaskbarRevealAnimation(v);
- } else if (view instanceof FolderIcon) {
- ((FolderIcon) view).setTranslationYForTaskbarRevealAnimation(v);
- } else {
- view.setTranslationY(v);
- }
- }
-
- @Override
- public Float get(View view) {
- if (view instanceof BubbleTextView) {
- return ((BubbleTextView) view).getTranslationYForTaskbarRevealAnimation();
- } else if (view instanceof FolderIcon) {
- return ((FolderIcon) view).getTranslationYForTaskbarRevealAnimation();
- }
- return view.getTranslationY();
- }
- };
-
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index a56300a..a53f08a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -20,10 +20,11 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON;
+import static com.android.launcher3.LauncherSettings.Animation.VIEW_BACKGROUND;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -1053,7 +1054,8 @@
activityOptions.options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER,
mLastTouchUpTime);
}
- if (item != null && item.itemType == ITEM_TYPE_SEARCH_ACTION) {
+ if (item != null && (item.animationType == DEFAULT_NO_ICON
+ || item.animationType == VIEW_BACKGROUND)) {
activityOptions.options.setSplashScreenStyle(
SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR);
} else {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
index 278a45a..ff3a292 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -22,6 +22,7 @@
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import android.util.Log;
import android.util.SparseArray;
import android.widget.RemoteViews;
@@ -33,14 +34,14 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.LauncherWidgetHolder;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
-import java.util.Map;
+import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.BiConsumer;
import java.util.function.IntConsumer;
@@ -50,6 +51,8 @@
*/
public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
+ private static final String TAG = "QuickstepWidgetHolder";
+
private static final UpdateKey<AppWidgetProviderInfo> KEY_PROVIDER_UPDATE =
AppWidgetHostView::onUpdateProviderInfo;
private static final UpdateKey<RemoteViews> KEY_VIEWS_UPDATE =
@@ -63,6 +66,8 @@
private static AppWidgetHost sWidgetHost = null;
+ private final SparseArray<AppWidgetHostView> mViews = new SparseArray<>();
+
private final @Nullable RemoteViews.InteractionHandler mInteractionHandler;
private final @NonNull IntConsumer mAppWidgetRemovedCallback;
@@ -71,15 +76,14 @@
// Map to all pending updated keyed with appWidgetId;
private final SparseArray<PendingUpdate> mPendingUpdateMap = new SparseArray<>();
- @Thunk
- QuickstepWidgetHolder(@NonNull Context context,
+ private QuickstepWidgetHolder(@NonNull Context context,
@Nullable IntConsumer appWidgetRemovedCallback,
@Nullable RemoteViews.InteractionHandler interactionHandler) {
super(context, appWidgetRemovedCallback);
mAppWidgetRemovedCallback = appWidgetRemovedCallback != null ? appWidgetRemovedCallback
: i -> {};
mInteractionHandler = interactionHandler;
- sHolders.add(this);
+ MAIN_EXECUTOR.execute(() -> sHolders.add(this));
}
@Override
@@ -92,7 +96,7 @@
sHolders.forEach(h -> h.mAppWidgetRemovedCallback.accept(i))),
() -> MAIN_EXECUTOR.execute(() ->
sHolders.forEach(h -> h.mProviderChangedListeners.forEach(
- ProviderChangedListener::notifyWidgetProvidersChanged))),
+ ProviderChangedListener::notifyWidgetProvidersChanged))),
UI_HELPER_EXECUTOR.getLooper());
if (!WidgetsModel.GO_DISABLE_WIDGETS) {
sWidgetHost.startListening();
@@ -107,11 +111,7 @@
int count = mPendingUpdateMap.size();
for (int i = 0; i < count; i++) {
int widgetId = mPendingUpdateMap.keyAt(i);
- QuickstepWidgetHolderListener listener = sListeners.get(widgetId);
- if (listener == null) {
- continue;
- }
- AppWidgetHostView view = listener.mView.get(this);
+ AppWidgetHostView view = mViews.get(widgetId);
if (view == null) {
continue;
}
@@ -131,7 +131,16 @@
mPendingUpdateMap.clear();
}
- private <T> void addPendingAction(int widgetId, UpdateKey<T> key, T data) {
+ private <T> void onWidgetUpdate(int widgetId, UpdateKey<T> key, T data) {
+ if (isListening()) {
+ AppWidgetHostView view = mViews.get(widgetId);
+ if (view == null) {
+ return;
+ }
+ key.accept(view, data);
+ return;
+ }
+
PendingUpdate pendingUpdate = mPendingUpdateMap.get(widgetId);
if (pendingUpdate == null) {
pendingUpdate = new PendingUpdate();
@@ -167,7 +176,11 @@
*/
@Override
public void destroy() {
- sHolders.remove(this);
+ try {
+ MAIN_EXECUTOR.submit(() -> sHolders.remove(this)).get();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to remove self from holder list", e);
+ }
}
@Override
@@ -228,15 +241,16 @@
}
widgetView.setInteractionHandler(mInteractionHandler);
widgetView.setAppWidget(appWidgetId, appWidget);
+ mViews.put(appWidgetId, widgetView);
QuickstepWidgetHolderListener listener = sListeners.get(appWidgetId);
if (listener == null) {
- listener = new QuickstepWidgetHolderListener(appWidgetId, this, widgetView);
+ listener = new QuickstepWidgetHolderListener(appWidgetId);
sWidgetHost.setListener(appWidgetId, listener);
sListeners.put(appWidgetId, listener);
- } else {
- listener.resetView(this, widgetView);
}
+ RemoteViews remoteViews = listener.addHolder(this);
+ widgetView.updateAppWidget(remoteViews);
return widgetView;
}
@@ -247,31 +261,30 @@
@Override
public void clearViews() {
for (int i = sListeners.size() - 1; i >= 0; i--) {
- sListeners.valueAt(i).mView.remove(this);
+ sListeners.valueAt(i).mListeningHolders.remove(this);
}
}
private static class QuickstepWidgetHolderListener
implements AppWidgetHost.AppWidgetHostListener {
- @NonNull
- private final Map<QuickstepWidgetHolder, AppWidgetHostView> mView = new WeakHashMap<>();
+ // Static listeners should use a set that is backed by WeakHashMap to avoid memory leak
+ private final Set<QuickstepWidgetHolder> mListeningHolders = Collections.newSetFromMap(
+ new WeakHashMap<>());
private final int mWidgetId;
- @Nullable private RemoteViews mRemoteViews = null;
+ private @Nullable RemoteViews mRemoteViews;
- QuickstepWidgetHolderListener(int widgetId, @NonNull QuickstepWidgetHolder holder,
- @NonNull LauncherAppWidgetHostView view) {
+ QuickstepWidgetHolderListener(int widgetId) {
mWidgetId = widgetId;
- mView.put(holder, view);
}
@UiThread
- public void resetView(@NonNull QuickstepWidgetHolder holder,
- @NonNull AppWidgetHostView view) {
- mView.put(holder, view);
- view.updateAppWidget(mRemoteViews);
+ @Nullable
+ public RemoteViews addHolder(@NonNull QuickstepWidgetHolder holder) {
+ mListeningHolders.add(holder);
+ return mRemoteViews;
}
@Override
@@ -295,13 +308,8 @@
}
private <T> void executeOnMainExecutor(UpdateKey<T> key, T data) {
- MAIN_EXECUTOR.execute(() -> mView.forEach((holder, view) -> {
- if (holder.isListening()) {
- key.accept(view, data);
- } else {
- holder.addPendingAction(mWidgetId, key, data);
- }
- }));
+ MAIN_EXECUTOR.execute(() -> mListeningHolders.forEach(holder ->
+ holder.onWidgetUpdate(mWidgetId, key, data)));
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 07fcf48..f16b43d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -91,7 +91,7 @@
builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
mRecentsView.updateEmptyMessage();
// TODO(b/246283207): Remove logging once root cause of flake detected.
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
Log.d("b/246283207", "RecentsView#setStateWithAnimationInternal getCurrentPage(): "
+ mRecentsView.getCurrentPage()
+ ", getScrollForPage(getCurrentPage())): "
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 5eeeb36..a02f3de 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
+import static com.android.launcher3.QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION;
import static com.android.launcher3.WorkspaceStateTransitionAnimation.getWorkspaceSpringScaleAnimator;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
@@ -119,6 +120,12 @@
long scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
numPagesToScroll * PER_PAGE_SCROLL_DURATION);
config.duration = Math.max(config.duration, scrollDuration);
+
+ // Sync scroll so that it ends before or at the same time as the taskbar animation.
+ if (DisplayController.isTransientTaskbar(mActivity)
+ && mActivity.getDeviceProfile().isTaskbarPresent) {
+ config.duration = Math.min(config.duration, TASKBAR_TO_HOME_DURATION);
+ }
overview.snapToPage(DEFAULT_PAGE, Math.toIntExact(config.duration));
} else {
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index e264a7f..e9385d9 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -878,7 +878,7 @@
if (DesktopTaskView.DESKTOP_MODE_SUPPORTED && targets.hasDesktopTasks()) {
mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets);
} else {
- mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets);
+ mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(targets);
}
mRecentsAnimationController = controller;
mRecentsAnimationTargets = targets;
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 589459f..a8f3c3a 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -37,6 +37,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.R;
import com.android.launcher3.tracing.OverviewComponentObserverProto;
import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -65,6 +66,7 @@
private final Intent mMyHomeIntent;
private final Intent mFallbackIntent;
private final SparseIntArray mConfigChangesMap = new SparseIntArray();
+ private final String mSetupWizardPkg;
private Consumer<Boolean> mOverviewChangeListener = b -> { };
@@ -86,6 +88,7 @@
new ComponentName(context.getPackageName(), info.activityInfo.name);
mMyHomeIntent.setComponent(myHomeComponent);
mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges);
+ mSetupWizardPkg = context.getString(R.string.setup_wizard_pkg);
ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
mFallbackIntent = new Intent(Intent.ACTION_MAIN)
@@ -127,6 +130,12 @@
private void updateOverviewTargets() {
ComponentName defaultHome = PackageManagerWrapper.getInstance()
.getHomeActivities(new ArrayList<>());
+ if (defaultHome != null && defaultHome.getPackageName().equals(mSetupWizardPkg)) {
+ // Treat setup wizard as null default home, because there is a period between setup and
+ // launcher being default home where it is briefly null. Otherwise, it would appear as
+ // if overview targets are changing twice, giving the listener an incorrect signal.
+ defaultHome = null;
+ }
mIsHomeDisabled = mDeviceState.isHomeDisabled();
mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 9b00dcf..f30d3f1 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -89,7 +89,7 @@
* Length of targets.apps should match that of {@link #mRemoteTargetHandles}.
*
* If split screen may be active when this is called, you might want to use
- * {@link #assignTargetsForSplitScreen(Context, RemoteAnimationTargets)}
+ * {@link #assignTargetsForSplitScreen(RemoteAnimationTargets)}
*/
public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) {
for (int i = 0; i < mRemoteTargetHandles.length; i++) {
@@ -102,43 +102,45 @@
}
/**
- * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this matches the
- * apps in targets.apps to that of the _active_ split screened tasks.
- * See {@link #assignTargetsForSplitScreen(RemoteAnimationTargets, int[])}
+ * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this assigns the
+ * apps in {@code targets.apps} to the {@link #mRemoteTargetHandles} with index 0 will being
+ * the left/top task, index 1 right/bottom.
*/
- public RemoteTargetHandle[] assignTargetsForSplitScreen(
- Context context, RemoteAnimationTargets targets) {
- int[] splitIds = TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds();
- return assignTargetsForSplitScreen(targets, splitIds);
- }
-
- /**
- * Assigns the provided splitIDs to the {@link #mRemoteTargetHandles}, with index 0 will being
- * the left/top task, index 1 right/bottom
- */
- public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets,
- int[] splitIds) {
- RemoteAnimationTarget topLeftTarget; // only one set if single/fullscreen task
- RemoteAnimationTarget bottomRightTarget;
+ public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) {
if (mRemoteTargetHandles.length == 1) {
// If we're not in split screen, the splitIds count doesn't really matter since we
// should always hit this case.
mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
if (targets.apps.length > 0) {
// Unclear why/when target.apps length == 0, but it sure does happen :(
- topLeftTarget = targets.apps[0];
- mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget, null);
+ mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(targets.apps[0], null);
}
} else {
- // split screen
- topLeftTarget = targets.findTask(splitIds[0]);
- bottomRightTarget = targets.findTask(splitIds[1]);
+ RemoteAnimationTarget topLeftTarget = targets.apps[0];
+
+ // Fetch the adjacent target for split screen.
+ RemoteAnimationTarget bottomRightTarget = null;
+ for (int i = 1; i < targets.apps.length; i++) {
+ final RemoteAnimationTarget target = targets.apps[i];
+ Rect topLeftBounds = getStartBounds(topLeftTarget);
+ Rect bounds = getStartBounds(target);
+ if (topLeftBounds.left > bounds.right || topLeftBounds.top > bounds.bottom) {
+ bottomRightTarget = topLeftTarget;
+ topLeftTarget = target;
+ break;
+ } else if (topLeftBounds.right < bounds.left || topLeftBounds.bottom < bounds.top) {
+ bottomRightTarget = target;
+ break;
+ }
+ }
// remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
// vice versa
mSplitBounds = new SplitBounds(
getStartBounds(topLeftTarget),
- getStartBounds(bottomRightTarget), splitIds[0], splitIds[1]);
+ getStartBounds(bottomRightTarget),
+ topLeftTarget.taskId,
+ bottomRightTarget.taskId);
mRemoteTargetHandles[0].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 1a72e3f..da97df6 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -190,7 +190,7 @@
if (forDesktop) {
remoteTargetHandles = gluer.assignTargetsForDesktop(targets);
} else if (v.containsMultipleTasks()) {
- remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets, v.getTaskIds());
+ remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets);
} else {
remoteTargetHandles = gluer.assignTargets(targets);
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index accab38..4680608 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -1132,6 +1132,10 @@
return;
}
+ // TODO(b/258022658): Remove temporary logging.
+ Log.i(TAG, "preloadOverview: forSUWAllSet=" + forSUWAllSet
+ + ", isHomeAndOverviewSame=" + mOverviewComponentObserver.isHomeAndOverviewSame());
+
mTaskAnimationManager.preloadRecentsAnimation(overviewIntent);
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
index 1630d0f..9982162 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
@@ -52,7 +52,7 @@
private final float mUnstashArea;
private final float mScreenWidth;
- private final int mTaskbarNavThresholdY;
+ private final int mTaskbarNavThreshold;
private final boolean mIsTaskbarAllAppsOpen;
private boolean mHasPassedTaskbarNavThreshold;
@@ -73,9 +73,7 @@
Resources res = context.getResources();
mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
- int taskbarNavThreshold = res.getDimensionPixelSize(R.dimen.taskbar_nav_threshold);
- int screenHeight = taskbarActivityContext.getDeviceProfile().heightPx;
- mTaskbarNavThresholdY = screenHeight - taskbarNavThreshold;
+ mTaskbarNavThreshold = res.getDimensionPixelSize(R.dimen.taskbar_nav_threshold);
mIsTaskbarAllAppsOpen =
mTaskbarActivityContext != null && mTaskbarActivityContext.isTaskbarAllAppsOpen();
@@ -157,7 +155,7 @@
if (mIsTransientTaskbar) {
float dY = mLastPos.y - mDownPos.y;
boolean passedTaskbarNavThreshold = dY < 0
- && mLastPos.y < mTaskbarNavThresholdY;
+ && Math.abs(dY) >= mTaskbarNavThreshold;
if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) {
mHasPassedTaskbarNavThreshold = true;
@@ -165,7 +163,7 @@
}
if (dY < 0) {
- dY = -OverScroll.dampedScroll(-dY, mTaskbarNavThresholdY);
+ dY = -OverScroll.dampedScroll(-dY, mTaskbarNavThreshold);
if (mTransitionCallback != null && !mIsTaskbarAllAppsOpen) {
mTransitionCallback.onActionMove(dY);
}
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 0389d07..79971de 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -88,6 +88,10 @@
private static final float ANIMATION_PAUSE_ALPHA_THRESHOLD = 0.1f;
+ private final Rect mTempSettingsBounds = new Rect();
+ private final Rect mTempInclusionBounds = new Rect();
+ private final Rect mTempExclusionBounds = new Rect();
+
private TISBindHelper mTISBindHelper;
private TISBinder mBinder;
@@ -131,9 +135,9 @@
!TextUtils.isEmpty(suwDeviceName)
? suwDeviceName : getString(R.string.default_device_name)));
- TextView tv = findViewById(R.id.navigation_settings);
- tv.setTextColor(accentColor);
- tv.setOnClickListener(v -> {
+ TextView settings = findViewById(R.id.navigation_settings);
+ settings.setTextColor(accentColor);
+ settings.setOnClickListener(v -> {
try {
startActivityForResult(
Intent.parseUri(URI_SYSTEM_NAVIGATION_SETTING, 0), 0);
@@ -142,12 +146,41 @@
}
});
- TextView hintTextView = findViewById(R.id.hint);
+ TextView hint = findViewById(R.id.hint);
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
if (!dp.isGestureMode) {
- hintTextView.setText(R.string.allset_button_hint);
+ hint.setText(R.string.allset_button_hint);
}
- hintTextView.setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
+ hint.setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
+
+ View textContent = findViewById(R.id.text_content_view);
+ textContent.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ mTempSettingsBounds.set(
+ settings.getLeft(),
+ settings.getTop(),
+ settings.getRight(),
+ settings.getBottom());
+ mTempInclusionBounds.set(
+ 0,
+ // Do not allow overlapping with the subtitle text
+ subtitle.getBottom(),
+ textContent.getWidth(),
+ textContent.getHeight());
+ mTempExclusionBounds.set(
+ hint.getLeft(),
+ hint.getTop(),
+ hint.getRight(),
+ hint.getBottom());
+
+ Utilities.translateOverlappingView(
+ settings,
+ mTempSettingsBounds,
+ mTempInclusionBounds,
+ mTempExclusionBounds,
+ Utilities.TRANSLATE_UP);
+ });
+
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mVibrator = getSystemService(Vibrator.class);
diff --git a/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
index bd0ce34..b508484 100644
--- a/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
+++ b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.graphics.Insets;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -34,6 +35,10 @@
/** Root layout that TutorialFragment uses to intercept motion events. */
public class RootSandboxLayout extends RelativeLayout {
+ private final Rect mTempStepIndicatorBounds = new Rect();
+ private final Rect mTempInclusionBounds = new Rect();
+ private final Rect mTempExclusionBounds = new Rect();
+
private View mFeedbackView;
private View mTutorialStepView;
private View mSkipButton;
@@ -98,18 +103,23 @@
private void updateTutorialStepViewTranslation(
@NonNull View anchorView, boolean translateToRight) {
- mTutorialStepView.setTranslationX(translateToRight
- ? Math.min(
- // Translate to the right if the views are overlapping on large fonts and
- // display sizes.
- Math.max(0, anchorView.getRight() - mTutorialStepView.getLeft()),
- // Do not translate beyond the bounds of the container view.
- mFeedbackView.getWidth() - mTutorialStepView.getRight())
- : Math.max(
- // Translate to the left if the views are overlapping on large fonts and
- // display sizes.
- Math.min(0, anchorView.getLeft() - mTutorialStepView.getRight()),
- // Do not translate beyond the bounds of the container view.
- -mTutorialStepView.getLeft()));
+ mTempStepIndicatorBounds.set(
+ mTutorialStepView.getLeft(),
+ mTutorialStepView.getTop(),
+ mTutorialStepView.getRight(),
+ mTutorialStepView.getBottom());
+ mTempInclusionBounds.set(0, 0, mFeedbackView.getWidth(), mFeedbackView.getHeight());
+ mTempExclusionBounds.set(
+ anchorView.getLeft(),
+ anchorView.getTop(),
+ anchorView.getRight(),
+ anchorView.getBottom());
+
+ Utilities.translateOverlappingView(
+ mTutorialStepView,
+ mTempStepIndicatorBounds,
+ mTempInclusionBounds,
+ mTempExclusionBounds,
+ translateToRight ? Utilities.TRANSLATE_RIGHT : Utilities.TRANSLATE_LEFT);
}
}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 0a155cb..4690d94 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -139,7 +139,7 @@
if (IS_VERBOSE) {
Log.d(TAG, String.format("\nwriteSnapshot(%d):\n%s", instanceId.getId(), info));
}
- if (!Utilities.ATLEAST_R || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (!Utilities.ATLEAST_R || Utilities.isRunningInTestHarness()) {
return;
}
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
@@ -438,7 +438,7 @@
}
// TODO: remove this when b/231648228 is fixed.
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
return;
}
int cardinality = mCardinality.orElseGet(() -> getCardinality(atomInfo));
@@ -636,7 +636,7 @@
}
private static int getCardinality(LauncherAtom.ItemInfo info) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
return 0;
}
switch (info.getContainerInfo().getContainerCase()) {
@@ -758,7 +758,7 @@
}
private static int getHierarchy(LauncherAtom.ItemInfo info) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
return 0;
}
if (info.getContainerInfo().getContainerCase() == FOLDER) {
@@ -801,7 +801,7 @@
}
private static int getSearchAttributes(LauncherAtom.ItemInfo info) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
return 0;
}
ContainerInfo containerInfo = info.getContainerInfo();
diff --git a/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java b/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java
index effdfdd..f6b2441 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java
@@ -15,12 +15,13 @@
*/
package com.android.quickstep.util;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_MOVE_FROM_CENTER_ANIM;
+
import android.annotation.NonNull;
import android.view.View;
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.widget.NavigableAppWidgetHostView;
+import com.android.launcher3.Reorderable;
+import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.TranslationApplier;
/**
@@ -31,12 +32,9 @@
@Override
public void apply(@NonNull View view, float x, float y) {
- if (view instanceof NavigableAppWidgetHostView) {
- ((NavigableAppWidgetHostView) view).setTranslationForMoveFromCenterAnimation(x, y);
- } else if (view instanceof BubbleTextView) {
- ((BubbleTextView) view).setTranslationForMoveFromCenterAnimation(x, y);
- } else if (view instanceof FolderIcon) {
- ((FolderIcon) view).setTranslationForMoveFromCenterAnimation(x, y);
+ if (view instanceof Reorderable) {
+ MultiTranslateDelegate mtd = ((Reorderable) view).getTranslateDelegate();
+ mtd.setTranslation(INDEX_MOVE_FROM_CENTER_ANIM, x, y);
} else {
view.setTranslationX(x);
view.setTranslationY(y);
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 69ed2f8..4bc41bc 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -129,7 +129,7 @@
* @param pointerIndex Index for the pointer being tracked in the motion event
*/
public void addPosition(MotionEvent ev, int pointerIndex) {
- long timeoutMs = Utilities.IS_RUNNING_IN_TEST_HARNESS
+ long timeoutMs = Utilities.isRunningInTestHarness()
? TEST_HARNESS_TRIGGER_TIMEOUT
: mMakePauseHarderToTrigger
? HARDER_TRIGGER_TIMEOUT
@@ -195,7 +195,7 @@
if (mIsPaused != isPaused) {
mIsPaused = isPaused;
String logString = "onMotionPauseChanged, paused=" + mIsPaused + " reason=" + reason;
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
Log.d(TAG, logString);
}
ActiveGestureLog.INSTANCE.addLog(logString);
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index e928b27..cf07e6e 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -64,7 +64,7 @@
});
}
- if (!Utilities.IS_RUNNING_IN_TEST_HARNESS
+ if (!Utilities.isRunningInTestHarness()
&& !hasReachedMaxCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
stateManager.addStateListener(new StateListener<LauncherState>() {
boolean mFromAllApps = false;
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index e4c2dae..f94d80f 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -150,7 +150,7 @@
// to other classes like PipTaskOrganizer / RecentsAnimationController to complete
// the cleanup.
if (SystemProperties.getBoolean(
- "persist.wm.debug.enable_pip_app_icon_overlay", false)) {
+ "persist.wm.debug.enable_pip_app_icon_overlay", true)) {
mPipContentOverlay = new PipContentOverlay.PipAppIconOverlay(view.getContext(),
mAppBounds, mActivityInfo);
} else {
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index 89177b6..14898b1 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -381,6 +381,7 @@
}
setOverlayEnabled(false);
onTaskListVisibilityChanged(false);
+ setVisibility(VISIBLE);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 0284d29..ac59403 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -147,6 +147,8 @@
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.DynamicResource;
@@ -706,6 +708,12 @@
private ObjectAnimator mActionsViewAlphaAnimator;
private float mActionsViewAlphaAnimatorFinalValue;
+ /**
+ * Keeps track of the desktop task. Optional and only present when the feature flag is enabled.
+ */
+ @Nullable
+ private DesktopTaskView mDesktopTaskView;
+
private MultiWindowModeChangedListener mMultiWindowModeChangedListener =
new MultiWindowModeChangedListener() {
@Override
@@ -1586,6 +1594,8 @@
// update the map of instance counts
mFilterState.updateInstanceCountMap(taskGroups);
+ // Clear out desktop view if it is set
+ mDesktopTaskView = null;
DesktopTask desktopTask = null;
// Add views as children based on whether it's grouped or single task. Looping through
@@ -1644,12 +1654,13 @@
if (!taskGroups.isEmpty()) {
addView(mClearAllButton);
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
- TaskView taskView = getTaskViewFromPool(TaskView.Type.DESKTOP);
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED
+ && !getSplitSelectController().isSplitSelectActive()) {
+ mDesktopTaskView = (DesktopTaskView) getTaskViewFromPool(TaskView.Type.DESKTOP);
// Always add a desktop task to the first position. Even if it is empty
- addView(taskView, 0);
+ addView(mDesktopTaskView, 0);
ArrayList<Task> tasks = desktopTask != null ? desktopTask.tasks : new ArrayList<>();
- ((DesktopTaskView) taskView).bind(tasks, mOrientationState);
+ mDesktopTaskView.bind(tasks, mOrientationState);
}
}
@@ -1709,7 +1720,7 @@
int finalTargetPage = targetPage;
runOnPageScrollsInitialized(() -> {
// TODO(b/246283207): Remove logging once root cause of flake detected.
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
Log.d("b/246283207", "RecentsView#applyLoadPlan() -> "
+ "previousCurrentPage: " + previousCurrentPage
+ ", targetPage: " + finalTargetPage
@@ -2774,6 +2785,10 @@
} else if (taskView.isDesktopTask()) {
// Desktop task was not focused. Pin it to the right of focused
desktopTaskIndex = i;
+ if (taskView.getVisibility() == View.GONE) {
+ // Desktop task view is hidden, skip it from grid calculations
+ continue;
+ }
if (!ENABLE_GRID_ONLY_OVERVIEW.get()) {
// Only apply x-translation when using legacy overview grid
gridTranslations[i] += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
@@ -4467,6 +4482,9 @@
mSplitSelectStateController.setAnimateCurrentTaskDismissal(
true /*animateCurrentTaskDismissal*/);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ updateDesktopTaskVisibility(false /* visible */);
+ }
}
/**
@@ -4475,6 +4493,7 @@
* Attempts to initiate split with an existing taskView, if one exists
*/
public void initiateSplitSelect(SplitSelectSource splitSelectSource) {
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "enterSplitSelect");
mSplitSelectSource = splitSelectSource;
mSplitHiddenTaskView = getTaskViewByTaskId(splitSelectSource.alreadyRunningTaskId);
mSplitHiddenTaskViewIndex = indexOfChild(mSplitHiddenTaskView);
@@ -4483,6 +4502,15 @@
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTaskId);
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ updateDesktopTaskVisibility(false /* visible */);
+ }
+ }
+
+ private void updateDesktopTaskVisibility(boolean visible) {
+ if (mDesktopTaskView != null) {
+ mDesktopTaskView.setVisibility(visible ? VISIBLE : GONE);
+ }
}
/**
@@ -4633,6 +4661,9 @@
mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE);
mSplitHiddenTaskView = null;
}
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ updateDesktopTaskVisibility(true /* visible */);
+ }
}
private void safeRemoveDragLayerView(@Nullable View viewToRemove) {
@@ -5004,8 +5035,7 @@
mRemoteTargetHandles = gluer.assignTargetsForDesktop(recentsAnimationTargets);
} else {
gluer = new RemoteTargetGluer(getContext(), getSizeStrategy());
- mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(
- getContext(), recentsAnimationTargets);
+ mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(recentsAnimationTargets);
}
mSplitBoundsConfig = gluer.getSplitBounds();
// Add release check to the targets from the RemoteTargetGluer and not the targets
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
new file mode 100644
index 0000000..f10b917
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import android.content.Intent;
+
+import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TaplTestsSplitscreen extends AbstractQuickStepTest {
+ private static final String CALCULATOR_APP_NAME = "Calculator";
+ private static final String CALCULATOR_APP_PACKAGE =
+ resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ TaplTestsLauncher3.initialize(this);
+
+ mLauncher.getWorkspace()
+ .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0))
+ .switchToAllApps()
+ .getAppIcon(CALCULATOR_APP_NAME)
+ .dragToHotseat(0);
+
+ startAppFast(CALCULATOR_APP_PACKAGE);
+ mLauncher.enableBlockTimeout(true);
+ mLauncher.showTaskbarIfHidden();
+ }
+
+ @After
+ public void tearDown() {
+ mLauncher.enableBlockTimeout(false);
+ }
+
+ @Test
+ // TODO (b/270201357): When this test is proven stable, remove this TestStabilityRule and
+ // introduce into presubmit as well.
+ @TestStabilityRule.Stability(
+ flavors = TestStabilityRule.LOCAL | TestStabilityRule.PLATFORM_POSTSUBMIT)
+ @PortraitLandscape
+ @TaskbarModeSwitch
+ public void testSplitAppFromHomeWithItself() throws Exception {
+ Assume.assumeTrue(mLauncher.isTablet());
+
+ mLauncher.goHome()
+ .switchToAllApps()
+ .getAppIcon(CALCULATOR_APP_NAME)
+ .openMenu()
+ .getSplitScreenMenuItem()
+ .click();
+
+ mLauncher.getLaunchedAppState()
+ .getTaskbar()
+ .getAppIcon(CALCULATOR_APP_NAME)
+ .launchIntoSplitScreen();
+ }
+}
diff --git a/res/color-night-v31/transient_taskbar_background.xml b/res/color-night-v31/taskbar_background.xml
similarity index 93%
rename from res/color-night-v31/transient_taskbar_background.xml
rename to res/color-night-v31/taskbar_background.xml
index 40f6494..8df1686 100644
--- a/res/color-night-v31/transient_taskbar_background.xml
+++ b/res/color-night-v31/taskbar_background.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
+<!-- Copyright (C) 2023 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.
@@ -16,4 +16,3 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@android:color/system_neutral1_500" android:lStar="15" />
</selector>
-
diff --git a/res/color-v31/taskbar_background.xml b/res/color-v31/taskbar_background.xml
index eaf676f..c2bcab8 100644
--- a/res/color-v31/taskbar_background.xml
+++ b/res/color-v31/taskbar_background.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_500" android:lStar="15" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="95" />
</selector>
diff --git a/res/color-v31/transient_taskbar_background.xml b/res/color-v31/transient_taskbar_background.xml
deleted file mode 100644
index bce947d..0000000
--- a/res/color-v31/transient_taskbar_background.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_500" android:lStar="95" />
-</selector>
-
diff --git a/res/drawable/bg_work_apps_paused_action_button.xml b/res/drawable/bg_work_apps_paused_action_button.xml
index 74d4693..57d1ae5 100644
--- a/res/drawable/bg_work_apps_paused_action_button.xml
+++ b/res/drawable/bg_work_apps_paused_action_button.xml
@@ -21,11 +21,12 @@
<solid android:color="@android:color/white" />
</shape>
</item>
-
<item android:id="@android:id/background">
<shape android:shape="rectangle">
- <solid android:color="?android:attr/colorControlHighlight" />
<corners android:radius="@dimen/rounded_button_radius" />
+ <stroke
+ android:width="@dimen/work_apps_paused_button_stroke"
+ android:color="@color/work_turn_on_stroke" />
</shape>
</item>
</ripple>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index b02e3e3..455217f 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -23,19 +23,20 @@
android:clipToPadding="false"
android:layout_below="@id/collapse_handle"
android:descendantFocusability="afterDescendants"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
launcher:pageIndicator="@+id/tabs" >
<com.android.launcher3.widget.picker.WidgetsRecyclerView
android:id="@+id/primary_widgets_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToPadding="false" />
<com.android.launcher3.widget.picker.WidgetsRecyclerView
android:id="@+id/work_widgets_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToPadding="false" />
</com.android.launcher3.widget.picker.WidgetPagedView>
@@ -47,6 +48,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/collapse_handle"
android:paddingBottom="0dp"
+ android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToOutline="true"
android:orientation="vertical">
@@ -57,7 +59,6 @@
android:gravity="center_horizontal"
android:textSize="24sp"
android:layout_marginTop="24dp"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:textColor="?android:attr/textColorSecondary"
android:text="@string/widget_button_text"/>
@@ -68,7 +69,6 @@
android:elevation="0.1dp"
android:background="?android:attr/colorBackground"
android:paddingBottom="8dp"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
launcher:layout_sticky="true">
<include layout="@layout/widgets_search_bar" />
</FrameLayout>
@@ -80,7 +80,6 @@
android:layout_marginTop="8dp"
android:background="@drawable/widgets_surface_background"
android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
- android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:visibility="gone" />
<com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
@@ -90,7 +89,6 @@
android:gravity="center_horizontal"
android:orientation="horizontal"
android:paddingVertical="8dp"
- android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:background="?android:attr/colorBackground"
style="@style/TextHeadline"
launcher:layout_sticky="true">
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index 366d2d2..887f00c 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -20,7 +20,7 @@
android:layout_below="@id/collapse_handle"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToPadding="false" />
<!-- SearchAndRecommendationsView without the tab layout as well -->
@@ -30,7 +30,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/collapse_handle"
android:paddingBottom="16dp"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
+ android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToOutline="true"
android:orientation="vertical">
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index f614d9b..52c5a49 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -21,7 +21,7 @@
<TextView
style="@style/PrimaryHeadline"
- android:textColor="?attr/workProfileOverlayTextColor"
+ android:textColor="?android:attr/textColorPrimary"
android:id="@+id/work_apps_paused_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -45,7 +45,7 @@
android:layout_width="wrap_content"
android:layout_height="@dimen/rounded_button_height"
android:id="@+id/enable_work_apps"
- android:textColor="?attr/workProfileOverlayTextColor"
+ android:textColor="?android:attr/textColorPrimary"
android:text="@string/work_apps_enable_btn_text"
android:textAlignment="center"
android:background="@drawable/bg_work_apps_paused_action_button"
diff --git a/res/values-night-v31/colors.xml b/res/values-night-v31/colors.xml
index 2c1bc90..f331361 100644
--- a/res/values-night-v31/colors.xml
+++ b/res/values-night-v31/colors.xml
@@ -24,4 +24,6 @@
<color name="home_settings_thumb_off_color">@android:color/system_neutral2_300</color>
<color name="home_settings_track_on_color">@android:color/system_accent2_700</color>
<color name="home_settings_track_off_color">@android:color/system_neutral1_700</color>
+
+ <color name="all_apps_button_color">@android:color/system_neutral2_200</color>
</resources>
\ No newline at end of file
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index 4ba77fa..17fe419 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -17,5 +17,5 @@
-->
<resources>
- <color name="all_apps_button_color">@color/all_apps_button_color_dark</color>
+ <color name="all_apps_button_color">#BFC8CC</color>
</resources>
\ No newline at end of file
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index f87d9fc..054fe47 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -62,8 +62,7 @@
<color name="preload_icon_accent_color_dark">@android:color/system_accent1_300</color>
<color name="preload_icon_background_color_dark">@android:color/system_neutral2_700</color>
- <color name="all_apps_button_color_light">@android:color/system_neutral2_700</color>
- <color name="all_apps_button_color_dark">@android:color/system_neutral2_200</color>
+ <color name="all_apps_button_color">@android:color/system_neutral2_700</color>
<color name="widget_picker_background_selected">@android:color/system_accent2_100</color>
</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index f70937a..96938ca 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -50,7 +50,6 @@
<attr name="folderTextColor" format="color" />
<attr name="folderHintColor" format="color" />
<attr name="isFolderDarkText" format="boolean" />
- <attr name="workProfileOverlayTextColor" format="color" />
<attr name="workspaceAccentColor" format="color" />
<attr name="dropTargetHoverTextColor" format="color" />
<attr name="preloadIconAccentColor" format="color" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index ef7bf91..8788557 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -80,12 +80,12 @@
<color name="workspace_accent_color_light">#ff8df5e3</color>
<color name="workspace_accent_color_dark">#ff3d665f</color>
- <color name="all_apps_button_color">@color/all_apps_button_color_light</color>
- <color name="all_apps_button_color_light">#40484B</color>
- <color name="all_apps_button_color_dark">#BFC8CC</color>
+ <color name="all_apps_button_color">#40484B</color>
<color name="preload_icon_accent_color_light">#00668B</color>
<color name="preload_icon_background_color_light">#B5CAD7</color>
<color name="preload_icon_accent_color_dark">#4BB6E8</color>
<color name="preload_icon_background_color_dark">#40484D</color>
+
+ <color name="work_turn_on_stroke">?android:attr/colorAccent</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 82758bf..aa84d2b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -155,6 +155,7 @@
<dimen name="work_edu_card_margin">16dp</dimen>
<dimen name="work_edu_card_radius">16dp</dimen>
<dimen name="work_edu_card_bottom_margin">26dp</dimen>
+ <dimen name="work_apps_paused_button_stroke">1dp</dimen>
<dimen name="work_card_margin">24dp</dimen>
<!-- (x) icon button inside work edu card -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 474a289..65d215f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -59,7 +59,6 @@
<item name="folderHintColor">@color/folder_hint_text_color_dark</item>
<item name="loadingIconColor">#CCFFFFFF</item>
<item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
- <item name="workProfileOverlayTextColor">#FF212121</item>
<item name="eduHalfSheetBGColor">?android:attr/colorAccent</item>
<item name="workspaceAccentColor">@color/workspace_accent_color_light</item>
<item name="dropTargetHoverTextColor">@color/workspace_text_color_dark</item>
@@ -114,7 +113,6 @@
<item name="isMainColorDark">true</item>
<item name="loadingIconColor">#99FFFFFF</item>
<item name="iconOnlyShortcutColor">#B3FFFFFF</item>
- <item name="workProfileOverlayTextColor">@android:color/white</item>
<item name="eduHalfSheetBGColor">#DD000000</item>
<item name="overviewScrimColor">@color/overview_scrim_dark</item>
<item name="preloadIconAccentColor">@color/preload_icon_accent_color_dark</item>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 94b8cd8..bc4a5c3 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -827,6 +827,6 @@
private boolean hasSeenReconfigurableWidgetEducationTip() {
return mLauncher.getSharedPrefs()
.getBoolean(KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN, false)
- || Utilities.IS_RUNNING_IN_TEST_HARNESS;
+ || Utilities.isRunningInTestHarness();
}
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index df38c26..3eb03ed 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -34,7 +34,6 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -71,6 +70,7 @@
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.views.ActivityContext;
@@ -100,21 +100,8 @@
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
- private final PointF mTranslationForReorderBounce = new PointF(0, 0);
- private final PointF mTranslationForReorderPreview = new PointF(0, 0);
-
- private float mTranslationXForTaskbarAlignmentAnimation = 0f;
- private float mTranslationYForTaskbarAlignmentAnimation = 0f;
-
- private float mTranslationXForTaskbarRevealAnimation = 0f;
- private float mTranslationYForTaskbarRevealAnimation = 0f;
-
- private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
-
private float mScaleForReorderBounce = 1f;
- private float mTranslationXForTaskbarAllAppsIcon = 0f;
-
private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
= new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
@Override
@@ -142,6 +129,7 @@
}
};
+ private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
private final ActivityContext mActivity;
private FastBitmapDrawable mIcon;
private boolean mCenterVertically;
@@ -960,131 +948,23 @@
mDisplay == DISPLAY_SEARCH_RESULT_SMALL;
}
- private void updateTranslation() {
- super.setTranslationX(mTranslationForReorderBounce.x
- + mTranslationForReorderPreview.x
- + mTranslationXForTaskbarAllAppsIcon
- + mTranslationForMoveFromCenterAnimation.x
- + mTranslationXForTaskbarAlignmentAnimation
- + mTranslationXForTaskbarRevealAnimation
- );
- super.setTranslationY(mTranslationForReorderBounce.y
- + mTranslationForReorderPreview.y
- + mTranslationForMoveFromCenterAnimation.y
- + mTranslationYForTaskbarAlignmentAnimation
- + mTranslationYForTaskbarRevealAnimation);
- }
-
- /**
- * Sets translationX for taskbar all apps icon
- */
- public void setTranslationXForTaskbarAllAppsIcon(float translationX) {
- mTranslationXForTaskbarAllAppsIcon = translationX;
- updateTranslation();
- }
-
- public void setReorderBounceOffset(float x, float y) {
- mTranslationForReorderBounce.set(x, y);
- updateTranslation();
- }
-
- public void getReorderBounceOffset(PointF offset) {
- offset.set(mTranslationForReorderBounce);
+ @Override
+ public MultiTranslateDelegate getTranslateDelegate() {
+ return mTranslateDelegate;
}
@Override
- public void setReorderPreviewOffset(float x, float y) {
- mTranslationForReorderPreview.set(x, y);
- updateTranslation();
- }
-
- @Override
- public void getReorderPreviewOffset(PointF offset) {
- offset.set(mTranslationForReorderPreview);
- }
-
public void setReorderBounceScale(float scale) {
mScaleForReorderBounce = scale;
super.setScaleX(scale);
super.setScaleY(scale);
}
+ @Override
public float getReorderBounceScale() {
return mScaleForReorderBounce;
}
- /**
- * Sets translation values for move from center animation
- */
- public void setTranslationForMoveFromCenterAnimation(float x, float y) {
- mTranslationForMoveFromCenterAnimation.set(x, y);
- updateTranslation();
- }
-
- /**
- * Sets translationX for taskbar to launcher alignment animation
- */
- public void setTranslationXForTaskbarAlignmentAnimation(float translationX) {
- mTranslationXForTaskbarAlignmentAnimation = translationX;
- updateTranslation();
- }
-
- /**
- * Returns translationX value for taskbar to launcher alignment animation
- */
- public float getTranslationXForTaskbarAlignmentAnimation() {
- return mTranslationXForTaskbarAlignmentAnimation;
- }
-
- /**
- * Sets translationX for taskbar to launcher alignment animation
- */
- public void setTranslationYForTaskbarAlignmentAnimation(float translationY) {
- mTranslationYForTaskbarAlignmentAnimation = translationY;
- updateTranslation();
- }
-
- /**
- * Returns translationY value for taskbar to launcher alignment animation
- */
- public float getTranslationYForTaskbarAlignmentAnimation() {
- return mTranslationYForTaskbarAlignmentAnimation;
- }
-
- /**
- * Sets translationX value for taskbar reveal animation
- */
- public void setTranslationXForTaskbarRevealAnimation(float translationX) {
- mTranslationXForTaskbarRevealAnimation = translationX;
- updateTranslation();
- }
-
- /**
- * Returns translation values for taskbar reveal animation
- */
- public float getTranslationXForTaskbarRevealAnimation() {
- return mTranslationXForTaskbarRevealAnimation;
- }
-
- /**
- * Sets translationY value for taskbar reveal animation
- */
- public void setTranslationYForTaskbarRevealAnimation(float translationY) {
- mTranslationYForTaskbarRevealAnimation = translationY;
- updateTranslation();
- }
-
- /**
- * Returns translationY values for taskbar reveal animation
- */
- public float getTranslationYForTaskbarRevealAnimation() {
- return mTranslationYForTaskbarRevealAnimation;
- }
-
- public View getView() {
- return this;
- }
-
@Override
public int getViewType() {
return DRAGGABLE_ICON;
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index b96e4df..38b0e08 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -22,6 +22,8 @@
import static com.android.launcher3.config.FeatureFlags.SHOW_HOME_GARDENING;
import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_REORDER_BOUNCE_OFFSET;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_REORDER_PREVIEW_OFFSET;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -63,6 +65,7 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.CellPosMapper.CellPos;
+import com.android.launcher3.celllayout.ReorderAlgorithm;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
@@ -70,6 +73,7 @@
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.util.ParcelableSparseArray;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
@@ -112,7 +116,7 @@
final PointF mTmpPointF = new PointF();
protected GridOccupancy mOccupied;
- protected GridOccupancy mTmpOccupied;
+ public GridOccupancy mTmpOccupied;
private OnTouchListener mInterceptTouchListener;
@@ -191,7 +195,7 @@
private final ArrayList<View> mIntersectingViews = new ArrayList<>();
private final Rect mOccupiedRect = new Rect();
- private final int[] mDirectionVector = new int[2];
+ public final int[] mDirectionVector = new int[2];
ItemConfiguration mPreviousSolution = null;
private static final int INVALID_DIRECTION = -100;
@@ -1099,13 +1103,12 @@
lp.isLockedToGrid = false;
// End compute new x and y
- item.getReorderPreviewOffset(mTmpPointF);
- final float initPreviewOffsetX = mTmpPointF.x;
- final float initPreviewOffsetY = mTmpPointF.y;
+ MultiTranslateDelegate mtd = item.getTranslateDelegate();
+ float initPreviewOffsetX = mtd.getTranslationX(INDEX_REORDER_PREVIEW_OFFSET).getValue();
+ float initPreviewOffsetY = mtd.getTranslationY(INDEX_REORDER_PREVIEW_OFFSET).getValue();
final float finalPreviewOffsetX = newX - oldX;
final float finalPreviewOffsetY = newY - oldY;
-
// Exit early if we're not actually moving the view
if (finalPreviewOffsetX == 0 && finalPreviewOffsetY == 0
&& initPreviewOffsetX == 0 && initPreviewOffsetY == 0) {
@@ -1123,7 +1126,7 @@
float r = (Float) animation.getAnimatedValue();
float x = (1 - r) * initPreviewOffsetX + r * finalPreviewOffsetX;
float y = (1 - r) * initPreviewOffsetY + r * finalPreviewOffsetY;
- item.setReorderPreviewOffset(x, y);
+ item.getTranslateDelegate().setTranslation(INDEX_REORDER_PREVIEW_OFFSET, x, y);
}
});
va.addListener(new AnimatorListenerAdapter() {
@@ -1134,7 +1137,8 @@
// place just yet.
if (!cancelled) {
lp.isLockedToGrid = true;
- item.setReorderPreviewOffset(0, 0);
+ item.getTranslateDelegate()
+ .setTranslation(INDEX_REORDER_PREVIEW_OFFSET, 0, 0);
child.requestLayout();
}
if (mReorderAnimators.containsKey(lp)) {
@@ -1243,8 +1247,8 @@
* @return The X, Y cell of a vacant area that can contain this object,
* nearest the requested location.
*/
- int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
- int spanY, int[] result, int[] resultSpan) {
+ public int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY,
+ int spanX, int spanY, int[] result, int[] resultSpan) {
return findNearestArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, false,
result, resultSpan);
}
@@ -1377,6 +1381,10 @@
return bestXY;
}
+ public GridOccupancy getOccupied() {
+ return mOccupied;
+ }
+
private void copySolutionToTempState(ItemConfiguration solution, View dragView) {
mTmpOccupied.clear();
@@ -1434,7 +1442,7 @@
CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (c != null && !skip && (child instanceof Reorderable)) {
- ReorderPreviewAnimation rha = new ReorderPreviewAnimation((Reorderable) child,
+ ReorderPreviewAnimation rha = new ReorderPreviewAnimation(child,
mode, lp.getCellX(), lp.getCellY(), c.cellX, c.cellY, c.spanX, c.spanY);
rha.animate();
}
@@ -1456,8 +1464,8 @@
// Class which represents the reorder preview animations. These animations show that an item is
// in a temporary state, and hint at where the item will return to.
- class ReorderPreviewAnimation {
- final Reorderable child;
+ class ReorderPreviewAnimation<T extends View & Reorderable> {
+ final T child;
float finalDeltaX;
float finalDeltaY;
float initDeltaX;
@@ -1477,7 +1485,7 @@
float animationProgress = 0;
ValueAnimator a;
- public ReorderPreviewAnimation(Reorderable child, int mode, int cellX0, int cellY0,
+ ReorderPreviewAnimation(View childView, int mode, int cellX0, int cellY0,
int cellX1, int cellY1, int spanX, int spanY) {
regionToCenterPoint(cellX0, cellY0, spanX, spanY, mTmpPoint);
final int x0 = mTmpPoint[0];
@@ -1488,16 +1496,16 @@
final int dX = x1 - x0;
final int dY = y1 - y0;
- this.child = child;
+ this.child = (T) childView;
this.mode = mode;
finalDeltaX = 0;
finalDeltaY = 0;
- child.getReorderBounceOffset(mTmpPointF);
- initDeltaX = mTmpPointF.x;
- initDeltaY = mTmpPointF.y;
+ MultiTranslateDelegate mtd = child.getTranslateDelegate();
+ initDeltaX = mtd.getTranslationX(INDEX_REORDER_BOUNCE_OFFSET).getValue();
+ initDeltaY = mtd.getTranslationY(INDEX_REORDER_BOUNCE_OFFSET).getValue();
initScale = child.getReorderBounceScale();
- finalScale = mChildScale - (CHILD_DIVIDEND / child.getView().getWidth()) * initScale;
+ finalScale = mChildScale - (CHILD_DIVIDEND / child.getWidth()) * initScale;
int dir = mode == MODE_HINT ? -1 : 1;
if (dX == dY && dX == 0) {
@@ -1573,7 +1581,7 @@
float r1 = (mode == MODE_HINT && repeating) ? 1.0f : animationProgress;
float x = r1 * finalDeltaX + (1 - r1) * initDeltaX;
float y = r1 * finalDeltaY + (1 - r1) * initDeltaY;
- child.setReorderBounceOffset(x, y);
+ child.getTranslateDelegate().setTranslation(INDEX_REORDER_BOUNCE_OFFSET, x, y);
float s = animationProgress * finalScale + (1 - animationProgress) * initScale;
child.setReorderBounceScale(s);
}
@@ -1652,38 +1660,8 @@
}
}
- /**
- * Returns a "reorder" where we simply drop the item in the closest empty space, without moving
- * any other item in the way.
- *
- * @param pixelX X coordinate in pixels in the screen
- * @param pixelY Y coordinate in pixels in the screen
- * @param spanX horizontal cell span
- * @param spanY vertical cell span
- * @return the configuration that represents the found reorder
- */
- ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, int minSpanX,
- int minSpanY, int spanX, int spanY) {
- ItemConfiguration solution = new ItemConfiguration();
- int[] result = new int[2];
- int[] resultSpan = new int[2];
- findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, result,
- resultSpan);
- if (result[0] >= 0 && result[1] >= 0) {
- copyCurrentStateToSolution(solution, false);
- solution.cellX = result[0];
- solution.cellY = result[1];
- solution.spanX = resultSpan[0];
- solution.spanY = resultSpan[1];
- solution.isSolution = true;
- } else {
- solution.isSolution = false;
- }
- return solution;
- }
-
// For a given cell and span, fetch the set of views intersecting the region.
- private void getViewsIntersectingRegion(int cellX, int cellY, int spanX, int spanY,
+ public void getViewsIntersectingRegion(int cellX, int cellY, int spanX, int spanY,
View dragView, Rect boundingRect, ArrayList<View> intersectingViews) {
if (boundingRect != null) {
boundingRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
@@ -1708,7 +1686,7 @@
}
}
- boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY,
+ public boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY,
View dragView, int[] result) {
result = findNearestAreaIgnoreOccupied(pixelX, pixelY, spanX, spanY, result);
getViewsIntersectingRegion(result[0], result[1], spanX, spanY, dragView, null,
@@ -2254,7 +2232,7 @@
those cells. Instead we use some heuristics to often lock the vector to up, down, left
or right, which helps make pushing feel right.
*/
- private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
+ public void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
int spanY, View dragView, int[] resultDirection) {
//TODO(adamcohen) b/151776141 use the items visual center for the direction vector
@@ -2346,7 +2324,7 @@
return success;
}
- private boolean rearrangementExists(int cellX, int cellY, int spanX, int spanY, int[] direction,
+ public boolean rearrangementExists(int cellX, int cellY, int spanX, int spanY, int[] direction,
View ignoreView, ItemConfiguration solution) {
// Return early if get invalid cell positions
if (cellX < 0 || cellY < 0) return false;
@@ -2402,55 +2380,18 @@
return true;
}
+ public ReorderAlgorithm createReorderAlgorithm() {
+ return new ReorderAlgorithm(this);
+ }
+
protected ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
ItemConfiguration solution) {
- return findReorderSolutionRecursive(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
- direction, dragView, decX, solution);
+ return createReorderAlgorithm().findReorderSolution(pixelX, pixelY, minSpanX, minSpanY,
+ spanX, spanY, direction, dragView, decX, solution);
}
- protected ItemConfiguration findReorderSolutionRecursive(int pixelX, int pixelY, int minSpanX,
- int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
- ItemConfiguration solution) {
- // Copy the current state into the solution. This solution will be manipulated as necessary.
- copyCurrentStateToSolution(solution, false);
- // Copy the current occupied array into the temporary occupied array. This array will be
- // manipulated as necessary to find a solution.
- mOccupied.copyTo(mTmpOccupied);
-
- // We find the nearest cell into which we would place the dragged item, assuming there's
- // nothing in its way.
- int result[] = new int[2];
- result = findNearestAreaIgnoreOccupied(pixelX, pixelY, spanX, spanY, result);
-
- boolean success;
- // First we try the exact nearest position of the item being dragged,
- // we will then want to try to move this around to other neighbouring positions
- success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
- solution);
-
- if (!success) {
- // We try shrinking the widget down to size in an alternating pattern, shrink 1 in
- // x, then 1 in y etc.
- if (spanX > minSpanX && (minSpanY == spanY || decX)) {
- return findReorderSolutionRecursive(pixelX, pixelY, minSpanX, minSpanY, spanX - 1,
- spanY, direction, dragView, false, solution);
- } else if (spanY > minSpanY) {
- return findReorderSolutionRecursive(pixelX, pixelY, minSpanX, minSpanY, spanX,
- spanY - 1, direction, dragView, true, solution);
- }
- solution.isSolution = false;
- } else {
- solution.isSolution = true;
- solution.cellX = result[0];
- solution.cellY = result[1];
- solution.spanX = spanX;
- solution.spanY = spanY;
- }
- return solution;
- }
-
- protected void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
+ public void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
@@ -2466,35 +2407,6 @@
}
/**
- * Returns a "reorder" if there is empty space without rearranging anything.
- *
- * @param pixelX X coordinate in pixels in the screen
- * @param pixelY Y coordinate in pixels in the screen
- * @param spanX horizontal cell span
- * @param spanY vertical cell span
- * @param dragView view being dragged in reorder
- * @return the configuration that represents the found reorder
- */
- public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
- int spanY, View dragView) {
- int[] result = new int[2];
- if (isNearestDropLocationOccupied(pixelX, pixelY, spanX, spanY, dragView, result)) {
- result[0] = result[1] = -1;
- }
- ItemConfiguration solution = new ItemConfiguration();
- copyCurrentStateToSolution(solution, false);
- solution.isSolution = result[0] != -1;
- if (!solution.isSolution) {
- return solution;
- }
- solution.cellX = result[0];
- solution.cellY = result[1];
- solution.spanX = spanX;
- solution.spanY = spanY;
- return solution;
- }
-
- /**
* When the user drags an Item in the workspace sometimes we need to move the items already in
* the workspace to make space for the new item, this function return a solution for that
* reorder.
@@ -2511,29 +2423,8 @@
*/
public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX, int minSpanY,
int spanX, int spanY, View dragView) {
- getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector);
-
- ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(pixelX, pixelY, spanX, spanY,
- dragView);
-
- // Find a solution involving pushing / displacing any items in the way
- ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX, minSpanY,
- spanX, spanY, mDirectionVector, dragView, true, new ItemConfiguration());
-
- // We attempt the approach which doesn't shuffle views at all
- ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(pixelX, pixelY, minSpanX,
- minSpanY, spanX, spanY);
-
- // If the reorder solution requires resizing (shrinking) the item being dropped, we instead
- // favor a solution in which the item is not resized, but
- if (swapSolution.isSolution && swapSolution.area() >= closestSpaceSolution.area()) {
- return swapSolution;
- } else if (closestSpaceSolution.isSolution) {
- return closestSpaceSolution;
- } else if (dropInPlaceSolution.isSolution) {
- return dropInPlaceSolution;
- }
- return null;
+ return createReorderAlgorithm().calculateReorder(pixelX, pixelY, minSpanX, minSpanY,
+ spanX, spanY, dragView);
}
int[] performReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
@@ -2585,7 +2476,7 @@
* {@link MODE_ON_DROP}, {@link MODE_ON_DROP_EXTERNAL}, {@link MODE_ACCEPT_DROP}
* defined in {@link CellLayout}.
*/
- void performReorder(ItemConfiguration solution, View dragView, int mode) {
+ public void performReorder(ItemConfiguration solution, View dragView, int mode) {
if (mode == MODE_SHOW_REORDER_HINT) {
beginOrAdjustReorderPreviewAnimations(solution, dragView,
ReorderPreviewAnimation.MODE_HINT);
@@ -2631,38 +2522,41 @@
return mItemPlacementDirty;
}
- static class ItemConfiguration extends CellAndSpan {
- final ArrayMap<View, CellAndSpan> map = new ArrayMap<>();
+ /**
+ * Represents the solution to a reorder of items in the Workspace.
+ */
+ public static class ItemConfiguration extends CellAndSpan {
+ public final ArrayMap<View, CellAndSpan> map = new ArrayMap<>();
private final ArrayMap<View, CellAndSpan> savedMap = new ArrayMap<>();
- final ArrayList<View> sortedViews = new ArrayList<>();
- ArrayList<View> intersectingViews;
- boolean isSolution = false;
+ public final ArrayList<View> sortedViews = new ArrayList<>();
+ public ArrayList<View> intersectingViews;
+ public boolean isSolution = false;
- void save() {
+ public void save() {
// Copy current state into savedMap
for (View v: map.keySet()) {
savedMap.get(v).copyFrom(map.get(v));
}
}
- void restore() {
+ public void restore() {
// Restore current state from savedMap
for (View v: savedMap.keySet()) {
map.get(v).copyFrom(savedMap.get(v));
}
}
- void add(View v, CellAndSpan cs) {
+ public void add(View v, CellAndSpan cs) {
map.put(v, cs);
savedMap.put(v, new CellAndSpan());
sortedViews.add(v);
}
- int area() {
+ public int area() {
return spanX * spanY;
}
- void getBoundingRectForViews(ArrayList<View> views, Rect outRect) {
+ public void getBoundingRectForViews(ArrayList<View> views, Rect outRect) {
boolean first = true;
for (View v: views) {
CellAndSpan c = map.get(v);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index de60d05..22b07ef 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1647,7 +1647,7 @@
@Override
protected void onNewIntent(Intent intent) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Launcher.onNewIntent: " + intent);
}
Object traceToken = TraceHelper.INSTANCE.beginSection(ON_NEW_INTENT_EVT);
@@ -3172,7 +3172,7 @@
// Setting the touch point to (-1, -1) will show the options popup in the center of
// the screen.
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Opening options popup on key up");
}
showDefaultOptions(-1, -1);
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 66ea616..76cae6a 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -41,6 +41,10 @@
* An animation using the view's background.
*/
public static final int VIEW_BACKGROUND = 1;
+ /**
+ * The default animation for a given view/item info type, but without the splash icon.
+ */
+ public static final int DEFAULT_NO_ICON = 2;
}
/**
diff --git a/src/com/android/launcher3/MultipageCellLayout.java b/src/com/android/launcher3/MultipageCellLayout.java
index 6a518a7..12cb35d 100644
--- a/src/com/android/launcher3/MultipageCellLayout.java
+++ b/src/com/android/launcher3/MultipageCellLayout.java
@@ -23,11 +23,11 @@
import android.view.View;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
+import com.android.launcher3.celllayout.MulticellReorderAlgorithm;
+import com.android.launcher3.celllayout.ReorderAlgorithm;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
-import java.util.function.Supplier;
-
/**
* CellLayout that simulates a split in the middle for use in foldable devices.
*/
@@ -36,8 +36,6 @@
private final Drawable mLeftBackground;
private final Drawable mRightBackground;
- private View mSeam;
-
private boolean mSeamWasAdded = false;
public MultipageCellLayout(Context context) {
@@ -62,7 +60,6 @@
mCountX = deviceProfile.inv.numColumns * 2;
mCountY = deviceProfile.inv.numRows;
- mSeam = new View(getContext());
setGridSize(mCountX, mCountY);
}
@@ -74,90 +71,18 @@
cellX++;
}
int finalCellX = cellX;
- return simulateSeam(
+ return ((MulticellReorderAlgorithm) createReorderAlgorithm()).simulateSeam(
() -> super.createAreaForResize(finalCellX, cellY, spanX, spanY, dragView,
direction, commit));
}
@Override
- ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, int minSpanX, int minSpanY,
- int spanX, int spanY) {
- return removeSeamFromSolution(simulateSeam(
- () -> super.closestEmptySpaceReorder(pixelX, pixelY, minSpanX, minSpanY, spanX,
- spanY)));
+ public ReorderAlgorithm createReorderAlgorithm() {
+ return new MulticellReorderAlgorithm(this);
}
@Override
- protected ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
- int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
- ItemConfiguration solution) {
- return removeSeamFromSolution(simulateSeam(
- () -> super.findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
- direction, dragView, decX, solution)));
- }
-
- @Override
- public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX, int spanY,
- View dragView) {
- return removeSeamFromSolution(simulateSeam(
- () -> super.dropInPlaceSolution(pixelX, pixelY, spanX, spanY, dragView)));
- }
-
- void addSeam() {
- CellLayoutLayoutParams lp = new CellLayoutLayoutParams(mCountX / 2, 0, 1, mCountY);
- mSeamWasAdded = true;
- lp.canReorder = false;
- mCountX++;
- mShortcutsAndWidgets.addViewInLayout(mSeam, lp);
- mOccupied = createGridOccupancyWithSeam(mOccupied);
- mTmpOccupied = new GridOccupancy(mCountX, mCountY);
- }
-
- void removeSeam() {
- mCountX--;
- mShortcutsAndWidgets.removeViewInLayout(mSeam);
- mTmpOccupied = new GridOccupancy(mCountX, mCountY);
- mSeamWasAdded = false;
- }
-
- protected <T> T simulateSeam(Supplier<T> f) {
- if (mSeamWasAdded) {
- return f.get();
- }
- GridOccupancy auxGrid = mOccupied;
- addSeam();
- T res = f.get();
- removeSeam();
- mOccupied = auxGrid;
- return res;
- }
-
- private ItemConfiguration removeSeamFromSolution(ItemConfiguration solution) {
- solution.map.forEach((view, cell) -> cell.cellX = cell.cellX > mCountX / 2
- ? cell.cellX - 1 : cell.cellX);
- solution.cellX = solution.cellX > mCountX / 2 ? solution.cellX - 1 : solution.cellX;
- return solution;
- }
-
-
-
- GridOccupancy createGridOccupancyWithSeam(GridOccupancy gridOccupancy) {
- GridOccupancy grid = new GridOccupancy(getCountX(), getCountY());
- for (int x = 0; x < getCountX(); x++) {
- for (int y = 0; y < getCountY(); y++) {
- int offset = x >= getCountX() / 2 ? 1 : 0;
- if (x == getCountX() / 2) {
- grid.cells[x][y] = true;
- } else {
- grid.cells[x][y] = gridOccupancy.cells[x - offset][y];
- }
- }
- }
- return grid;
- }
-
- @Override
- protected void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
+ public void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
@@ -196,4 +121,24 @@
mLeftBackground.setBounds(rect.left, rect.top, rect.right / 2 - 20, rect.bottom);
mRightBackground.setBounds(rect.right / 2 + 20, rect.top, rect.right, rect.bottom);
}
+
+ public void setCountX(int countX) {
+ mCountX = countX;
+ }
+
+ public void setCountY(int countY) {
+ mCountY = countY;
+ }
+
+ public void setOccupied(GridOccupancy occupied) {
+ mOccupied = occupied;
+ }
+
+ public boolean isSeamWasAdded() {
+ return mSeamWasAdded;
+ }
+
+ public void setSeamWasAdded(boolean seamWasAdded) {
+ mSeamWasAdded = seamWasAdded;
+ }
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index c3d8a53..e3bce87 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -773,7 +773,7 @@
if (mScroller.isFinished() && pageScrollChanged) {
// TODO(b/246283207): Remove logging once root cause of flake detected.
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS && !(this instanceof Workspace)) {
+ if (Utilities.isRunningInTestHarness() && !(this instanceof Workspace)) {
Log.d("b/246283207", this.getClass().getSimpleName() + "#onLayout() -> "
+ "if(mScroller.isFinished() && pageScrollChanged) -> getNextPage(): "
+ getNextPage() + ", getScrollForPage(getNextPage()): "
@@ -1370,8 +1370,14 @@
int velocity = (int) mOrientationHandler.getPrimaryVelocity(velocityTracker,
mActivePointerId);
float delta = primaryDirection - mDownMotionPrimary;
- int pageOrientedSize = (int) (mOrientationHandler.getMeasuredSize(
- getPageAt(mCurrentPage))
+
+ View current = getPageAt(mCurrentPage);
+ if (current == null) {
+ Log.e(TAG, "current page was null. this should not happen.");
+ return true;
+ }
+
+ int pageOrientedSize = (int) (mOrientationHandler.getMeasuredSize(current)
* mOrientationHandler.getPrimaryScale(this));
boolean isSignificantMove = isSignificantMove(Math.abs(delta), pageOrientedSize);
@@ -1713,7 +1719,7 @@
return false;
}
- if (FeatureFlags.IS_STUDIO_BUILD && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (FeatureFlags.IS_STUDIO_BUILD && !Utilities.isRunningInTestHarness()) {
duration *= Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, 1);
}
diff --git a/src/com/android/launcher3/Reorderable.java b/src/com/android/launcher3/Reorderable.java
index 047fb01..5afd95d 100644
--- a/src/com/android/launcher3/Reorderable.java
+++ b/src/com/android/launcher3/Reorderable.java
@@ -16,33 +16,19 @@
package com.android.launcher3;
-import android.graphics.PointF;
-import android.view.View;
+import com.android.launcher3.util.MultiTranslateDelegate;
public interface Reorderable {
/**
- * Set the offset related to reorder hint and bounce animations
+ * Returns the delegate to control translation
*/
- void setReorderBounceOffset(float x, float y);
-
- void getReorderBounceOffset(PointF offset);
-
- /**
- * Set the offset related to previewing the new reordered position
- */
- void setReorderPreviewOffset(float x, float y);
-
- void getReorderPreviewOffset(PointF offset);
+ MultiTranslateDelegate getTranslateDelegate();
/**
* Set the scale related to reorder hint and "bounce" animations
*/
void setReorderBounceScale(float scale);
- float getReorderBounceScale();
- /**
- * Get the com.android.view related to this object
- */
- View getView();
+ float getReorderBounceScale();
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 55b745b..b00199f 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.CellLayout.FOLDER;
import static com.android.launcher3.CellLayout.HOTSEAT;
import static com.android.launcher3.CellLayout.WORKSPACE;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_WIDGET_CENTERING;
import android.app.WallpaperManager;
import android.content.Context;
@@ -208,7 +209,8 @@
float scaleY = appWidgetScale.y;
nahv.setScaleToFit(Math.min(scaleX, scaleY));
- nahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
+ nahv.getTranslateDelegate().setTranslation(INDEX_WIDGET_CENTERING,
+ -(lp.width - (lp.width * scaleX)) / 2.0f,
-(lp.height - (lp.height * scaleY)) / 2.0f);
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7d01f7b..0fbaecb 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -65,6 +65,7 @@
import android.view.animation.Interpolator;
import androidx.annotation.ChecksSdkIntAtLeast;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;
@@ -136,6 +137,14 @@
@Deprecated
public static final boolean IS_DEBUG_DEVICE = BuildConfig.IS_DEBUG_DEVICE;
+ public static final int TRANSLATE_UP = 0;
+ public static final int TRANSLATE_DOWN = 1;
+ public static final int TRANSLATE_LEFT = 2;
+ public static final int TRANSLATE_RIGHT = 3;
+
+ @IntDef({TRANSLATE_UP, TRANSLATE_DOWN, TRANSLATE_LEFT, TRANSLATE_RIGHT})
+ public @interface AdjustmentDirection{}
+
/**
* Returns true if theme is dark.
*/
@@ -150,11 +159,14 @@
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
}
- public static boolean IS_RUNNING_IN_TEST_HARNESS =
- ActivityManager.isRunningInTestHarness();
+ private static boolean sIsRunningInTestHarness = ActivityManager.isRunningInTestHarness();
+
+ public static boolean isRunningInTestHarness() {
+ return sIsRunningInTestHarness;
+ }
public static void enableRunningInTestHarnessForTests() {
- IS_RUNNING_IN_TEST_HARNESS = true;
+ sIsRunningInTestHarness = true;
}
public static boolean isPropertyEnabled(String propertyName) {
@@ -728,4 +740,63 @@
matrixValues[Matrix.MTRANS_X], matrixValues[Matrix.MTRANS_Y]
));
}
+
+ /**
+ * Translates the {@code targetView} so that it overlaps with {@code exclusionBounds} as little
+ * as possible, while remaining within {@code inclusionBounds}.
+ * <p>
+ * {@code inclusionBounds} will always take precedence over {@code exclusionBounds}, so if
+ * {@code targetView} needs to be translated outside of {@code inclusionBounds} to fully fix an
+ * overlap with {@code exclusionBounds}, then {@code targetView} will only be translated up to
+ * the border of {@code inclusionBounds}.
+ * <p>
+ * Note: {@code targetViewBounds}, {@code inclusionBounds} and {@code exclusionBounds} must all
+ * be in relation to the same reference point on screen.
+ * <p>
+ * @param targetView the view being translated
+ * @param targetViewBounds the bounds of the {@code targetView}
+ * @param inclusionBounds the bounds the {@code targetView} absolutely must stay within
+ * @param exclusionBounds the bounds to try to move the {@code targetView} away from
+ * @param adjustmentDirection the translation direction that should be attempted to fix an
+ * overlap
+ */
+ public static void translateOverlappingView(
+ @NonNull View targetView,
+ @NonNull Rect targetViewBounds,
+ @NonNull Rect inclusionBounds,
+ @NonNull Rect exclusionBounds,
+ @AdjustmentDirection int adjustmentDirection) {
+ switch (adjustmentDirection) {
+ case TRANSLATE_RIGHT:
+ targetView.setTranslationX(Math.min(
+ // Translate to the right if the view is overlapping on the left.
+ Math.max(0, exclusionBounds.right - targetViewBounds.left),
+ // Do not translate beyond the inclusion bounds.
+ inclusionBounds.right - targetViewBounds.right));
+ break;
+ case TRANSLATE_LEFT:
+ targetView.setTranslationX(Math.max(
+ // Translate to the left if the view is overlapping on the right.
+ Math.min(0, exclusionBounds.left - targetViewBounds.right),
+ // Do not translate beyond the inclusion bounds.
+ inclusionBounds.left - targetViewBounds.left));
+ break;
+ case TRANSLATE_DOWN:
+ targetView.setTranslationY(Math.min(
+ // Translate downwards if the view is overlapping on the top.
+ Math.max(0, exclusionBounds.bottom - targetViewBounds.top),
+ // Do not translate beyond the inclusion bounds.
+ inclusionBounds.bottom - targetViewBounds.bottom));
+ break;
+ case TRANSLATE_UP:
+ targetView.setTranslationY(Math.max(
+ // Translate upwards if the view is overlapping on the bottom.
+ Math.min(0, exclusionBounds.top - targetViewBounds.bottom),
+ // Do not translate beyond the inclusion bounds.
+ inclusionBounds.top - targetViewBounds.top));
+ break;
+ default:
+ // No-Op
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 0188a47..df22425 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -127,7 +127,7 @@
|| onboardingPrefs.getBoolean(OnboardingPrefs.HOME_BOUNCE_SEEN)
|| AbstractFloatingView.getTopOpenView(launcher) != null
|| launcher.getSystemService(UserManager.class).isDemoUser()
- || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ || Utilities.isRunningInTestHarness()) {
return;
}
diff --git a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
new file mode 100644
index 0000000..cb12161
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2023 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.celllayout;
+
+import android.view.View;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.MultipageCellLayout;
+import com.android.launcher3.util.GridOccupancy;
+
+import java.util.function.Supplier;
+
+/**
+ * Variant of ReorderAlgorithm which simulates a foldable screen and adds a seam in the middle
+ * to prevent items to be placed in the middle.
+ */
+public class MulticellReorderAlgorithm extends ReorderAlgorithm {
+
+ private final View mSeam;
+
+ public MulticellReorderAlgorithm(CellLayout cellLayout) {
+ super(cellLayout);
+ mSeam = new View(cellLayout.getContext());
+ }
+
+ private CellLayout.ItemConfiguration removeSeamFromSolution(
+ CellLayout.ItemConfiguration solution) {
+ solution.map.forEach((view, cell) -> cell.cellX =
+ cell.cellX > mCellLayout.getCountX() / 2 ? cell.cellX - 1 : cell.cellX);
+ solution.cellX =
+ solution.cellX > mCellLayout.getCountX() / 2 ? solution.cellX - 1 : solution.cellX;
+ return solution;
+ }
+
+ @Override
+ public CellLayout.ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY,
+ int minSpanX, int minSpanY,
+ int spanX, int spanY) {
+ return removeSeamFromSolution(simulateSeam(
+ () -> super.closestEmptySpaceReorder(pixelX, pixelY, minSpanX, minSpanY, spanX,
+ spanY)));
+ }
+
+ @Override
+ public CellLayout.ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
+ int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
+ CellLayout.ItemConfiguration solution) {
+ return removeSeamFromSolution(simulateSeam(
+ () -> super.findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
+ direction, dragView, decX, solution)));
+ }
+
+ @Override
+ public CellLayout.ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
+ int spanY,
+ View dragView) {
+ return removeSeamFromSolution(simulateSeam(
+ () -> super.dropInPlaceSolution(pixelX, pixelY, spanX, spanY, dragView)));
+ }
+
+ void addSeam() {
+ MultipageCellLayout mcl = (MultipageCellLayout) mCellLayout;
+ mcl.setSeamWasAdded(true);
+ CellLayoutLayoutParams lp = new CellLayoutLayoutParams(mcl.getCountX() / 2, 0, 1,
+ mcl.getCountY());
+ lp.canReorder = false;
+ mcl.setCountX(mcl.getCountX() + 1);
+ mcl.getShortcutsAndWidgets().addViewInLayout(mSeam, lp);
+ mcl.setOccupied(createGridOccupancyWithSeam(mcl.getOccupied()));
+ mcl.mTmpOccupied = new GridOccupancy(mcl.getCountX(), mcl.getCountY());
+ }
+
+ void removeSeam() {
+ MultipageCellLayout mcl = (MultipageCellLayout) mCellLayout;
+ mcl.setCountX(mcl.getCountX() - 1);
+ mcl.getShortcutsAndWidgets().removeViewInLayout(mSeam);
+ mcl.mTmpOccupied = new GridOccupancy(mcl.getCountX(), mcl.getCountY());
+ mcl.setSeamWasAdded(false);
+ }
+
+ /**
+ * The function supplied here will execute while the CellLayout has a simulated seam added.
+ * @param f function to run under simulation
+ * @param <T> return value of the supplied function
+ * @return Value of supplied function
+ */
+ public <T> T simulateSeam(Supplier<T> f) {
+ MultipageCellLayout mcl = (MultipageCellLayout) mCellLayout;
+ if (mcl.isSeamWasAdded()) {
+ return f.get();
+ }
+ GridOccupancy auxGrid = mcl.getOccupied();
+ addSeam();
+ T res = f.get();
+ removeSeam();
+ mcl.setOccupied(auxGrid);
+ return res;
+ }
+
+ GridOccupancy createGridOccupancyWithSeam(GridOccupancy gridOccupancy) {
+ GridOccupancy grid = new GridOccupancy(mCellLayout.getCountX(), mCellLayout.getCountY());
+ for (int x = 0; x < mCellLayout.getCountX(); x++) {
+ for (int y = 0; y < mCellLayout.getCountY(); y++) {
+ int offset = x >= mCellLayout.getCountX() / 2 ? 1 : 0;
+ if (x == mCellLayout.getCountX() / 2) {
+ grid.cells[x][y] = true;
+ } else {
+ grid.cells[x][y] = gridOccupancy.cells[x - offset][y];
+ }
+ }
+ }
+ return grid;
+ }
+}
diff --git a/src/com/android/launcher3/celllayout/ReorderAlgorithm.java b/src/com/android/launcher3/celllayout/ReorderAlgorithm.java
new file mode 100644
index 0000000..5e5eefe
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/ReorderAlgorithm.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2023 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.celllayout;
+
+import android.view.View;
+
+import com.android.launcher3.CellLayout;
+
+/**
+ * Contains the logic of a reorder.
+ *
+ * The content of this class was extracted from {@link CellLayout} and should mimic the exact
+ * same behaviour.
+ */
+public class ReorderAlgorithm {
+
+ CellLayout mCellLayout;
+
+ public ReorderAlgorithm(CellLayout cellLayout) {
+ mCellLayout = cellLayout;
+ }
+
+ /**
+ * This method differs from closestEmptySpaceReorder and dropInPlaceSolution because this method
+ * will move items around and will change the shape of the item if possible to try to find a
+ * solution.
+ *
+ * When changing the size of the widget this method will try first subtracting -1 in the x
+ * dimension and then subtracting -1 in the y dimension until finding a possible solution or
+ * until it no longer can reduce the span.
+ *
+ * @param pixelX X coordinate in pixels in the screen
+ * @param pixelY Y coordinate in pixels in the screen
+ * @param minSpanX minimum possible horizontal span it will try to find a solution for.
+ * @param minSpanY minimum possible vertical span it will try to find a solution for.
+ * @param spanX horizontal cell span
+ * @param spanY vertical cell span
+ * @param direction direction in which it will try to push the items intersecting the desired
+ * view
+ * @param dragView view being dragged in reorder
+ * @param decX whether it will decrease the horizontal or vertical span if it can't find a
+ * solution for the current span.
+ * @param solution variable to store the solution
+ * @return the same solution variable
+ */
+ public CellLayout.ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
+ int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
+ CellLayout.ItemConfiguration solution) {
+ // Copy the current state into the solution. This solution will be manipulated as necessary.
+ mCellLayout.copyCurrentStateToSolution(solution, false);
+ // Copy the current occupied array into the temporary occupied array. This array will be
+ // manipulated as necessary to find a solution.
+ mCellLayout.getOccupied().copyTo(mCellLayout.mTmpOccupied);
+
+ // We find the nearest cell into which we would place the dragged item, assuming there's
+ // nothing in its way.
+ int[] result = new int[2];
+ result = mCellLayout.findNearestAreaIgnoreOccupied(pixelX, pixelY, spanX, spanY, result);
+
+ boolean success;
+ // First we try the exact nearest position of the item being dragged,
+ // we will then want to try to move this around to other neighbouring positions
+ success = mCellLayout.rearrangementExists(result[0], result[1], spanX, spanY, direction,
+ dragView, solution);
+
+ if (!success) {
+ // We try shrinking the widget down to size in an alternating pattern, shrink 1 in
+ // x, then 1 in y etc.
+ if (spanX > minSpanX && (minSpanY == spanY || decX)) {
+ return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX - 1, spanY,
+ direction, dragView, false, solution);
+ } else if (spanY > minSpanY) {
+ return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY - 1,
+ direction, dragView, true, solution);
+ }
+ solution.isSolution = false;
+ } else {
+ solution.isSolution = true;
+ solution.cellX = result[0];
+ solution.cellY = result[1];
+ solution.spanX = spanX;
+ solution.spanY = spanY;
+ }
+ return solution;
+ }
+
+ /**
+ * Returns a "reorder" if there is empty space without rearranging anything.
+ *
+ * @param pixelX X coordinate in pixels in the screen
+ * @param pixelY Y coordinate in pixels in the screen
+ * @param spanX horizontal cell span
+ * @param spanY vertical cell span
+ * @param dragView view being dragged in reorder
+ * @return the configuration that represents the found reorder
+ */
+ public CellLayout.ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
+ int spanY, View dragView) {
+ int[] result = new int[2];
+ if (mCellLayout.isNearestDropLocationOccupied(pixelX, pixelY, spanX, spanY, dragView,
+ result)) {
+ result[0] = result[1] = -1;
+ }
+ CellLayout.ItemConfiguration solution = new CellLayout.ItemConfiguration();
+ mCellLayout.copyCurrentStateToSolution(solution, false);
+ solution.isSolution = result[0] != -1;
+ if (!solution.isSolution) {
+ return solution;
+ }
+ solution.cellX = result[0];
+ solution.cellY = result[1];
+ solution.spanX = spanX;
+ solution.spanY = spanY;
+ return solution;
+ }
+
+ /**
+ * Returns a "reorder" where we simply drop the item in the closest empty space, without moving
+ * any other item in the way.
+ *
+ * @param pixelX X coordinate in pixels in the screen
+ * @param pixelY Y coordinate in pixels in the screen
+ * @param spanX horizontal cell span
+ * @param spanY vertical cell span
+ * @return the configuration that represents the found reorder
+ */
+ public CellLayout.ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY,
+ int minSpanX, int minSpanY, int spanX, int spanY) {
+ CellLayout.ItemConfiguration solution = new CellLayout.ItemConfiguration();
+ int[] result = new int[2];
+ int[] resultSpan = new int[2];
+ mCellLayout.findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, result,
+ resultSpan);
+ if (result[0] >= 0 && result[1] >= 0) {
+ mCellLayout.copyCurrentStateToSolution(solution, false);
+ solution.cellX = result[0];
+ solution.cellY = result[1];
+ solution.spanX = resultSpan[0];
+ solution.spanY = resultSpan[1];
+ solution.isSolution = true;
+ } else {
+ solution.isSolution = false;
+ }
+ return solution;
+ }
+
+ /**
+ * When the user drags an Item in the workspace sometimes we need to move the items already in
+ * the workspace to make space for the new item, this function return a solution for that
+ * reorder.
+ *
+ * @param pixelX X coordinate in the screen of the dragView in pixels
+ * @param pixelY Y coordinate in the screen of the dragView in pixels
+ * @param minSpanX minimum horizontal span the item can be shrunk to
+ * @param minSpanY minimum vertical span the item can be shrunk to
+ * @param spanX occupied horizontal span
+ * @param spanY occupied vertical span
+ * @param dragView the view of the item being draged
+ * @return returns a solution for the given parameters, the solution contains all the icons and
+ * the locations they should be in the given solution.
+ */
+ public CellLayout.ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX,
+ int minSpanY, int spanX, int spanY, View dragView) {
+ mCellLayout.getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView,
+ mCellLayout.mDirectionVector);
+
+ CellLayout.ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(pixelX, pixelY,
+ spanX, spanY,
+ dragView);
+
+ // Find a solution involving pushing / displacing any items in the way
+ CellLayout.ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX,
+ minSpanY, spanX, spanY, mCellLayout.mDirectionVector, dragView, true,
+ new CellLayout.ItemConfiguration());
+
+ // We attempt the approach which doesn't shuffle views at all
+ CellLayout.ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(
+ pixelX, pixelY, minSpanX, minSpanY, spanX, spanY);
+
+ // If the reorder solution requires resizing (shrinking) the item being dropped, we instead
+ // favor a solution in which the item is not resized, but
+ if (swapSolution.isSolution && swapSolution.area() >= closestSpaceSolution.area()) {
+ return swapSolution;
+ } else if (closestSpaceSolution.isSolution) {
+ return closestSpaceSolution;
+ } else if (dropInPlaceSolution.isSolution) {
+ return dropInPlaceSolution;
+ }
+ return null;
+ }
+}
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 770e931..24cc0ac 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -127,7 +127,7 @@
*/
private static AccessibilityManager getAccessibilityManagerForTest(Context context) {
// If not running in a test harness, don't participate in test exchanges.
- if (!Utilities.IS_RUNNING_IN_TEST_HARNESS) return null;
+ if (!Utilities.isRunningInTestHarness()) return null;
final AccessibilityManager accessibilityManager = getManager(context);
if (!accessibilityManager.isEnabled()) return null;
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index c98b60f..98b61d1 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -21,9 +21,14 @@
import android.content.Context;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.BuildConfig;
import com.android.launcher3.Utilities;
+import java.util.function.Predicate;
+import java.util.function.ToIntFunction;
+
/**
* Defines a set of flags used to control various launcher behaviors.
*
@@ -33,6 +38,11 @@
public static final String FLAGS_PREF_NAME = "featureFlags";
+ @VisibleForTesting
+ public static Predicate<BooleanFlag> sBooleanReader = f -> f.mCurrentValue;
+ @VisibleForTesting
+ public static ToIntFunction<IntFlag> sIntReader = f -> f.mCurrentValue;
+
private FeatureFlags() { }
public static boolean showFlagTogglerUi(Context context) {
@@ -385,7 +395,7 @@
}
public boolean get() {
- return mCurrentValue;
+ return sBooleanReader.test(this);
}
}
@@ -401,7 +411,7 @@
}
public int get() {
- return mCurrentValue;
+ return sIntReader.applyAsInt(this);
}
}
}
diff --git a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
index 08e50dd..0e76bbb 100644
--- a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
+++ b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
@@ -43,7 +43,7 @@
private long getEnterSpringLoadHoverTime() {
// Some TAPL tests are flaky on Cuttlefish with a low waiting time
- return Utilities.IS_RUNNING_IN_TEST_HARNESS
+ return Utilities.isRunningInTestHarness()
? ENTER_SPRING_LOAD_HOVER_TIME_IN_TEST
: ENTER_SPRING_LOAD_HOVER_TIME;
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index ee1a060..86f4beb 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -28,7 +28,6 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
@@ -77,6 +76,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.IconLabelDotView;
@@ -93,6 +93,7 @@
public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView,
DraggableView, Reorderable {
+ private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
@Thunk ActivityContext mActivity;
@Thunk Folder mFolder;
public FolderInfo mInfo;
@@ -133,14 +134,6 @@
private Rect mTouchArea = new Rect();
- private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
- private float mTranslationXForTaskbarAlignmentAnimation = 0f;
- private float mTranslationYForTaskbarAlignmentAnimation = 0f;
- private float mTranslationXForTaskbarRevealAnimation = 0f;
- private float mTranslationYForTaskbarRevealAnimation = 0f;
-
- private final PointF mTranslationForReorderBounce = new PointF(0, 0);
- private final PointF mTranslationForReorderPreview = new PointF(0, 0);
private float mScaleForReorderBounce = 1f;
private static final Property<FolderIcon, Float> DOT_SCALE_PROPERTY
@@ -770,120 +763,23 @@
mPreviewItemManager.onFolderClose(currentPage);
}
- private void updateTranslation() {
- super.setTranslationX(mTranslationForReorderBounce.x
- + mTranslationForReorderPreview.x
- + mTranslationForMoveFromCenterAnimation.x
- + mTranslationXForTaskbarAlignmentAnimation
- + mTranslationXForTaskbarRevealAnimation);
- super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
- + mTranslationForMoveFromCenterAnimation.y
- + mTranslationYForTaskbarAlignmentAnimation
- + mTranslationYForTaskbarRevealAnimation);
- }
-
- public void setReorderBounceOffset(float x, float y) {
- mTranslationForReorderBounce.set(x, y);
- updateTranslation();
- }
-
- public void getReorderBounceOffset(PointF offset) {
- offset.set(mTranslationForReorderBounce);
- }
-
- /**
- * Sets translationX value for taskbar to launcher alignment animation
- */
- public void setTranslationXForTaskbarAlignmentAnimation(float translationX) {
- mTranslationXForTaskbarAlignmentAnimation = translationX;
- updateTranslation();
- }
-
- /**
- * Returns translation values for taskbar to launcher alignment animation
- */
- public float getTranslationXForTaskbarAlignmentAnimation() {
- return mTranslationXForTaskbarAlignmentAnimation;
- }
-
- /**
- * Sets translationY value for taskbar to launcher alignment animation
- */
- public void setTranslationYForTaskbarAlignmentAnimation(float translationY) {
- mTranslationYForTaskbarAlignmentAnimation = translationY;
- updateTranslation();
- }
-
- /**
- * Returns translation values for taskbar to launcher alignment animation
- */
- public float getTranslationYForTaskbarAlignmentAnimation() {
- return mTranslationYForTaskbarAlignmentAnimation;
- }
-
- /**
- * Sets translationX value for taskbar reveal animation
- */
- public void setTranslationXForTaskbarRevealAnimation(float translationX) {
- mTranslationXForTaskbarRevealAnimation = translationX;
- updateTranslation();
- }
-
- /**
- * Returns translation values for taskbar reveal animation
- */
- public float getTranslationXForTaskbarRevealAnimation() {
- return mTranslationXForTaskbarRevealAnimation;
- }
-
- /**
- * Sets translationY value for taskbar reveal animation
- */
- public void setTranslationYForTaskbarRevealAnimation(float translationY) {
- mTranslationYForTaskbarRevealAnimation = translationY;
- updateTranslation();
- }
-
- /**
- * Returns translationY values for taskbar reveal animation
- */
- public float getTranslationYForTaskbarRevealAnimation() {
- return mTranslationYForTaskbarRevealAnimation;
- }
-
- /**
- * Sets translation values for move from center animation
- */
- public void setTranslationForMoveFromCenterAnimation(float x, float y) {
- mTranslationForMoveFromCenterAnimation.set(x, y);
- updateTranslation();
+ @Override
+ public MultiTranslateDelegate getTranslateDelegate() {
+ return mTranslateDelegate;
}
@Override
- public void setReorderPreviewOffset(float x, float y) {
- mTranslationForReorderPreview.set(x, y);
- updateTranslation();
- }
-
- @Override
- public void getReorderPreviewOffset(PointF offset) {
- offset.set(mTranslationForReorderPreview);
- }
-
public void setReorderBounceScale(float scale) {
mScaleForReorderBounce = scale;
super.setScaleX(scale);
super.setScaleY(scale);
}
+ @Override
public float getReorderBounceScale() {
return mScaleForReorderBounce;
}
- public View getView() {
- return this;
- }
-
@Override
public int getViewType() {
return DRAGGABLE_ICON;
diff --git a/src/com/android/launcher3/testing/TestInformationProvider.java b/src/com/android/launcher3/testing/TestInformationProvider.java
index 5444d92..17b472a 100644
--- a/src/com/android/launcher3/testing/TestInformationProvider.java
+++ b/src/com/android/launcher3/testing/TestInformationProvider.java
@@ -61,7 +61,7 @@
@Override
public Bundle call(String method, String arg, Bundle extras) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
TestInformationHandler handler = TestInformationHandler.newInstance(getContext());
handler.init(getContext());
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
index c151606..f95548d 100644
--- a/src/com/android/launcher3/testing/TestLogging.java
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -39,13 +39,13 @@
}
public static void recordEvent(String sequence, String event) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
recordEventSlow(sequence, event);
}
}
public static void recordEvent(String sequence, String message, Object parameter) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
recordEventSlow(sequence, message + ": " + parameter);
}
}
@@ -58,14 +58,14 @@
}
public static void recordKeyEvent(String sequence, String message, KeyEvent event) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (Utilities.isRunningInTestHarness()) {
recordEventSlow(sequence, message + ": " + event);
registerEventNotFromTest(event);
}
}
public static void recordMotionEvent(String sequence, String message, MotionEvent event) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS && event.getAction() != MotionEvent.ACTION_MOVE) {
+ if (Utilities.isRunningInTestHarness() && event.getAction() != MotionEvent.ACTION_MOVE) {
recordEventSlow(sequence, message + ": " + event);
registerEventNotFromTest(event);
}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 9ed6700..3d455d8 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -139,11 +139,11 @@
// TODO(b/258604917): When running in test harness, use !sTransientTaskbarStatusForTests
// once tests are updated to expect new persistent behavior such as not allowing long press
// to stash.
- if (!Utilities.IS_RUNNING_IN_TEST_HARNESS && FORCE_PERSISTENT_TASKBAR.get()) {
+ if (!Utilities.isRunningInTestHarness() && FORCE_PERSISTENT_TASKBAR.get()) {
return false;
}
return getInfo().navigationMode == NavigationMode.NO_BUTTON
- && (Utilities.IS_RUNNING_IN_TEST_HARNESS
+ && (Utilities.isRunningInTestHarness()
? sTransientTaskbarStatusForTests
: ENABLE_TRANSIENT_TASKBAR.get());
}
diff --git a/src/com/android/launcher3/util/MultiTranslateDelegate.java b/src/com/android/launcher3/util/MultiTranslateDelegate.java
new file mode 100644
index 0000000..0b5bc8d
--- /dev/null
+++ b/src/com/android/launcher3/util/MultiTranslateDelegate.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 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.util;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+
+import android.view.View;
+
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
+
+/**
+ * A utility class to split translation components for various workspace items
+ */
+public class MultiTranslateDelegate {
+
+ // offset related to reorder hint and bounce animations
+ public static final int INDEX_REORDER_BOUNCE_OFFSET = 0;
+ // offset related to previewing the new reordered position
+ public static final int INDEX_REORDER_PREVIEW_OFFSET = 1;
+ public static final int INDEX_MOVE_FROM_CENTER_ANIM = 2;
+
+ // Specific for icons and folders
+ public static final int INDEX_TASKBAR_ALIGNMENT_ANIM = 3;
+ public static final int INDEX_TASKBAR_REVEAL_ANIM = 4;
+
+ // Specific for widgets
+ public static final int INDEX_WIDGET_CENTERING = 3;
+
+ public static final int COUNT = 5;
+
+ private final MultiPropertyFactory<View> mTranslationX;
+ private final MultiPropertyFactory<View> mTranslationY;
+
+ public MultiTranslateDelegate(View target) {
+ this(target, COUNT, COUNT);
+ }
+
+ public MultiTranslateDelegate(View target, int countX, int countY) {
+ mTranslationX = new MultiPropertyFactory<>(target, VIEW_TRANSLATE_X, countX, Float::sum);
+ mTranslationY = new MultiPropertyFactory<>(target, VIEW_TRANSLATE_Y, countY, Float::sum);
+ }
+
+ /**
+ * Helper method to set both translations, x and y at a given index
+ */
+ public void setTranslation(int index, float x, float y) {
+ getTranslationX(index).setValue(x);
+ getTranslationY(index).setValue(y);
+ }
+
+ /**
+ * Returns the translation x for the provided index
+ */
+ public MultiProperty getTranslationX(int index) {
+ return mTranslationX.get(index);
+ }
+
+ /**
+ * Returns the translation y for the provided index
+ */
+ public MultiProperty getTranslationY(int index) {
+ return mTranslationY.get(index);
+ }
+}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index b6f6223..10f40b7 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -45,6 +45,7 @@
import android.view.WindowInsetsController;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
+import android.window.SplashScreen;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -320,7 +321,14 @@
return false;
}
- Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v, item).toBundle() : null;
+ Bundle optsBundle = null;
+ if (v != null) {
+ optsBundle = getActivityLaunchOptions(v, item).toBundle();
+ } else if (item != null && item.animationType == LauncherSettings.Animation.DEFAULT_NO_ICON
+ && Utilities.ATLEAST_T) {
+ optsBundle = ActivityOptions.makeBasic()
+ .setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR).toBundle();
+ }
UserHandle user = item == null ? null : item.user;
// Prepare intent
diff --git a/src/com/android/launcher3/views/IconButtonView.java b/src/com/android/launcher3/views/IconButtonView.java
index 9969eeb..71f6756 100644
--- a/src/com/android/launcher3/views/IconButtonView.java
+++ b/src/com/android/launcher3/views/IconButtonView.java
@@ -37,6 +37,7 @@
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.util.MultiTranslateDelegate;
/**
* Button in Taskbar that shows a tinted background and foreground.
@@ -45,6 +46,12 @@
private static final int[] ATTRS = {android.R.attr.icon};
+ private static final int INDEX_TASKBAR_ALL_APPS_ICON = MultiTranslateDelegate.COUNT;
+ private static final int MY_COUNT = MultiTranslateDelegate.COUNT + 1;
+
+ private final MultiTranslateDelegate mTranslateDelegate =
+ new MultiTranslateDelegate(this, MY_COUNT, MultiTranslateDelegate.COUNT);
+
public IconButtonView(Context context) {
this(context, null);
}
@@ -88,6 +95,18 @@
}
}
+ @Override
+ public MultiTranslateDelegate getTranslateDelegate() {
+ return mTranslateDelegate;
+ }
+
+ /**
+ * Sets translationX for taskbar all apps icon
+ */
+ public void setTranslationXForTaskbarAllAppsIcon(float translationX) {
+ getTranslateDelegate().getTranslationX(INDEX_TASKBAR_ALL_APPS_ICON).setValue(translationX);
+ }
+
private static class IconDrawable extends FastBitmapDrawable {
private final Drawable mFg;
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 2ac1e94..4f94c92 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -33,6 +33,7 @@
import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
+import androidx.annotation.Px;
import androidx.core.view.ViewCompat;
import com.android.launcher3.DeviceProfile;
@@ -61,7 +62,7 @@
implements OnClickListener, OnLongClickListener, DragSource,
PopupDataProvider.PopupDataChangeListener, Insettable {
/** The default number of cells that can fit horizontally in a widget sheet. */
- protected static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4;
+ public static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4;
protected static final String KEY_WIDGETS_EDUCATION_TIP_SEEN =
"launcher.widgets_education_tip_seen";
@@ -70,15 +71,18 @@
/* Touch handling related member variables. */
private Toast mWidgetInstructionToast;
- private int mContentHorizontalMarginInPx;
+ @Px protected int mContentHorizontalMargin;
+ @Px protected int mWidgetCellHorizontalPadding;
protected int mNavBarScrimHeight;
private final Paint mNavBarScrimPaint;
public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mContentHorizontalMarginInPx = getResources().getDimensionPixelSize(
+ mContentHorizontalMargin = getResources().getDimensionPixelSize(
R.dimen.widget_list_horizontal_margin);
+ mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize(
+ R.dimen.widget_cell_horizontal_padding);
mNavBarScrimPaint = new Paint();
mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
}
@@ -138,11 +142,11 @@
@Override
public void setInsets(Rect insets) {
mInsets.set(insets);
- int contentHorizontalMarginInPx = getResources().getDimensionPixelSize(
+ @Px int contentHorizontalMargin = getResources().getDimensionPixelSize(
R.dimen.widget_list_horizontal_margin);
- if (contentHorizontalMarginInPx != mContentHorizontalMarginInPx) {
- onContentHorizontalMarginChanged(contentHorizontalMarginInPx);
- mContentHorizontalMarginInPx = contentHorizontalMarginInPx;
+ if (contentHorizontalMargin != mContentHorizontalMargin) {
+ onContentHorizontalMarginChanged(contentHorizontalMargin);
+ mContentHorizontalMargin = contentHorizontalMargin;
}
}
@@ -198,19 +202,6 @@
MeasureSpec.getSize(heightMeasureSpec));
}
- /** Returns the number of cells that can fit horizontally in a given {@code content}. */
- protected int computeMaxHorizontalSpans(View content, int contentHorizontalPaddingPx) {
- DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
- int availableWidth = content.getMeasuredWidth()
- - contentHorizontalPaddingPx
- - (2 * mContentHorizontalMarginInPx);
- Point cellSize = deviceProfile.getCellSize();
- if (cellSize.x > 0) {
- return availableWidth / cellSize.x;
- }
- return DEFAULT_MAX_HORIZONTAL_SPANS;
- }
-
private boolean beginDraggingWidget(WidgetCell v) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_DROP_TARGET, "2");
@@ -341,7 +332,7 @@
/** Returns {@code true} if tip has previously been shown on any of {@link BaseWidgetSheet}. */
protected boolean hasSeenEducationTip() {
return mActivityContext.getSharedPrefs().getBoolean(KEY_WIDGETS_EDUCATION_TIP_SEEN, false)
- || Utilities.IS_RUNNING_IN_TEST_HARNESS;
+ || Utilities.isRunningInTestHarness();
}
@Override
diff --git a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
index 241c937..3389fb1 100644
--- a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
@@ -19,7 +19,6 @@
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.view.KeyEvent;
import android.view.View;
@@ -29,6 +28,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Reorderable;
import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.views.ActivityContext;
import java.util.ArrayList;
@@ -39,20 +39,13 @@
public abstract class NavigableAppWidgetHostView extends AppWidgetHostView
implements DraggableView, Reorderable {
+ private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
+
/**
* The scaleX and scaleY value such that the widget fits within its cellspans, scaleX = scaleY.
*/
private float mScaleToFit = 1f;
- /**
- * The translation values to center the widget within its cellspans.
- */
- private final PointF mTranslationForCentering = new PointF(0, 0);
-
- private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
-
- private final PointF mTranslationForReorderBounce = new PointF(0, 0);
- private final PointF mTranslationForReorderPreview = new PointF(0, 0);
private float mScaleForReorderBounce = 1f;
private final Rect mTempRect = new Rect();
@@ -163,57 +156,23 @@
setSelected(childIsFocused);
}
- public View getView() {
- return this;
- }
-
- private void updateTranslation() {
- super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
- + mTranslationForCentering.x + mTranslationForMoveFromCenterAnimation.x);
- super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
- + mTranslationForCentering.y + mTranslationForMoveFromCenterAnimation.y);
- }
-
- public void setTranslationForCentering(float x, float y) {
- mTranslationForCentering.set(x, y);
- updateTranslation();
- }
-
- public void setTranslationForMoveFromCenterAnimation(float x, float y) {
- mTranslationForMoveFromCenterAnimation.set(x, y);
- updateTranslation();
- }
-
- public void setReorderBounceOffset(float x, float y) {
- mTranslationForReorderBounce.set(x, y);
- updateTranslation();
- }
-
- public void getReorderBounceOffset(PointF offset) {
- offset.set(mTranslationForReorderBounce);
- }
-
- @Override
- public void setReorderPreviewOffset(float x, float y) {
- mTranslationForReorderPreview.set(x, y);
- updateTranslation();
- }
-
- @Override
- public void getReorderPreviewOffset(PointF offset) {
- offset.set(mTranslationForReorderPreview);
- }
-
private void updateScale() {
super.setScaleX(mScaleToFit * mScaleForReorderBounce);
super.setScaleY(mScaleToFit * mScaleForReorderBounce);
}
+ @Override
+ public MultiTranslateDelegate getTranslateDelegate() {
+ return mTranslateDelegate;
+ }
+
+ @Override
public void setReorderBounceScale(float scale) {
mScaleForReorderBounce = scale;
updateScale();
}
+ @Override
public float getReorderBounceScale() {
return mScaleForReorderBounce;
}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index ce47d70..80bc1a7 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
import static com.android.launcher3.Utilities.ATLEAST_S;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_WIDGET_CENTERING;
import android.content.Context;
import android.graphics.Bitmap;
@@ -314,7 +315,7 @@
setScaleToFit(1.0f);
}
// When the drag start, translations need to be set to zero to center the view
- setTranslationForCentering(0f, 0f);
+ getTranslateDelegate().setTranslation(INDEX_WIDGET_CENTERING, 0f, 0f);
}
}
@@ -464,7 +465,8 @@
} else {
mAppWidgetHostViewPreview.setScaleToFit(mAppWidgetHostViewScale);
}
- mAppWidgetHostViewPreview.setTranslationForCentering(
+ mAppWidgetHostViewPreview.getTranslateDelegate().setTranslation(
+ INDEX_WIDGET_CENTERING,
-(params.width - (params.width * mPreviewContainerScale)) / 2.0f,
-(params.height - (params.height * mPreviewContainerScale)) / 2.0f);
mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 4099302..06c622d 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -36,6 +36,8 @@
import android.widget.TableRow;
import android.widget.TextView;
+import androidx.annotation.Px;
+
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.WidgetItem;
@@ -69,8 +71,7 @@
private static final long EDUCATION_TIP_DELAY_MS = 300;
private ItemInfo mOriginalItemInfo;
- private int mMaxHorizontalSpan = DEFAULT_MAX_HORIZONTAL_SPANS;
- private final int mWidgetCellHorizontalPadding;
+ @Px private int mMaxHorizontalSpan;
private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
new OnLayoutChangeListener() {
@@ -111,8 +112,6 @@
if (!hasSeenEducationTip()) {
addOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
}
- mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize(
- R.dimen.widget_cell_horizontal_padding);
setContentBackground(getContext().getDrawable(R.drawable.bg_rounded_corner_bottom_sheet));
}
@@ -134,7 +133,7 @@
private boolean updateMaxSpansPerRow() {
if (getMeasuredWidth() == 0) return false;
- int maxHorizontalSpan = computeMaxHorizontalSpans(mContent, mWidgetCellHorizontalPadding);
+ @Px int maxHorizontalSpan = mContent.getMeasuredWidth() - (2 * mContentHorizontalMargin);
if (mMaxHorizontalSpan != maxHorizontalSpan) {
// Ensure the table layout is showing widgets in the right column after measure.
mMaxHorizontalSpan = maxHorizontalSpan;
@@ -184,7 +183,9 @@
TableLayout widgetsTable = findViewById(R.id.widgets_table);
widgetsTable.removeAllViews();
- WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(widgets, mMaxHorizontalSpan)
+ WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgets, mActivityContext,
+ mActivityContext.getDeviceProfile(), mMaxHorizontalSpan,
+ mWidgetCellHorizontalPadding)
.forEach(row -> {
TableRow tableRow = new TableRow(getContext());
tableRow.setGravity(Gravity.TOP);
diff --git a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
index 626e0b9..d709196 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.widget.model;
+import androidx.annotation.Px;
+
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
@@ -26,7 +28,7 @@
*/
public final class WidgetsListContentEntry extends WidgetsListBaseEntry {
- private final int mMaxSpanSizeInCells;
+ @Px private final int mMaxSpanSize;
/**
* Constructor for {@link WidgetsListContentEntry}.
@@ -37,7 +39,7 @@
*/
public WidgetsListContentEntry(PackageItemInfo pkgItem, String titleSectionName,
List<WidgetItem> items) {
- this(pkgItem, titleSectionName, items, /* maxSpanSizeInCells= */ 0);
+ this(pkgItem, titleSectionName, items, /* maxSpanSize= */ 0);
}
/**
@@ -46,43 +48,43 @@
* @param pkgItem package info associated with the entry
* @param titleSectionName title section name associated with the entry.
* @param items list of widgets for the package.
- * @param maxSpanSizeInCells the max horizontal span in cells that is allowed for grouping more
+ * @param maxSpanSize the max horizontal span in pixels that is allowed for grouping more
* than one widgets in a table row.
*/
public WidgetsListContentEntry(PackageItemInfo pkgItem, String titleSectionName,
- List<WidgetItem> items, int maxSpanSizeInCells) {
+ List<WidgetItem> items, @Px int maxSpanSize) {
super(pkgItem, titleSectionName, items);
- mMaxSpanSizeInCells = maxSpanSizeInCells;
+ mMaxSpanSize = maxSpanSize;
}
@Override
public String toString() {
- return "Content:" + mPkgItem.packageName + ":" + mWidgets.size() + " maxSpanSizeInCells: "
- + mMaxSpanSizeInCells;
+ return "Content:" + mPkgItem.packageName + ":" + mWidgets.size() + " maxSpanSize: "
+ + mMaxSpanSize;
}
/**
- * Returns a copy of this {@link WidgetsListContentEntry} with updated
- * {@param maxSpanSizeInCells}.
+ * Returns a copy of this {@link WidgetsListContentEntry} with updated {@code maxSpanSize}.
*
- * @param maxSpanSizeInCells the maximum horizontal span in cells that is allowed for grouping
+ * @param maxSpanSize the maximum horizontal span in pixels that is allowed for grouping
* more than one widgets in a table row.
*/
- public WidgetsListContentEntry withMaxSpanSize(int maxSpanSizeInCells) {
- if (mMaxSpanSizeInCells == maxSpanSizeInCells) return this;
+ public WidgetsListContentEntry withMaxSpanSize(@Px int maxSpanSize) {
+ if (mMaxSpanSize == maxSpanSize) return this;
return new WidgetsListContentEntry(
mPkgItem,
mTitleSectionName,
mWidgets,
- /* maxSpanSizeInCells= */ maxSpanSizeInCells);
+ /* maxSpanSize= */ maxSpanSize);
}
/**
- * Returns the max horizontal span size in cells that is allowed for grouping more than one
+ * Returns the max horizontal span size in pixels that is allowed for grouping more than one
* widget in a table row.
*/
- public int getMaxSpanSizeInCells() {
- return mMaxSpanSizeInCells;
+ @Px
+ public int getMaxSpanSize() {
+ return mMaxSpanSize;
}
@Override
@@ -91,6 +93,6 @@
WidgetsListContentEntry otherEntry = (WidgetsListContentEntry) obj;
return mWidgets.equals(otherEntry.mWidgets) && mPkgItem.equals(otherEntry.mPkgItem)
&& mTitleSectionName.equals(otherEntry.mTitleSectionName)
- && mMaxSpanSizeInCells == otherEntry.mMaxSpanSizeInCells;
+ && mMaxSpanSize == otherEntry.mMaxSpanSize;
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 77781bd..d5c4315 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -53,6 +53,7 @@
import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.Px;
import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
@@ -181,14 +182,13 @@
}
};
- private final int mTabsHeight;
- private final int mWidgetSheetContentHorizontalPadding;
+ @Px private final int mTabsHeight;
@Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
@Nullable private PersonalWorkPagedView mViewPager;
private boolean mIsInSearchMode;
private boolean mIsNoWidgetsViewNeeded;
- private int mMaxSpansPerRow = DEFAULT_MAX_HORIZONTAL_SPANS;
+ @Px private int mMaxSpanPerRow;
private TextView mNoWidgetsView;
private StickyHeaderLayout mSearchScrollView;
@@ -224,8 +224,6 @@
mTabsHeight = mHasWorkProfile
? resources.getDimensionPixelSize(R.dimen.all_apps_header_pill_height)
: 0;
- mWidgetSheetContentHorizontalPadding = 2 * resources.getDimensionPixelSize(
- R.dimen.widget_cell_horizontal_padding);
mUserManagerState.init(UserCache.INSTANCE.get(context),
context.getSystemService(UserManager.class));
@@ -337,7 +335,7 @@
: mSearchScrollView.findViewById(R.id.title);
mRightPane = mIsTwoPane ? mContent.findViewById(R.id.right_pane) : null;
mWidgetsListTableViewHolderBinder =
- new WidgetsListTableViewHolderBinder(layoutInflater, this, this);
+ new WidgetsListTableViewHolderBinder(mActivityContext, layoutInflater, this, this);
onRecommendedWidgetsBound();
onWidgetsBound();
@@ -536,22 +534,20 @@
View content = mHasWorkProfile
? mViewPager
: mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView;
-
if (mIsTwoPane && mRightPane != null) {
content = mRightPane;
}
- int maxHorizontalSpans = computeMaxHorizontalSpans(content,
- mWidgetSheetContentHorizontalPadding);
- if (mMaxSpansPerRow != maxHorizontalSpans) {
- mMaxSpansPerRow = maxHorizontalSpans;
- mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
- mMaxSpansPerRow);
- mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
- mMaxSpansPerRow);
+ @Px int maxHorizontalSpan = content.getMeasuredWidth() - (2 * mContentHorizontalMargin);
+ if (mMaxSpanPerRow != maxHorizontalSpan) {
+ mMaxSpanPerRow = maxHorizontalSpan;
+ mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPxPerRow(
+ maxHorizontalSpan);
+ mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setMaxHorizontalSpansPxPerRow(
+ maxHorizontalSpan);
if (mHasWorkProfile) {
- mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
- mMaxSpansPerRow);
+ mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPxPerRow(
+ maxHorizontalSpan);
}
onRecommendedWidgetsBound();
return true;
@@ -700,8 +696,12 @@
- noWidgetsViewHeight) * RECOMMENDATION_TABLE_HEIGHT_RATIO;
List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
- WidgetsTableUtils.groupWidgetItemsIntoTableWithoutReordering(
- recommendedWidgets, mMaxSpansPerRow);
+ WidgetsTableUtils.groupWidgetItemsUsingRowPxWithoutReordering(
+ recommendedWidgets,
+ mActivityContext,
+ mActivityContext.getDeviceProfile(),
+ mMaxSpanPerRow,
+ mWidgetCellHorizontalPadding);
mRecommendedWidgetsTable.setRecommendedWidgets(
recommendedWidgetsInTable, maxTableHeight);
} else {
@@ -939,7 +939,7 @@
protected boolean hasSeenEducationDialog() {
return mActivityContext.getSharedPrefs()
.getBoolean(KEY_WIDGETS_EDUCATION_DIALOG_SEEN, false)
- || Utilities.IS_RUNNING_IN_TEST_HARNESS;
+ || Utilities.isRunningInTestHarness();
}
private void setUpEducationViewsIfNeeded() {
@@ -1051,7 +1051,7 @@
if (mAdapterType == PRIMARY || mAdapterType == WORK) {
mWidgetsRecyclerView.addOnAttachStateChangeListener(mBindScrollbarInSearchMode);
}
- mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
+ mWidgetsListAdapter.setMaxHorizontalSpansPxPerRow(mMaxSpanPerRow);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index c28402e..c89eea8 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_DEFAULT;
import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_FIRST;
import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_LAST;
+import static com.android.launcher3.widget.BaseWidgetSheet.DEFAULT_MAX_HORIZONTAL_SPANS;
import android.content.Context;
import android.os.Process;
@@ -32,6 +33,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.Px;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.DiffUtil.DiffResult;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -49,6 +51,7 @@
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+import com.android.launcher3.widget.util.WidgetSizes;
import java.util.ArrayList;
import java.util.Arrays;
@@ -99,7 +102,7 @@
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
@Nullable private RecyclerView mRecyclerView;
@Nullable private PackageUserKey mPendingClickHeader;
- private int mMaxSpanSize = 4;
+ @Px private int mMaxHorizontalSpan;
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
IntSupplier emptySpaceHeightProvider, OnClickListener iconClickListener,
@@ -107,11 +110,14 @@
WidgetsFullSheet.HeaderChangeListener headerChangeListener) {
mHeaderChangeListener = headerChangeListener;
mContext = context;
+ mMaxHorizontalSpan = WidgetSizes.getWidgetSizePx(
+ ActivityContext.lookupContext(context).getDeviceProfile(),
+ DEFAULT_MAX_HORIZONTAL_SPANS, 1).getWidth();
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_LIST,
new WidgetsListTableViewHolderBinder(
- layoutInflater, iconClickListener, iconLongClickListener));
+ mContext, layoutInflater, iconClickListener, iconLongClickListener));
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_HEADER,
new WidgetsListHeaderViewHolderBinder(
@@ -199,7 +205,8 @@
} else if (entry instanceof WidgetsListContentEntry) {
// Adjust the original content entries to accommodate for the current
// maxSpanSize.
- return ((WidgetsListContentEntry) entry).withMaxSpanSize(mMaxSpanSize);
+ return ((WidgetsListContentEntry) entry).withMaxSpanSize(
+ mMaxHorizontalSpan);
}
return entry;
})
@@ -407,11 +414,11 @@
}
/**
- * Sets the max horizontal span in cells that is allowed for grouping more than one widget in a
+ * Sets the max horizontal span in pixels that is allowed for grouping more than one widget in a
* table row.
*/
- public void setMaxHorizontalSpansPerRow(int maxHorizontalSpans) {
- mMaxSpanSize = maxHorizontalSpans;
+ public void setMaxHorizontalSpansPxPerRow(@Px int maxHorizontalSpan) {
+ mMaxHorizontalSpan = maxHorizontalSpan;
updateVisibleEntries();
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 2e8f0ab..c7d2aa3 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.widget.picker;
+import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import android.util.Pair;
@@ -27,9 +28,13 @@
import android.widget.TableLayout;
import android.widget.TableRow;
+import androidx.annotation.NonNull;
+import androidx.annotation.Px;
+
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.util.WidgetsTableUtils;
@@ -47,13 +52,21 @@
private final LayoutInflater mLayoutInflater;
private final OnClickListener mIconClickListener;
+ private @NonNull final Context mContext;
+ private @NonNull final ActivityContext mActivityContext;
+ @Px private final int mCellPadding;
private final OnLongClickListener mIconLongClickListener;
public WidgetsListTableViewHolderBinder(
+ @NonNull Context context,
LayoutInflater layoutInflater,
OnClickListener iconClickListener,
OnLongClickListener iconLongClickListener) {
mLayoutInflater = layoutInflater;
+ mContext = context;
+ mActivityContext = ActivityContext.lookupContext(context);
+ mCellPadding = context.getResources().getDimensionPixelSize(
+ R.dimen.widget_cell_horizontal_padding);
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
}
@@ -87,8 +100,11 @@
(position & POSITION_LAST) != 0));
List<ArrayList<WidgetItem>> widgetItemsTable =
- WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
- entry.mWidgets, entry.getMaxSpanSizeInCells());
+ WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(entry.mWidgets,
+ mContext,
+ mActivityContext.getDeviceProfile(),
+ entry.getMaxSpanSize(),
+ mCellPadding);
recycleTableBeforeBinding(table, widgetItemsTable);
// Bind the widget items.
diff --git a/src/com/android/launcher3/widget/util/WidgetsTableUtils.java b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
index 72e27bf..74d3062 100644
--- a/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
+++ b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
@@ -15,6 +15,11 @@
*/
package com.android.launcher3.widget.util;
+import android.content.Context;
+
+import androidx.annotation.Px;
+
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.model.WidgetItem;
import java.util.ArrayList;
@@ -49,34 +54,41 @@
* Groups {@code widgetItems} items into a 2D array which matches their appearance in a UI
* table. This takes liberty to rearrange widgets to make the table visually appealing.
*/
- public static List<ArrayList<WidgetItem>> groupWidgetItemsIntoTableWithReordering(
- List<WidgetItem> widgetItems, final int maxSpansPerRow) {
+ public static List<ArrayList<WidgetItem>> groupWidgetItemsUsingRowPxWithReordering(
+ List<WidgetItem> widgetItems, Context context, final DeviceProfile dp,
+ final @Px int rowPx, final @Px int cellPadding) {
List<WidgetItem> sortedWidgetItems = widgetItems.stream().sorted(WIDGET_SHORTCUT_COMPARATOR)
.collect(Collectors.toList());
- return groupWidgetItemsIntoTableWithoutReordering(sortedWidgetItems, maxSpansPerRow);
+ return groupWidgetItemsUsingRowPxWithoutReordering(sortedWidgetItems, context, dp, rowPx,
+ cellPadding);
}
/**
* Groups {@code widgetItems} into a 2D array which matches their appearance in a UI table while
- * maintaining their order.
+ * maintaining their order. This function is a variant of
+ * {@code groupWidgetItemsIntoTableWithoutReordering} in that this uses widget pixels for
+ * calculation.
*
* <p>Grouping:
* 1. Widgets and shortcuts never group together in the same row.
- * 2. The ordered widgets are grouped together in the same row until their total horizontal
- * spans exceed the {@code maxSpansPerRow} - 1.
- * 3. The order shortcuts are grouped together in the same row until their total horizontal
- * spans exceed the {@code maxSpansPerRow} - 1.
- * 4. If there is only one widget in a row, its width may exceed the {@code maxSpansPerRow}.
+ * 2. The ordered widgets are grouped together in the same row until their individual occupying
+ * pixels exceed the total allowed pixels for the cell.
+ * 3. The ordered shortcuts are grouped together in the same row until their individual
+ * occupying pixels exceed the total allowed pixels for the cell.
+ * 4. If there is only one widget in a row, its width may exceed the {@code rowPx}.
*
- * <p>Let's say the {@code maxSpansPerRow} is set to 6. Widgets can be grouped in the same row
- * if their total horizontal spans added don't exceed 5.
- * Example 1: Row 1: 2x2, 2x3, 1x1. Total horizontal spans is 5. This is okay.
- * Example 2: Row 1: 2x2, 4x3, 1x1. the total horizontal spans is 7. This is wrong. 4x3 and 1x1
- * should be moved to a new row.
- * Example 3: Row 1: 6x4. This is okay because this is the only item in the row.
+ * <p>Let's say the {@code rowPx} is set to 600 and we have 5 widgets. Widgets can be grouped
+ * in the same row if each of their individual occupying pixels does not exceed
+ * {@code rowPx} / 5 - 2 * {@code cellPadding}.
+ * Example 1: Row 1: 200x200, 200x300, 100x100. Average horizontal pixels is 200 and no widgets
+ * exceed that width. This is okay.
+ * Example 2: Row 1: 200x200, 400x300, 100x100. Average horizontal pixels is 200 and one widget
+ * exceed that width. This is not allowed.
+ * Example 3: Row 1: 700x400. This is okay because this is the only item in the row.
*/
- public static List<ArrayList<WidgetItem>> groupWidgetItemsIntoTableWithoutReordering(
- List<WidgetItem> widgetItems, final int maxSpansPerRow) {
+ public static List<ArrayList<WidgetItem>> groupWidgetItemsUsingRowPxWithoutReordering(
+ List<WidgetItem> widgetItems, Context context, final DeviceProfile dp,
+ final @Px int rowPx, final @Px int cellPadding) {
List<ArrayList<WidgetItem>> widgetItemsTable = new ArrayList<>();
ArrayList<WidgetItem> widgetItemsAtRow = null;
@@ -86,23 +98,28 @@
widgetItemsTable.add(widgetItemsAtRow);
}
int numOfWidgetItems = widgetItemsAtRow.size();
- int totalHorizontalSpan = widgetItemsAtRow.stream().map(item -> item.spanX)
- .reduce(/* default= */ 0, Integer::sum);
- int totalHorizontalSpanAfterAddingWidget = widgetItem.spanX + totalHorizontalSpan;
+ @Px int individualSpan = (rowPx / (numOfWidgetItems + 1)) - (2 * cellPadding);
if (numOfWidgetItems == 0) {
widgetItemsAtRow.add(widgetItem);
} else if (
- // The max spans per row is reduced by 1 to ensure we don't pack too many
- // 1xn widgets on the same row, which may reduce the space for rendering a
- // widget's description.
- totalHorizontalSpanAfterAddingWidget <= maxSpansPerRow - 1
- && widgetItem.hasSameType(widgetItemsAtRow.get(numOfWidgetItems - 1))) {
+ // Since the size of the widget cell is determined by dividing the maximum span
+ // pixels evenly, making sure that each widget would have enough span pixels to
+ // show their contents.
+ widgetItem.hasSameType(widgetItemsAtRow.get(numOfWidgetItems - 1))
+ && widgetItemsAtRow.stream().allMatch(
+ item -> WidgetSizes.getWidgetItemSizePx(context, dp, item)
+ .getWidth() <= individualSpan)
+ && WidgetSizes.getWidgetItemSizePx(context, dp, widgetItem)
+ .getWidth() <= individualSpan) {
// Group items in the same row if
// 1. they are with the same type, i.e. a row can only have widgets or shortcuts but
// never a mix of both.
- // 2. the total number of horizontal spans are smaller than or equal to
- // MAX_SPAN_PER_ROW. If an item has a horizontal span > MAX_SPAN_PER_ROW, we just
- // place it in its own row regardless of the horizontal span limit.
+ // 2. Each widget will have horizontal cell span pixels that is at least as large as
+ // it is required to fit in the horizontal content, unless the widget horizontal
+ // span pixels is larger than the maximum allowed.
+ // If an item has horizontal span pixels larger than the maximum allowed pixels
+ // per row, we just place it in its own row regardless of the horizontal span
+ // limit.
widgetItemsAtRow.add(widgetItem);
} else {
widgetItemsAtRow = new ArrayList<>();
diff --git a/tests/Android.bp b/tests/Android.bp
index dfbaf86..81853d1 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -116,7 +116,8 @@
manifest: "AndroidManifest.xml",
platform_apis: true,
test_config: "Launcher3Tests.xml",
- data: [":Launcher3"]
+ data: [":Launcher3"],
+ test_suites: ["general-tests"],
}
// Shared between tests and launcher
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 19b8b0c..5f516eb 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -29,7 +29,9 @@
import android.content.Intent;
import android.graphics.Point;
+import android.os.SystemClock;
import android.platform.test.annotations.IwTest;
+import android.util.Log;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -525,9 +527,11 @@
@Test
@PortraitLandscape
public void testDragAppIconToWorkspaceCell() throws Exception {
+ long startTime, endTime, elapsedTime;
Point[] targets = getCornersAndCenterPositions();
for (Point target : targets) {
+ startTime = SystemClock.uptimeMillis();
final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
allApps.freeze();
try {
@@ -537,12 +541,21 @@
}
// Reset the workspace for the next shortcut creation.
initialize(this);
+ endTime = SystemClock.uptimeMillis();
+ elapsedTime = endTime - startTime;
+ Log.d("testDragAppIconToWorkspaceCellTime",
+ "Milliseconds taken to drag app icon to workspace cell: " + elapsedTime);
}
// test to move a shortcut to other cell.
final HomeAppIcon launcherTestAppIcon = createShortcutInCenterIfNotExist(APP_NAME);
for (Point target : targets) {
+ startTime = SystemClock.uptimeMillis();
launcherTestAppIcon.dragToWorkspace(target.x, target.y);
+ endTime = SystemClock.uptimeMillis();
+ elapsedTime = endTime - startTime;
+ Log.d("testDragAppIconToWorkspaceCellTime",
+ "Milliseconds taken to move shortcut to other cell: " + elapsedTime);
}
}
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index d7c6c4f..433fd31 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -27,12 +27,18 @@
import androidx.test.uiautomator.UiDevice;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.FeatureFlags.BooleanFlag;
+import com.android.launcher3.config.FeatureFlags.IntFlag;
+
import org.junit.Assert;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
+import java.util.function.Predicate;
+import java.util.function.ToIntFunction;
public class TestUtil {
public static final String DUMMY_PACKAGE = "com.example.android.aardwolf";
@@ -68,6 +74,36 @@
}
}
+ /**
+ * Utility class to override a boolean flag during test. Note that the returned SafeCloseable
+ * must be closed to restore the original state
+ */
+ public static SafeCloseable overrideFlag(BooleanFlag flag, boolean value) {
+ Predicate<BooleanFlag> originalProxy = FeatureFlags.sBooleanReader;
+ Predicate<BooleanFlag> testProxy = f -> f == flag ? value : originalProxy.test(f);
+ FeatureFlags.sBooleanReader = testProxy;
+ return () -> {
+ if (FeatureFlags.sBooleanReader == testProxy) {
+ FeatureFlags.sBooleanReader = originalProxy;
+ }
+ };
+ }
+
+ /**
+ * Utility class to override a int flag during test. Note that the returned SafeCloseable
+ * must be closed to restore the original state
+ */
+ public static SafeCloseable overrideFlag(IntFlag flag, int value) {
+ ToIntFunction<IntFlag> originalProxy = FeatureFlags.sIntReader;
+ ToIntFunction<IntFlag> testProxy = f -> f == flag ? value : originalProxy.applyAsInt(f);
+ FeatureFlags.sIntReader = testProxy;
+ return () -> {
+ if (FeatureFlags.sIntReader == testProxy) {
+ FeatureFlags.sIntReader = originalProxy;
+ }
+ };
+ }
+
public static void uninstallDummyApp() throws IOException {
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
"pm uninstall " + DUMMY_PACKAGE);
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index 9dc46f1..e0101f5 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -94,6 +94,7 @@
}).when(mIconCache).getTitleNoCache(any());
mViewHolderBinder = new WidgetsListTableViewHolderBinder(
+ mContext,
LayoutInflater.from(mContext),
mOnIconClickListener,
mOnLongClickListener);
diff --git a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
index 715dcca..d2c2fd7 100644
--- a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
@@ -23,6 +23,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -35,11 +36,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
+import com.android.launcher3.util.ActivityContextWrapper;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.util.WidgetsTableUtils;
@@ -57,11 +60,19 @@
public final class WidgetsTableUtilsTest {
private static final String TEST_PACKAGE = "com.google.test";
+ private static final int SPACE_SIZE = 10;
+ private static final int CELL_SIZE = 50;
+ private static final int NUM_OF_COLS = 5;
+ private static final int NUM_OF_ROWS = 5;
+
@Mock
private IconCache mIconCache;
+ @Mock
+ private DeviceProfile mTestDeviceProfile;
+
private Context mContext;
- private InvariantDeviceProfile mTestProfile;
+ private InvariantDeviceProfile mTestInvariantProfile;
private WidgetItem mWidget1x1;
private WidgetItem mWidget2x2;
private WidgetItem mWidget2x3;
@@ -76,12 +87,13 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = getApplicationContext();
+ mContext = new ActivityContextWrapper(getApplicationContext());
- mTestProfile = new InvariantDeviceProfile();
- mTestProfile.numRows = 5;
- mTestProfile.numColumns = 5;
+ mTestInvariantProfile = new InvariantDeviceProfile();
+ mTestInvariantProfile.numColumns = NUM_OF_COLS;
+ mTestInvariantProfile.numRows = NUM_OF_ROWS;
+ initDP();
initTestWidgets();
initTestShortcuts();
@@ -92,17 +104,17 @@
@Test
- public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpansPerRow5_shouldGroupWidgetsInTable() {
+ public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpanPxPerRow220_cellPadding0_shouldGroupWidgetsInTable() {
List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
mWidget2x2);
List<ArrayList<WidgetItem>> widgetItemInTable =
- WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
- widgetItems, /* maxSpansPerRow= */ 5);
+ WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
+ mTestDeviceProfile, 220, 0);
- // Row 0: 1x1, 2x2
- // Row 1: 2x3, 2x4
- // Row 2: 4x4
+ // Row 0: 1x1(50px), 2x2(110px)
+ // Row 1: 2x3(110px), 2x4(110px)
+ // Row 2: 4x4(230px)
assertThat(widgetItemInTable).hasSize(3);
assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3, mWidget2x4);
@@ -110,65 +122,91 @@
}
@Test
- public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpansPerRow4_shouldGroupWidgetsInTable() {
+ public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpanPxPerRow220_cellPadding10_shouldGroupWidgetsInTable() {
List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
mWidget2x2);
List<ArrayList<WidgetItem>> widgetItemInTable =
- WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
- widgetItems, /* maxSpansPerRow= */ 4);
+ WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
+ mTestDeviceProfile, 220, 10);
- // Row 0: 1x1, 2x2
- // Row 1: 2x3,
- // Row 2: 2x4,
- // Row 3: 4x4
- assertThat(widgetItemInTable).hasSize(4);
- assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
- assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3);
- assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x4);
- assertThat(widgetItemInTable.get(3)).containsExactly(mWidget4x4);
+ // Row 0: 1x1(50px), 2x2(110px)
+ // Row 1: 2x3(110px), 2x4(110px)
+ // Row 2: 4x4(230px)
+ assertThat(widgetItemInTable).hasSize(5);
+ assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1);
+ assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x2);
+ assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x3);
+ assertThat(widgetItemInTable.get(3)).containsExactly(mWidget2x4);
+ assertThat(widgetItemInTable.get(4)).containsExactly(mWidget4x4);
}
@Test
- public void groupWidgetItemsIntoTableWithReordering_mixItems_maxSpansPerRow4_shouldGroupWidgetsInTable() {
+ public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpanPxPerRow350_cellPadding0_shouldGroupWidgetsInTable() {
+ List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
+ mWidget2x2);
+
+ List<ArrayList<WidgetItem>> widgetItemInTable =
+ WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
+ mTestDeviceProfile, 350, 0);
+
+ // Row 0: 1x1(50px), 2x2(110px), 2x3(110px)
+ // Row 1: 2x4(110px)
+ // Row 2: 4x4(230px)
+ assertThat(widgetItemInTable).hasSize(3);
+ assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2, mWidget2x3);
+ assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x4);
+ assertThat(widgetItemInTable.get(2)).containsExactly(mWidget4x4);
+ }
+
+ @Test
+ public void groupWidgetItemsIntoTableWithReordering_mixItems_maxSpanPxPerRow350_cellPadding0_shouldGroupWidgetsInTable() {
List<WidgetItem> widgetItems = List.of(mWidget4x4, mShortcut3, mWidget2x3, mShortcut1,
mWidget1x1, mShortcut2, mWidget2x4, mWidget2x2);
List<ArrayList<WidgetItem>> widgetItemInTable =
- WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
- widgetItems, /* maxSpansPerRow= */ 4);
+ WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
+ mTestDeviceProfile, 350, 0);
- // Row 0: 1x1, 2x2
- // Row 1: 2x3,
- // Row 2: 2x4,
- // Row 3: 4x4
- // Row 4: shortcut3, shortcut1, shortcut2
- assertThat(widgetItemInTable).hasSize(5);
- assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
- assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3);
- assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x4);
- assertThat(widgetItemInTable.get(3)).containsExactly(mWidget4x4);
- assertThat(widgetItemInTable.get(4)).containsExactly(mShortcut3, mShortcut2, mShortcut1);
+ // Row 0: 1x1(50px), 2x2(110px), 2x3(110px)
+ // Row 1: 2x4(110px),
+ // Row 2: 4x4(230px)
+ // Row 3: shortcut3(50px), shortcut1(50px), shortcut2(50px)
+ assertThat(widgetItemInTable).hasSize(4);
+ assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2, mWidget2x3);
+ assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x4);
+ assertThat(widgetItemInTable.get(2)).containsExactly(mWidget4x4);
+ assertThat(widgetItemInTable.get(3)).containsExactly(mShortcut3, mShortcut2, mShortcut1);
}
@Test
- public void groupWidgetItemsIntoTableWithoutReordering_shouldMaintainTheOrder() {
+ public void groupWidgetItemsIntoTableWithoutReordering_maxSpanPxPerRow220_cellPadding0_shouldMaintainTheOrder() {
List<WidgetItem> widgetItems =
List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4, mWidget2x2);
List<ArrayList<WidgetItem>> widgetItemInTable =
- WidgetsTableUtils.groupWidgetItemsIntoTableWithoutReordering(
- widgetItems, /* maxSpansPerRow= */ 5);
+ WidgetsTableUtils.groupWidgetItemsUsingRowPxWithoutReordering(widgetItems, mContext,
+ mTestDeviceProfile, 220, 0);
- // Row 0: 4x4
- // Row 1: 2x3, 1x1
- // Row 2: 2x4, 2x2
+ // Row 0: 4x4(230px)
+ // Row 1: 2x3(110px), 1x1(50px)
+ // Row 2: 2x4(110px), 2x2(110px)
assertThat(widgetItemInTable).hasSize(3);
assertThat(widgetItemInTable.get(0)).containsExactly(mWidget4x4);
assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3, mWidget1x1);
assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x4, mWidget2x2);
}
+ private void initDP() {
+ doAnswer(i -> {
+ ((Point) i.getArgument(0)).set(CELL_SIZE, CELL_SIZE);
+ return null;
+ }).when(mTestDeviceProfile).getCellSize(any(Point.class));
+ when(mTestDeviceProfile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE));
+ mTestDeviceProfile.cellLayoutBorderSpacePx = new Point(SPACE_SIZE, SPACE_SIZE);
+ when(mTestDeviceProfile.shouldInsetWidgets()).thenReturn(false);
+ }
+
private void initTestWidgets() {
List<Point> widgetSizes = List.of(new Point(1, 1), new Point(2, 2), new Point(2, 3),
new Point(2, 4), new Point(4, 4));
@@ -184,7 +222,7 @@
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
widgetInfo.spanX = widgetSize.x;
widgetInfo.spanY = widgetSize.y;
- widgetItems.add(new WidgetItem(widgetInfo, mTestProfile, mIconCache));
+ widgetItems.add(new WidgetItem(widgetInfo, mTestInvariantProfile, mIconCache));
}
);
mWidget1x1 = widgetItems.get(0);
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
index 82d9630..667290f 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
@@ -54,5 +54,14 @@
return createMenuItem(menuItem);
}
+ /**
+ * Returns a menu item that matches the text "Split screen". Fails if it doesn't exist.
+ */
+ public SplitScreenMenuItem getSplitScreenMenuItem() {
+ final UiObject2 menuItem = mLauncher.waitForObjectInContainer(mDeepShortcutsContainer,
+ AppIcon.getAppIconSelector("Split screen", mLauncher));
+ return new SplitScreenMenuItem(mLauncher, menuItem);
+ }
+
protected abstract AppIconMenuItem createMenuItem(UiObject2 menuItem);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 3dcb437..48e327f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -76,6 +76,27 @@
}
}
+ /**
+ * Clicks a launcher object to initiate splitscreen, where the selected app will be one of two
+ * apps running on the screen. Should be called when Launcher is in a "split staging" state
+ * and is waiting for the user's selection of a second app. Expects a SPLIT_START_EVENT to be
+ * fired when the click is executed.
+ */
+ public LaunchedAppState launchIntoSplitScreen() {
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "want to launch split tasks from " + launchableType())) {
+ LauncherInstrumentation.log("Launchable.launch before click "
+ + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
+
+ mLauncher.clickLauncherObject(mObject);
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, OverviewTask.SPLIT_START_EVENT);
+ return new LaunchedAppState(mLauncher);
+ }
+ }
+ }
+
protected LaunchedAppState assertAppLaunched(BySelector selector) {
mLauncher.assertTrue(
"App didn't start: (" + selector + ")",
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index adc993d..90f3d13 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -37,10 +37,9 @@
public final class OverviewTask {
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
- static final Pattern TASK_START_EVENT =
- Pattern.compile("startActivityFromRecentsAsync");
- static final Pattern SPLIT_START_EVENT =
- Pattern.compile("launchSplitTasks");
+ static final Pattern TASK_START_EVENT = Pattern.compile("startActivityFromRecentsAsync");
+ static final Pattern SPLIT_SELECT_EVENT = Pattern.compile("enterSplitSelect");
+ static final Pattern SPLIT_START_EVENT = Pattern.compile("launchSplitTasks");
private final LauncherInstrumentation mLauncher;
private final UiObject2 mTask;
private final BaseOverview mOverview;
diff --git a/tests/tapl/com/android/launcher3/tapl/SplitScreenMenuItem.java b/tests/tapl/com/android/launcher3/tapl/SplitScreenMenuItem.java
new file mode 100644
index 0000000..47cf20b
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/SplitScreenMenuItem.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 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.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.testing.shared.TestProtocol;
+
+/**
+ * A class representing the "Split screen" menu item in the app long-press menu. Used for TAPL
+ * testing in a similar way as other menu items {@link AppIconMenuItem}, but unlike AppIconMenuItem,
+ * the split screen command does not trigger an app launch. Instead, it causes Launcher to shift to
+ * a different state (OverviewSplitSelect).
+ */
+public final class SplitScreenMenuItem {
+ private final LauncherInstrumentation mLauncher;
+ private final UiObject2 mObject;
+
+ SplitScreenMenuItem(LauncherInstrumentation launcher, UiObject2 object) {
+ mLauncher = launcher;
+ mObject = object;
+ }
+
+ /**
+ * Executes a click command on this menu item. Expects a SPLIT_SELECT_EVENT to be fired.
+ */
+ public void click() {
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "want to enter split select from app long-press menu")) {
+ LauncherInstrumentation.log("clicking on split screen menu item "
+ + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
+
+ mLauncher.clickLauncherObject(mObject);
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, OverviewTask.SPLIT_SELECT_EVENT);
+ mLauncher.waitForLauncherObject("split_placeholder");
+ }
+ }
+ }
+}