Merge "Remove ADAPTIVE_ICON_WINDOW_ANIM feature flag."
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index a72935b..af6cb84 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -15,10 +15,7 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.Utilities.dpToPx;
import static com.android.launcher3.Utilities.mapBoundToRange;
import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -26,10 +23,7 @@
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -42,15 +36,9 @@
import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.Workspace;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.SpringAnimationBuilder;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.views.FloatingView;
@@ -60,7 +48,6 @@
import com.android.quickstep.views.FloatingWidgetView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -257,61 +244,15 @@
private class FloatingViewHomeAnimationFactory extends LauncherHomeAnimationFactory {
- private final float mTransY;
private final FloatingView mFloatingView;
- private ValueAnimator mBounceBackAnimator;
FloatingViewHomeAnimationFactory(FloatingView floatingView) {
mFloatingView = floatingView;
-
- ResourceProvider rp = DynamicResource.provider(mActivity);
- mTransY = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
- }
-
- @Override
- public boolean shouldPlayAtomicWorkspaceReveal() {
- return false;
- }
-
- protected void bounceBackToRestingPosition() {
- final float startValue = mTransY;
- final float endValue = 0;
- // Ensures the velocity is always aligned with the direction.
- float pixelPerSecond = Math.abs(mSwipeVelocity) * Math.signum(endValue - mTransY);
-
- DragLayer dl = mActivity.getDragLayer();
- Workspace workspace = mActivity.getWorkspace();
- Hotseat hotseat = mActivity.getHotseat();
-
- ResourceProvider rp = DynamicResource.provider(mActivity);
- ValueAnimator springTransY = new SpringAnimationBuilder(dl.getContext())
- .setStiffness(rp.getFloat(R.dimen.swipe_up_trans_y_stiffness))
- .setDampingRatio(rp.getFloat(R.dimen.swipe_up_trans_y_damping))
- .setMinimumVisibleChange(1f)
- .setStartValue(startValue)
- .setEndValue(endValue)
- .setStartVelocity(pixelPerSecond)
- .build(dl, VIEW_TRANSLATE_Y);
- springTransY.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- dl.setTranslationY(0f);
- dl.setAlpha(1f);
- SCALE_PROPERTY.set(workspace, 1f);
- SCALE_PROPERTY.set(hotseat, 1f);
- }
- });
-
- mBounceBackAnimator = springTransY;
- mBounceBackAnimator.start();
}
@Override
public void onCancel() {
mFloatingView.fastFinish();
- if (mBounceBackAnimator != null) {
- mBounceBackAnimator.cancel();
- }
}
}
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 8e9b668..8d9a685 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -177,10 +177,6 @@
// No-op
}
- public boolean shouldPlayAtomicWorkspaceReveal() {
- return true;
- }
-
public void setAnimation(RectFSpringAnim anim) { }
public void update(RectF currentRect, float progress, float radius) { }
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index b428b67..22a30e9 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -44,8 +44,9 @@
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteTransitionCompat;
-import com.android.systemui.shared.system.smartspace.ISmartspaceCallback;
-import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionController;
+import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
+import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
+import com.android.systemui.shared.system.smartspace.SmartspaceState;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
@@ -73,7 +74,7 @@
private ISystemUiProxy mSystemUiProxy;
private IPip mPip;
- private ISmartspaceTransitionController mSmartspaceTransitionController;
+ private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
private ISplitScreen mSplitScreen;
private IOneHanded mOneHanded;
private IShellTransitions mShellTransitions;
@@ -90,7 +91,7 @@
private IPipAnimationListener mPipAnimationListener;
private ISplitScreenListener mSplitScreenListener;
private IStartingWindowListener mStartingWindowListener;
- private ISmartspaceCallback mSmartspaceCallback;
+ private ILauncherUnlockAnimationController mPendingLauncherUnlockAnimationController;
private IRecentTasksListener mRecentTasksListener;
private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
@@ -157,7 +158,8 @@
public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
IOneHanded oneHanded, IShellTransitions shellTransitions,
IStartingWindow startingWindow, IRecentTasks recentTasks,
- ISmartspaceTransitionController smartSpaceTransitionController) {
+ ISysuiUnlockAnimationController sysuiUnlockAnimationController) {
+
unlinkToDeath();
mSystemUiProxy = proxy;
mPip = pip;
@@ -165,7 +167,7 @@
mOneHanded = oneHanded;
mShellTransitions = shellTransitions;
mStartingWindow = startingWindow;
- mSmartspaceTransitionController = smartSpaceTransitionController;
+ mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
mRecentTasks = recentTasks;
linkToDeath();
// re-attach the listeners once missing due to setProxy has not been initialized yet.
@@ -178,8 +180,10 @@
if (mStartingWindowListener != null && mStartingWindow != null) {
setStartingWindowListener(mStartingWindowListener);
}
- if (mSmartspaceCallback != null && mSmartspaceTransitionController != null) {
- setSmartspaceCallback(mSmartspaceCallback);
+ if (mPendingLauncherUnlockAnimationController != null
+ && mSysuiUnlockAnimationController != null) {
+ setLauncherUnlockAnimationController(mPendingLauncherUnlockAnimationController);
+ mPendingLauncherUnlockAnimationController = null;
}
for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
registerRemoteTransition(mRemoteTransitions.get(i));
@@ -731,15 +735,42 @@
// SmartSpace transitions
//
- public void setSmartspaceCallback(ISmartspaceCallback callback) {
- if (mSmartspaceTransitionController != null) {
+ /**
+ * Sets the instance of {@link ILauncherUnlockAnimationController} that System UI should use to
+ * control the launcher side of the unlock animation. This will also cause us to dispatch the
+ * current state of the smartspace to System UI (this will subsequently happen if the state
+ * changes).
+ */
+ public void setLauncherUnlockAnimationController(
+ ILauncherUnlockAnimationController controller) {
+ if (mSysuiUnlockAnimationController != null) {
try {
- mSmartspaceTransitionController.setSmartspace(callback);
+ mSysuiUnlockAnimationController.setLauncherUnlockController(controller);
+
+ if (controller != null) {
+ controller.dispatchSmartspaceStateToSysui();
+ }
} catch (RemoteException e) {
Log.w(TAG, "Failed call setStartingWindowListener", e);
}
+ } else {
+ mPendingLauncherUnlockAnimationController = controller;
}
- mSmartspaceCallback = callback;
+ }
+
+ /**
+ * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare
+ * the unlock animation accordingly.
+ */
+ public void notifySysuiSmartspaceStateUpdated(SmartspaceState state) {
+ if (mSysuiUnlockAnimationController != null) {
+ try {
+ mSysuiUnlockAnimationController.onLauncherSmartspaceStateUpdated(state);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call notifySysuiSmartspaceStateUpdated", e);
+ e.printStackTrace();
+ }
+ }
}
//
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index d09a5d4..0c34ead 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -31,8 +31,8 @@
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -105,7 +105,7 @@
import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
-import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionController;
+import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
@@ -162,15 +162,15 @@
bundle.getBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS));
IStartingWindow startingWindow = IStartingWindow.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SHELL_STARTING_WINDOW));
- ISmartspaceTransitionController smartspaceTransitionController =
- ISmartspaceTransitionController.Stub.asInterface(
- bundle.getBinder(KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER));
+ ISysuiUnlockAnimationController launcherUnlockAnimationController =
+ ISysuiUnlockAnimationController.Stub.asInterface(
+ bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER));
IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
MAIN_EXECUTOR.execute(() -> {
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
- smartspaceTransitionController);
+ launcherUnlockAnimationController);
TouchInteractionService.this.initInputMonitor();
preloadOverview(true /* fromInit */);
});
diff --git a/quickstep/src/com/android/quickstep/util/WorkspaceUnlockAnim.java b/quickstep/src/com/android/quickstep/util/WorkspaceUnlockAnim.java
new file mode 100644
index 0000000..b01447e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/WorkspaceUnlockAnim.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 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.util;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.Utilities;
+
+/**
+ * Animation to animate in a workspace during the unlock transition.
+ */
+public class WorkspaceUnlockAnim {
+ /** Scale for the workspace icons at the beginning of the animation. */
+ private static final float START_SCALE = 0.9f;
+
+ /**
+ * Starting translation Y values for the animation. We use a larger value if we're animating in
+ * from a swipe, since there is more perceived upward movement when we unlock from a swipe.
+ */
+ private static final int START_TRANSLATION_DP = 15;
+ private static final int START_TRANSLATION_SWIPE_DP = 60;
+
+ private Launcher mLauncher;
+ private float mUnlockAmount = 0f;
+
+ public WorkspaceUnlockAnim(Launcher launcher) {
+ mLauncher = launcher;
+ }
+
+ /**
+ * Called when we're about to make the Launcher window visible and play the unlock animation.
+ *
+ * This is a blocking call so that System UI knows it's safe to show the Launcher window without
+ * causing the Launcher contents to flicker on screen. Do not do anything expensive here.
+ */
+ public void prepareForUnlock() {
+ mLauncher.getWorkspace().setAlpha(0f);
+ mLauncher.getHotseat().setAlpha(0f);
+
+ mUnlockAmount = 0f;
+ }
+
+ public void setUnlockAmount(float amount, boolean fromSwipe) {
+ mUnlockAmount = amount;
+
+ final float amountInverse = 1f - amount;
+ final float scale = START_SCALE + (1f - START_SCALE) * amount;
+
+ mLauncher.getWorkspace().setScaleX(scale);
+ mLauncher.getWorkspace().setScaleY(scale);
+ mLauncher.getWorkspace().setAlpha(amount);
+ mLauncher.getWorkspace().setPivotToScaleWithSelf(mLauncher.getHotseat());
+
+ mLauncher.getHotseat().setScaleX(scale);
+ mLauncher.getHotseat().setScaleY(scale);
+ mLauncher.getHotseat().setAlpha(amount);
+
+ if (fromSwipe) {
+ mLauncher.getWorkspace().setTranslationY(
+ Utilities.dpToPx(START_TRANSLATION_SWIPE_DP) * amountInverse);
+ mLauncher.getHotseat().setTranslationY(
+ Utilities.dpToPx(START_TRANSLATION_SWIPE_DP) * amountInverse);
+ } else {
+ mLauncher.getWorkspace().setTranslationY(
+ Utilities.dpToPx(START_TRANSLATION_DP) * amountInverse);
+ mLauncher.getHotseat().setTranslationY(
+ Utilities.dpToPx(START_TRANSLATION_DP) * amountInverse);
+ }
+ }
+}
diff --git a/res/values/config.xml b/res/values/config.xml
index d5629b5..1c996eb 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -139,19 +139,9 @@
<item name="swipe_up_scale_start" type="dimen" format="float">0.88</item>
- <item name="swipe_up_trans_y_dp" type="dimen" format="float">4.5</item>
- <item name="swipe_up_trans_y_dp_per_s" type="dimen" format="float">3</item>
-
- <item name="swipe_up_trans_y_damping" type="dimen" format="float">0.45</item>
- <item name="swipe_up_trans_y_stiffness" type="dimen" format="float">200</item>
-
<item name="swipe_up_rect_xy_damping_ratio" type="dimen" format="float">0.8</item>
<item name="swipe_up_rect_xy_stiffness" type="dimen" format="float">200</item>
- <item name="swipe_up_low_swipe_duration_multiplier" type="dimen" format="float">1</item>
-
- <item name="swipe_up_launcher_alpha_max_progress" type="dimen" format="float">0.85</item>
-
<item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
<item name="staggered_stiffness" type="dimen" format="float">150</item>
<dimen name="unlock_staggered_velocity_dp_per_s">2dp</dimen>
@@ -167,12 +157,6 @@
<array name="dynamic_resources">
<item>@dimen/swipe_up_scale_start</item>
- <item>@dimen/swipe_up_trans_y_dp</item>
- <item>@dimen/swipe_up_trans_y_dp_per_s</item>
- <item>@dimen/swipe_up_trans_y_damping</item>
- <item>@dimen/swipe_up_trans_y_stiffness</item>
- <item>@dimen/swipe_up_launcher_alpha_max_progress</item>
- <item>@dimen/swipe_up_low_swipe_duration_multiplier</item>
<item>@dimen/swipe_up_max_velocity</item>
</array>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 167c6b2..e215db9 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -33,6 +33,7 @@
<dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
<dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
<dimen name="dynamic_grid_hotseat_bottom_tall_padding">0dp</dimen>
+ <dimen name="inline_qsb_bottom_margin">0dp</dimen>
<!-- Qsb -->
<!-- Used for adjusting the position of QSB when placed in hotseat. This is a ratio and a higher
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index a2324ac..bb03b91 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -61,6 +61,7 @@
public final boolean isPhone;
public final boolean transposeLayoutWithOrientation;
public final boolean isTwoPanels;
+ public final boolean isQsbInline;
// Device properties in current orientation
public final boolean isLandscape;
@@ -165,6 +166,7 @@
public final float qsbBottomMarginOriginalPx;
public int qsbBottomMarginPx;
+ public int qsbWidth; // only used when isQsbInline
// All apps
public Point allAppsBorderSpacePx;
@@ -247,6 +249,7 @@
isPhone = !isTablet;
isTwoPanels = isTablet && useTwoPanels;
+
aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
mQsbCenterFactor = context.getResources().getFloat(R.dimen.qsb_center_factor);
@@ -271,7 +274,6 @@
}
}
- hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS
&& FeatureFlags.ENABLE_TASKBAR.get();
if (isTaskbarPresent) {
@@ -334,6 +336,8 @@
workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
+ hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
+ isQsbInline = isTablet && isLandscape && !isTwoPanels && hotseatQsbHeight > 0;
numShownHotseatIcons =
isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
numShownAllAppsColumns =
@@ -341,10 +345,17 @@
hotseatBarSizeExtraSpacePx = 0;
hotseatBarTopPaddingPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
- hotseatBarBottomPaddingPx = (isTallDevice ? res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_bottom_tall_padding)
- : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_non_tall_padding))
- + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
+ if (isQsbInline) {
+ hotseatBarBottomPaddingPx = res.getDimensionPixelSize(R.dimen.inline_qsb_bottom_margin);
+ qsbWidth = calculateQsbWidth();
+ } else {
+ hotseatBarBottomPaddingPx = (isTallDevice ? res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_bottom_tall_padding)
+ : res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_bottom_non_tall_padding))
+ + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
+ qsbWidth = 0;
+ }
hotseatBarSidePaddingEndPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
// Add a bit of space between nav bar and hotseat in vertical bar layout.
@@ -473,6 +484,14 @@
new DotRenderer(allAppsIconSizePx, dotPath, DEFAULT_DOT_SIZE);
}
+ private int calculateQsbWidth() {
+ return cellWidthPx * inv.numColumns
+ + cellLayoutBorderSpacePx.x * (inv.numColumns - 1)
+ - (cellWidthPx - iconSizePx) // left and right cell space
+ - iconSizePx * numShownHotseatIcons
+ - hotseatBorderSpace * numShownHotseatIcons;
+ }
+
private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) {
if (isVerticalBarLayout()) {
return 0;
@@ -723,6 +742,11 @@
// Hotseat
hotseatBorderSpace = pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics, scale);
+ if (isQsbInline) {
+ qsbWidth = calculateQsbWidth();
+ } else {
+ qsbWidth = 0;
+ }
updateHotseatIconSize(iconSizePx);
if (!isVerticalLayout) {
@@ -901,15 +925,24 @@
} else if (isTaskbarPresent) {
int hotseatHeight = workspacePadding.bottom;
int taskbarOffset = getTaskbarOffsetY();
+ int additionalLeftSpace = 0;
+
+ // Center the QSB with hotseat and push icons to the right
+ if (isQsbInline) {
+ additionalLeftSpace = qsbWidth + hotseatBorderSpace;
+ }
+
int hotseatTopDiff = hotseatHeight - taskbarOffset;
int endOffset = ApiWrapper.getHotseatEndOffset(context);
int requiredWidth = iconSizePx * numShownHotseatIcons
- + hotseatBorderSpace * (numShownHotseatIcons - 1);
+ + hotseatBorderSpace * (numShownHotseatIcons - 1)
+ + additionalLeftSpace;
int hotseatSize = Math.min(requiredWidth, availableWidthPx - endOffset);
int sideSpacing = (availableWidthPx - hotseatSize) / 2;
- mHotseatPadding.set(sideSpacing, hotseatTopDiff, sideSpacing, taskbarOffset);
+ mHotseatPadding.set(sideSpacing + additionalLeftSpace, hotseatTopDiff, sideSpacing,
+ taskbarOffset);
if (endOffset > sideSpacing) {
int diff = Utilities.isRtl(context.getResources())
@@ -942,6 +975,10 @@
* Returns the number of pixels the QSB is translated from the bottom of the screen.
*/
public int getQsbOffsetY() {
+ if (isQsbInline) {
+ return hotseatBarBottomPaddingPx;
+ }
+
int freeSpace = isTaskbarPresent
? workspacePadding.bottom
: hotseatBarSizePx - hotseatCellHeightPx - hotseatQsbHeight;
@@ -959,7 +996,11 @@
* Returns the number of pixels the taskbar is translated from the bottom of the screen.
*/
public int getTaskbarOffsetY() {
- return (getQsbOffsetY() - taskbarSize) / 2;
+ if (isQsbInline) {
+ return getQsbOffsetY() + (Math.abs(hotseatQsbHeight - iconSizePx) / 2);
+ } else {
+ return (getQsbOffsetY() - taskbarSize) / 2;
+ }
}
/**
@@ -1122,6 +1163,8 @@
writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
hotseatBarSidePaddingEndPx));
writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons);
+ writer.println(prefix + "\tisQsbInline: " + isQsbInline);
+ writer.println(prefix + pxToDpStr("qsbWidth", qsbWidth));
writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent);
writer.println(prefix + "\tisTaskbarPresentInApps:" + isTaskbarPresentInApps);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index ffe3816..cb0cc11 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -173,7 +173,10 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int width = getShortcutsAndWidgets().getMeasuredWidth();
+ int width = mActivity.getDeviceProfile().isQsbInline
+ ? mActivity.getDeviceProfile().qsbWidth
+ : getShortcutsAndWidgets().getMeasuredWidth();
+
mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
}
@@ -183,7 +186,13 @@
super.onLayout(changed, l, t, r, b);
int qsbWidth = mQsb.getMeasuredWidth();
- int left = (r - l - qsbWidth) / 2;
+ int left;
+ if (mActivity.getDeviceProfile().isQsbInline) {
+ int qsbSpace = mActivity.getDeviceProfile().hotseatBorderSpace;
+ left = l + getPaddingLeft() - qsbWidth - qsbSpace;
+ } else {
+ left = (r - l - qsbWidth) / 2;
+ }
int right = left + qsbWidth;
int bottom = b - t - mActivity.getDeviceProfile().getQsbOffsetY();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d67d746..3c4ccc6 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2783,6 +2783,17 @@
}
/**
+ * Informs us that the overlay (-1 screen, typically), has either become visible or invisible.
+ */
+ public void onOverlayVisibilityChanged(boolean visible) {}
+
+ /**
+ * Informs us that the page transition has ended, so that we can react to the newly selected
+ * page if we want to.
+ */
+ public void onPageEndTransition() {}
+
+ /**
* Add the icons for all apps.
*
* Implementation of the method from LauncherModel.Callbacks.
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index fec1d68..5583eae 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -19,6 +19,7 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static com.android.launcher3.CellLayout.FOLDER;
+import static com.android.launcher3.CellLayout.HOTSEAT;
import static com.android.launcher3.CellLayout.WORKSPACE;
import android.app.WallpaperManager;
@@ -146,7 +147,8 @@
// No need to add padding when cell layout border spacing is present.
boolean noPaddingX =
(dp.cellLayoutBorderSpacePx.x > 0 && mContainerType == WORKSPACE)
- || (dp.folderCellLayoutBorderSpacePx.x > 0 && mContainerType == FOLDER);
+ || (dp.folderCellLayoutBorderSpacePx.x > 0 && mContainerType == FOLDER)
+ || (dp.hotseatBorderSpace > 0 && mContainerType == HOTSEAT);
int cellPaddingX = noPaddingX
? 0
: mContainerType == WORKSPACE
@@ -251,7 +253,7 @@
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
// While the folder is open, the position of the icon cannot change.
lp.canReorder = false;
- if (mContainerType == CellLayout.HOTSEAT) {
+ if (mContainerType == HOTSEAT) {
CellLayout cl = (CellLayout) getParent();
cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
}
@@ -260,7 +262,7 @@
@Override
public void clearFolderLeaveBehind(FolderIcon child) {
((CellLayout.LayoutParams) child.getLayoutParams()).canReorder = true;
- if (mContainerType == CellLayout.HOTSEAT) {
+ if (mContainerType == HOTSEAT) {
CellLayout cl = (CellLayout) getParent();
cl.clearFolderLeaveBehind();
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 65006ff..b3ff38f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1135,6 +1135,10 @@
stripEmptyScreens();
mStripScreensOnPageStopMoving = false;
}
+
+ // Inform the Launcher activity that the page transition ended so that it can react to the
+ // newly visible page if it wants to.
+ mLauncher.onPageEndTransition();
}
public void setLauncherOverlay(LauncherOverlay overlay) {
@@ -1211,6 +1215,10 @@
.log(LAUNCHER_SWIPELEFT);
}
mOverlayShown = true;
+
+ // Let the Launcher activity know that the overlay is now visible.
+ mLauncher.onOverlayVisibilityChanged(mOverlayShown);
+
// Not announcing the overlay page for accessibility since it announces itself.
} else if (Float.compare(scroll, 0f) == 0) {
if (mOverlayShown) {
@@ -1234,6 +1242,10 @@
announcePageForAccessibility();
}
mOverlayShown = false;
+
+ // Let the Launcher activity know that the overlay is no longer visible.
+ mLauncher.onOverlayVisibilityChanged(mOverlayShown);
+
tryRunOverlayCallback();
}
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index e036943..b45c97b 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -152,7 +152,7 @@
/**
* Called if the Activity UI changed while the activity was not visible
*/
- protected void onUiChangedWhileSleeping() { }
+ public void onUiChangedWhileSleeping() { }
private void handleDeferredResume() {
if (hasBeenResumed() && !getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE)) {
diff --git a/tests/Android.bp b/tests/Android.bp
index 8def20f..f3e0500 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -23,7 +23,10 @@
// Source code used for test
filegroup {
name: "launcher-tests-src",
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt"
+ ],
}
// Source code used for oop test helpers
diff --git a/tests/src/com/android/launcher3/DeviceProfileTest.kt b/tests/src/com/android/launcher3/DeviceProfileTest.kt
new file mode 100644
index 0000000..6c99a21
--- /dev/null
+++ b/tests/src/com/android/launcher3/DeviceProfileTest.kt
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+package com.android.launcher3
+
+import android.content.Context
+import android.graphics.PointF
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.util.DisplayController.Info
+import com.android.launcher3.util.WindowBounds
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.*
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTest {
+
+ private var context: Context? = null
+ private var inv: InvariantDeviceProfile? = null
+ private var info: Info = mock(Info::class.java)
+ private var windowBounds: WindowBounds? = null
+ private var isMultiWindowMode: Boolean = false
+ private var transposeLayoutWithOrientation: Boolean = false
+ private var useTwoPanels: Boolean = false
+
+ @Before
+ fun setUp() {
+ context = ApplicationProvider.getApplicationContext()
+ // make sure to reset values
+ useTwoPanels = false
+ }
+
+ @Test
+ fun qsbWidth_is_match_parent_for_phones() {
+ initializeVarsForPhone()
+
+ val dp = DeviceProfile(
+ context,
+ inv,
+ info,
+ windowBounds,
+ isMultiWindowMode,
+ transposeLayoutWithOrientation,
+ useTwoPanels
+ )
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.qsbWidth).isEqualTo(0)
+ }
+
+ @Test
+ fun qsbWidth_is_match_parent_for_tablet_portrait() {
+ initializeVarsForTablet()
+
+ val dp = DeviceProfile(
+ context,
+ inv,
+ info,
+ windowBounds,
+ isMultiWindowMode,
+ transposeLayoutWithOrientation,
+ useTwoPanels
+ )
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.qsbWidth).isEqualTo(0)
+ }
+
+ @Test
+ fun qsbWidth_has_size_for_tablet_landscape() {
+ initializeVarsForTablet(true)
+
+ val dp = DeviceProfile(
+ context,
+ inv,
+ info,
+ windowBounds,
+ isMultiWindowMode,
+ transposeLayoutWithOrientation,
+ useTwoPanels
+ )
+
+ if (dp.hotseatQsbHeight > 0) {
+ assertThat(dp.isQsbInline).isTrue()
+ assertThat(dp.qsbWidth).isGreaterThan(0)
+ } else {
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.qsbWidth).isEqualTo(0)
+ }
+ }
+
+ /**
+ * This test is to make sure that two panels don't inline the QSB as tablets do
+ */
+ @Test
+ fun qsbWidth_is_match_parent_for_two_panel_landscape() {
+ initializeVarsForTablet(true)
+ useTwoPanels = true
+
+ val dp = DeviceProfile(
+ context,
+ inv,
+ info,
+ windowBounds,
+ isMultiWindowMode,
+ transposeLayoutWithOrientation,
+ useTwoPanels
+ )
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.qsbWidth).isEqualTo(0)
+ }
+
+ private fun initializeVarsForPhone(isLandscape: Boolean = false) {
+ val (x, y) = if (isLandscape)
+ Pair(3120, 1440)
+ else
+ Pair(1440, 3120)
+
+ windowBounds = WindowBounds(x, y, x, y - 100)
+
+ `when`(info.isTablet(any())).thenReturn(false)
+
+ scalableInvariantDeviceProfile()
+ }
+
+ private fun initializeVarsForTablet(isLandscape: Boolean = false) {
+ val (x, y) = if (isLandscape)
+ Pair(2560, 1600)
+ else
+ Pair(1600, 2560)
+
+ windowBounds = WindowBounds(x, y, x, y - 100)
+
+ `when`(info.isTablet(any())).thenReturn(true)
+
+ scalableInvariantDeviceProfile()
+ }
+
+ /**
+ * A very generic grid, just to make qsb tests work. For real calculations, make sure to use
+ * values that better represent a real grid.
+ */
+ private fun scalableInvariantDeviceProfile() {
+ inv = InvariantDeviceProfile().apply {
+ isScalable = true
+ numColumns = 5
+ numRows = 5
+ horizontalMargin = FloatArray(4) { 22f }
+ borderSpaces = listOf(
+ PointF(16f, 16f),
+ PointF(16f, 16f),
+ PointF(16f, 16f),
+ PointF(16f, 16f)
+ ).toTypedArray()
+ allAppsBorderSpaces = listOf(
+ PointF(16f, 16f),
+ PointF(16f, 16f),
+ PointF(16f, 16f),
+ PointF(16f, 16f)
+ ).toTypedArray()
+ hotseatBorderSpaces = FloatArray(4) { 16f }
+ iconSize = FloatArray(4) { 56f }
+ allAppsIconSize = FloatArray(4) { 56f }
+ iconTextSize = FloatArray(4) { 14f }
+ allAppsIconTextSize = FloatArray(4) { 14f }
+ minCellSize = listOf(
+ PointF(64f, 83f),
+ PointF(64f, 83f),
+ PointF(64f, 83f),
+ PointF(64f, 83f)
+ ).toTypedArray()
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index ccbb662..0c1f0cd 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -42,6 +42,7 @@
import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.Wait.Condition;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.util.rule.ShellCommandRule;
import org.junit.Before;
@@ -77,6 +78,7 @@
public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ }
@Test
+ @ScreenRecord // b/215673732
public void testPinWidgetNoConfig() throws Throwable {
runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
@@ -85,6 +87,7 @@
}
@Test
+ @ScreenRecord // b/215673732
public void testPinWidgetNoConfig_customPreview() throws Throwable {
// Command to set custom preview
Intent command = RequestPinItemActivity.getCommandIntent(
@@ -98,6 +101,7 @@
}
@Test
+ @ScreenRecord // b/215673732
public void testPinWidgetWithConfig() throws Throwable {
runTest("pinWidgetWithConfig", true,
(info, view) -> info instanceof LauncherAppWidgetInfo &&