Merge "Resize preview for correct clipping" into tm-qpr-dev
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 6bc3d38..c85e71c 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -277,6 +277,7 @@
<!-- Taskbar 3 button spacing -->
<dimen name="taskbar_button_space_inbetween">24dp</dimen>
+ <dimen name="taskbar_button_space_inbetween_phone">40dp</dimen>
<dimen name="taskbar_button_margin_5_5">26dp</dimen>
<dimen name="taskbar_button_margin_6_5">75dp</dimen>
<dimen name="taskbar_button_margin_4_5">47dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index d251f3e..d348cc3 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -517,6 +517,7 @@
appsView.setAlpha(startAlpha);
SCALE_PROPERTY.set(appsView, startScale);
appsView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mLauncher.resumeExpensiveViewUpdates();
};
} else if (mLauncher.isInState(OVERVIEW)) {
endListener = composeViewContentAnimator(launcherAnimator, alphas, scales);
@@ -634,6 +635,7 @@
overview.setFreezeViewVisibility(false);
SCALE_PROPERTY.set(overview, 1f);
mLauncher.getStateManager().reapplyState();
+ mLauncher.resumeExpensiveViewUpdates();
};
}
@@ -1416,7 +1418,7 @@
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- anim.start(mLauncher, velocityPxPerS);
+ anim.start(mLauncher, mDeviceProfile, velocityPxPerS);
}
});
return anim;
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 55c3c20..f1f18c1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -15,15 +15,20 @@
*/
package com.android.launcher3.taskbar;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.SYSUI_SURFACE_PROGRESS_INDEX;
+import static com.android.launcher3.taskbar.TaskbarManager.isPhoneButtonNavMode;
+import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_KEYGUARD;
+import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_SMALL_SCREEN;
import static com.android.launcher3.taskbar.Utilities.appendFlag;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
@@ -109,6 +114,7 @@
private static final int FLAG_NOTIFICATION_SHADE_EXPANDED = 1 << 10;
private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 11;
private static final int FLAG_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 12;
+ private static final int FLAG_SMALL_SCREEN = 1 << 13;
private static final String NAV_BUTTONS_SEPARATE_WINDOW_TITLE = "Taskbar Nav Buttons";
@@ -122,7 +128,7 @@
private final TaskbarActivityContext mContext;
private final FrameLayout mNavButtonsView;
- private final ViewGroup mNavButtonContainer;
+ private final LinearLayout mNavButtonContainer;
// Used for IME+A11Y buttons
private final ViewGroup mEndContextualContainer;
private final ViewGroup mStartContextualContainer;
@@ -180,9 +186,13 @@
*/
public void init(TaskbarControllers controllers) {
mControllers = controllers;
- mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize;
-
boolean isThreeButtonNav = mContext.isThreeButtonNav();
+ DeviceProfile deviceProfile = mContext.getDeviceProfile();
+ Resources resources = mContext.getResources();
+ mNavButtonsView.getLayoutParams().height = !isPhoneMode(deviceProfile) ?
+ deviceProfile.taskbarSize :
+ resources.getDimensionPixelSize(R.dimen.taskbar_size);
+
mIsImeRenderingNavButtons =
InputMethodService.canImeRenderGesturalNavButtons() && mContext.imeDrawsImeNavBar();
if (!mIsImeRenderingNavButtons) {
@@ -201,6 +211,11 @@
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0
&& (flags & FLAG_SCREEN_PINNING_ACTIVE) == 0));
+ mPropertyHolders.add(new StatePropertyHolder(
+ mControllers.taskbarViewController.getTaskbarIconAlpha()
+ .getProperty(ALPHA_INDEX_SMALL_SCREEN),
+ flags -> (flags & FLAG_SMALL_SCREEN) == 0));
+
mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
.getKeyguardBgTaskbar(), flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0));
@@ -231,7 +246,7 @@
initButtons(mNavButtonContainer, mEndContextualContainer,
mControllers.navButtonController);
updateButtonLayoutSpacing();
-
+ updateStateForFlag(FLAG_SMALL_SCREEN, isPhoneButtonNavMode(mContext));
if (isInSetup) {
// Since setup wizard only has back button enabled, it looks strange to be
// end-aligned, so start-align instead.
@@ -244,18 +259,18 @@
// TODO(b/210906568) Dark intensity is currently not propagated during setup, so set
// it based on dark theme for now.
- int mode = mContext.getResources().getConfiguration().uiMode
+ int mode = resources.getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK;
boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
} else if (isInKidsMode) {
- int iconSize = mContext.getResources().getDimensionPixelSize(
+ int iconSize = resources.getDimensionPixelSize(
R.dimen.taskbar_icon_size_kids);
- int buttonWidth = mContext.getResources().getDimensionPixelSize(
+ int buttonWidth = resources.getDimensionPixelSize(
R.dimen.taskbar_nav_buttons_width_kids);
- int buttonHeight = mContext.getResources().getDimensionPixelSize(
+ int buttonHeight = resources.getDimensionPixelSize(
R.dimen.taskbar_nav_buttons_height_kids);
- int buttonRadius = mContext.getResources().getDimensionPixelSize(
+ int buttonRadius = resources.getDimensionPixelSize(
R.dimen.taskbar_nav_buttons_corner_radius_kids);
int paddingleft = (buttonWidth - iconSize) / 2;
int paddingRight = paddingleft;
@@ -277,7 +292,7 @@
buttonWidth,
buttonHeight
);
- int homeButtonLeftMargin = mContext.getResources().getDimensionPixelSize(
+ int homeButtonLeftMargin = resources.getDimensionPixelSize(
R.dimen.taskbar_home_button_left_margin_kids);
homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0);
mHomeButton.setLayoutParams(homeLayoutparams);
@@ -287,7 +302,7 @@
buttonWidth,
buttonHeight
);
- int backButtonLeftMargin = mContext.getResources().getDimensionPixelSize(
+ int backButtonLeftMargin = resources.getDimensionPixelSize(
R.dimen.taskbar_back_button_left_margin_kids);
backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0);
mBackButton.setLayoutParams(backLayoutParams);
@@ -342,7 +357,7 @@
if (!mIsImeRenderingNavButtons) {
View imeDownButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
mStartContextualContainer, mControllers.navButtonController, R.id.back);
- imeDownButton.setRotation(Utilities.isRtl(mContext.getResources()) ? 90 : -90);
+ imeDownButton.setRotation(Utilities.isRtl(resources) ? 90 : -90);
// Only show when IME is visible.
mPropertyHolders.add(new StatePropertyHolder(imeDownButton,
flags -> (flags & FLAG_IME_VISIBLE) != 0));
@@ -614,6 +629,9 @@
}
private void updateNavButtonTranslationY() {
+ if (isPhoneButtonNavMode(mContext)) {
+ return;
+ }
final float normalTranslationY = mTaskbarNavButtonTranslationY.value;
final float imeAdjustmentTranslationY = mTaskbarNavButtonTranslationYForIme.value;
TaskbarUIController uiController = mControllers.uiController;
@@ -683,12 +701,22 @@
if (!mContext.isThreeButtonNav() || mContext.isNavBarKidsModeActive()) {
return;
}
+
+ if (isPhoneButtonNavMode(mContext)) {
+ updatePhoneButtonSpacing();
+ return;
+ }
+
DeviceProfile dp = mContext.getDeviceProfile();
Resources res = mContext.getResources();
// Add spacing after the end of the last nav button
FrameLayout.LayoutParams navButtonParams =
(FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
+ navButtonParams.gravity = Gravity.END;
+ navButtonParams.width = FrameLayout.LayoutParams.WRAP_CONTENT;
+ navButtonParams.height = MATCH_PARENT;
+
int navMarginEnd = (int) res.getDimension(dp.inv.inlineNavButtonsEndSpacing);
int contextualWidth = mEndContextualContainer.getWidth();
// If contextual buttons are showing, we check if the end margin is enough for the
@@ -706,6 +734,39 @@
View navButton = mNavButtonContainer.getChildAt(i);
LinearLayout.LayoutParams buttonLayoutParams =
(LinearLayout.LayoutParams) navButton.getLayoutParams();
+ buttonLayoutParams.weight = 0;
+ if (i == 0) {
+ buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
+ } else if (i == mNavButtonContainer.getChildCount() - 1) {
+ buttonLayoutParams.setMarginStart(spaceInBetween / 2);
+ } else {
+ buttonLayoutParams.setMarginStart(spaceInBetween / 2);
+ buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
+ }
+ }
+ }
+
+ /** Uniformly spaces out the 3 button nav for smaller phone screens */
+ private void updatePhoneButtonSpacing() {
+ DeviceProfile dp = mContext.getDeviceProfile();
+ Resources res = mContext.getResources();
+
+ // TODO: Polish pending, this is just to make it usable
+ FrameLayout.LayoutParams navContainerParams =
+ (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
+ int endStartMargins = res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size);
+ navContainerParams.gravity = Gravity.CENTER;
+ navContainerParams.setMarginEnd(endStartMargins);
+ navContainerParams.setMarginStart(endStartMargins);
+ mNavButtonContainer.setLayoutParams(navContainerParams);
+
+ // Add the spaces in between the nav buttons
+ int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone);
+ for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
+ View navButton = mNavButtonContainer.getChildAt(i);
+ LinearLayout.LayoutParams buttonLayoutParams =
+ (LinearLayout.LayoutParams) navButton.getLayoutParams();
+ buttonLayoutParams.weight = 1;
if (i == 0) {
buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
} else if (i == mNavButtonContainer.getChildCount() - 1) {
@@ -725,6 +786,8 @@
}
moveNavButtonsBackToTaskbarWindow();
+ mNavButtonContainer.removeAllViews();
+ mAllButtons.clear();
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 0652463..6b67b50 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -96,7 +94,7 @@
mControllers = controllers;
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
Resources resources = mActivity.getResources();
- if (isPhoneMode(mActivity.getDeviceProfile())) {
+ if (isPhoneGestureNavMode(mActivity.getDeviceProfile())) {
mStashedHandleView.getLayoutParams().height =
resources.getDimensionPixelSize(R.dimen.taskbar_size);
mStashedHandleWidth =
@@ -108,7 +106,7 @@
}
mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_STASHED).setValue(
- isPhoneMode(deviceProfile) ? 1 : 0);
+ isPhoneGestureNavMode(deviceProfile) ? 1 : 0);
mTaskbarStashedHandleHintScale.updateValue(1f);
final int stashedTaskbarHeight = mControllers.taskbarStashController.getStashedHeight();
@@ -136,7 +134,7 @@
view.setPivotY(stashedCenterY);
});
initRegionSampler();
- if (isPhoneMode(deviceProfile)) {
+ if (isPhoneGestureNavMode(deviceProfile)) {
onIsStashedChanged(true);
}
}
@@ -164,6 +162,10 @@
mRegionSamplingHelper = null;
}
+ private boolean isPhoneGestureNavMode(DeviceProfile deviceProfile) {
+ return TaskbarManager.isPhoneMode(deviceProfile) && !mActivity.isThreeButtonNav();
+ }
+
public MultiValueAlpha getStashedHandleAlpha() {
return mTaskbarStashedHandleAlpha;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index f5fe77a..e1bcbe2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -119,7 +119,7 @@
// The size we should return to when we call setTaskbarWindowFullscreen(false)
private int mLastRequestedNonFullscreenHeight;
- private final NavigationMode mNavMode;
+ private NavigationMode mNavMode;
private final boolean mImeDrawsImeNavBar;
private final ViewCache mViewCache = new ViewCache();
@@ -235,7 +235,8 @@
}
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
- public void updateDeviceProfile(DeviceProfile dp) {
+ public void updateDeviceProfile(DeviceProfile dp, NavigationMode navMode) {
+ mNavMode = navMode;
mControllers.taskbarAllAppsController.updateDeviceProfile(dp);
mDeviceProfile = dp.copy(this);
updateIconSize(getResources());
@@ -608,7 +609,10 @@
*/
public int getDefaultTaskbarWindowHeight() {
if (FLAG_HIDE_NAVBAR_WINDOW && mDeviceProfile.isPhone) {
- return getResources().getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+ Resources resources = getResources();
+ return isThreeButtonNav() ?
+ resources.getDimensionPixelSize(R.dimen.taskbar_size) :
+ resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
}
return mDeviceProfile.taskbarSize + Math.max(getLeftCornerRadius(), getRightCornerRadius());
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 77ef83c..ec9760c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -18,6 +18,7 @@
import android.content.res.Resources;
import android.graphics.Rect;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.AnimatedFloat;
@@ -173,7 +174,15 @@
* Returns how tall the background should be drawn at the bottom of the screen.
*/
public int getTaskbarBackgroundHeight() {
- return mActivity.getDeviceProfile().taskbarSize;
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ if (TaskbarManager.isPhoneMode(deviceProfile)) {
+ Resources resources = mActivity.getResources();
+ return mActivity.isThreeButtonNav() ?
+ resources.getDimensionPixelSize(R.dimen.taskbar_size) :
+ resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+ } else {
+ return deviceProfile.taskbarSize;
+ }
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 85e76b2..0bda3cd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -32,6 +32,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.Utilities;
@@ -48,6 +49,7 @@
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.StringJoiner;
+import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -92,6 +94,15 @@
// We skip any view synchronizations during init/destroy.
private boolean mCanSyncViews;
+ private final Consumer<Float> mIconAlphaForHomeConsumer = alpha -> {
+ mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1);
+ mLauncher.getHotseat().setQsbAlpha(
+ mLauncher.getDeviceProfile().isQsbInline && alpha > 0 ? 0 : 1);
+ };
+
+ private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
+ dp -> mIconAlphaForHomeConsumer.accept(mIconAlphaForHome.getValue());
+
private final StateManager.StateListener<LauncherState> mStateListener =
new StateManager.StateListener<LauncherState>() {
@@ -131,13 +142,7 @@
.getTaskbarBackgroundAlpha();
MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
- mIconAlphaForHome.setConsumer(
- alpha -> {
- mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1);
- if (mLauncher.getDeviceProfile().isQsbInline) {
- mLauncher.getHotseat().setQsbAlpha(alpha > 0 ? 0 : 1);
- }
- });
+ mIconAlphaForHome.setConsumer(mIconAlphaForHomeConsumer);
mIconAlignmentForResumedState.finishAnimation();
onIconAlignmentRatioChangedForAppAndHomeTransition();
@@ -150,6 +155,7 @@
applyState(0);
mCanSyncViews = true;
+ mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
}
public void onDestroy() {
@@ -164,6 +170,7 @@
mLauncher.getStateManager().removeStateListener(mStateListener);
mCanSyncViews = true;
+ mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
}
public Animator createAnimToLauncher(@NonNull LauncherState toState,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 353bf89..1212c61 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -82,6 +82,7 @@
// It's destruction/creation will be managed by the activity.
private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider =
new NonDestroyableScopedUnfoldTransitionProgressProvider();
+ private DisplayController.NavigationMode mNavMode;
private TaskbarActivityContext mTaskbarActivityContext;
private StatefulActivity mActivity;
@@ -132,9 +133,11 @@
| ActivityInfo.CONFIG_SCREEN_SIZE;
boolean requiresRecreate = (configDiff & configsRequiringRecreate) != 0;
if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0
- && mTaskbarActivityContext != null && dp != null) {
+ && mTaskbarActivityContext != null && dp != null
+ && !isPhoneMode(dp)) {
// Additional check since this callback gets fired multiple times w/o
// screen size changing, or when simply rotating the device.
+ // In the case of phone device rotation, we do want to call recreateTaskbar()
DeviceProfile oldDp = mTaskbarActivityContext.getDeviceProfile();
boolean isOrientationChange =
(configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0;
@@ -152,7 +155,7 @@
// Config change might be handled without re-creating the taskbar
if (mTaskbarActivityContext != null) {
if (dp != null && isTaskbarPresent(dp)) {
- mTaskbarActivityContext.updateDeviceProfile(dp);
+ mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode);
}
mTaskbarActivityContext.onConfigurationChanged(configDiff);
}
@@ -167,9 +170,11 @@
destroyExistingTaskbar());
mDispInfoChangeListener = (context, info, flags) -> {
if ((flags & CHANGE_FLAGS) != 0) {
+ mNavMode = info.navigationMode;
recreateTaskbar();
}
};
+ mNavMode = mDisplayController.getInfo().navigationMode;
mDisplayController.addChangeListener(mDispInfoChangeListener);
SettingsCache.INSTANCE.get(mContext).register(USER_SETUP_COMPLETE_URI,
mUserSetupCompleteListener);
@@ -289,7 +294,7 @@
mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp, mNavButtonController,
mUnfoldProgressProvider);
} else {
- mTaskbarActivityContext.updateDeviceProfile(dp);
+ mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode);
}
mTaskbarActivityContext.init(mSharedState);
@@ -324,6 +329,14 @@
return TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW && deviceProfile.isPhone;
}
+ /**
+ * @return {@code true} if {@link #isPhoneMode(DeviceProfile)} is true and we're using
+ * 3 button-nav
+ */
+ public static boolean isPhoneButtonNavMode(TaskbarActivityContext context) {
+ return isPhoneMode(context.getDeviceProfile()) && context.isThreeButtonNav();
+ }
+
private boolean isTaskbarPresent(DeviceProfile deviceProfile) {
return FLAG_HIDE_NAVBAR_WINDOW || deviceProfile.isTaskbarPresent;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 114ab4e..3ea9173 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
-import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
import static com.android.launcher3.taskbar.Utilities.appendFlag;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
@@ -180,7 +179,7 @@
mActivity = activity;
mPrefs = Utilities.getPrefs(mActivity);
mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity);
- if (isPhoneMode(mActivity.getDeviceProfile())) {
+ if (isPhoneMode()) {
// DeviceProfile's taskbar vars aren't initialized w/ the flag off
Resources resources = mActivity.getResources();
mUnstashedHeight = resources.getDimensionPixelSize(R.dimen.taskbar_size);
@@ -217,7 +216,8 @@
updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup);
updateStateForFlag(FLAG_IN_SETUP, isInSetup);
- updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode(mActivity.getDeviceProfile()));
+ updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode()
+ && !mActivity.isThreeButtonNav());
applyState();
notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp());
@@ -229,7 +229,7 @@
*/
public boolean supportsVisualStashing() {
return mControllers.uiController.supportsVisualStashing() ||
- isPhoneMode(mActivity.getDeviceProfile());
+ (isPhoneMode() && !mActivity.isThreeButtonNav());
}
/**
@@ -286,6 +286,13 @@
return (hasAnyFlag(FLAG_IN_STASHED_LAUNCHER_STATE) && supportsVisualStashing());
}
+ /**
+ * @return {@code true} if we're not on a large screen AND using gesture nav
+ */
+ private boolean isPhoneMode() {
+ return TaskbarManager.isPhoneMode(mActivity.getDeviceProfile());
+ }
+
private boolean hasAnyFlag(int flagMask) {
return hasAnyFlag(mState, flagMask);
}
@@ -312,7 +319,7 @@
* @see WindowInsets.Type#systemBars()
*/
public int getContentHeightToReportToApps() {
- if (isPhoneMode(mActivity.getDeviceProfile())) {
+ if (isPhoneMode() && !mActivity.isThreeButtonNav()) {
return getStashedHeight();
}
@@ -431,7 +438,7 @@
}
mAnimator = new AnimatorSet();
addJankMonitorListener(mAnimator, /* appearing= */ !mIsStashed);
- final float stashTranslation = isPhoneMode(mActivity.getDeviceProfile()) ? 0 :
+ final float stashTranslation = isPhoneMode() ? 0 :
(mUnstashedHeight - mStashedHeight) / 2f;
if (!supportsVisualStashing()) {
@@ -477,7 +484,7 @@
firstHalfAnimatorSet.playTogether(
mIconAlphaForStash.animateToValue(0),
- mIconScaleForStash.animateToValue(isPhoneMode(mActivity.getDeviceProfile()) ?
+ mIconScaleForStash.animateToValue(isPhoneMode() ?
0 : STASHED_TASKBAR_SCALE)
);
secondHalfAnimatorSet.playTogether(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 929dc20..992aa4b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -71,7 +71,8 @@
public static final int ALPHA_INDEX_NOTIFICATION_EXPANDED = 4;
public static final int ALPHA_INDEX_ASSISTANT_INVOKED = 5;
public static final int ALPHA_INDEX_IME_BUTTON_NAV = 6;
- private static final int NUM_ALPHA_CHANNELS = 7;
+ public static final int ALPHA_INDEX_SMALL_SCREEN = 7;
+ private static final int NUM_ALPHA_CHANNELS = 8;
private final TaskbarActivityContext mActivity;
private final TaskbarView mTaskbarView;
@@ -390,7 +391,8 @@
"ALPHA_INDEX_RECENTS_DISABLED",
"ALPHA_INDEX_NOTIFICATION_EXPANDED",
"ALPHA_INDEX_ASSISTANT_INVOKED",
- "ALPHA_INDEX_IME_BUTTON_NAV");
+ "ALPHA_INDEX_IME_BUTTON_NAV",
+ "ALPHA_INDEX_SMALL_SCREEN");
mModelCallbacks.dumpLogs(prefix + "\t", pw);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 872e64a..cb1da38 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -188,6 +188,9 @@
AllAppsSwipeController.applyAllAppsToNormalConfig(mActivity, config);
} else if (fromState == NORMAL && toState == ALL_APPS) {
AllAppsSwipeController.applyNormalToAllAppsAnimConfig(mActivity, config);
+ } else if (fromState == OVERVIEW && toState == OVERVIEW_SPLIT_SELECT) {
+ config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE,
+ clampToProgress(LINEAR, 0, 0.167f));
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
index e79d56b..cb08ac8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -16,6 +16,8 @@
package com.android.launcher3.uioverrides.states;
+import android.content.Context;
+
import com.android.launcher3.Launcher;
import com.android.quickstep.views.RecentsView;
@@ -24,6 +26,8 @@
* pinned and user is selecting the second one
*/
public class SplitScreenSelectState extends OverviewState {
+ private static final int OVERVIEW_SPLIT_SELECT_SLIDE_IN_DURATION = 500;
+
public SplitScreenSelectState(int id) {
super(id);
}
@@ -38,4 +42,9 @@
RecentsView recentsView = launcher.getOverviewPanel();
return recentsView.getSplitSelectTranslation();
}
+
+ @Override
+ public int getTransitionDuration(Context context, boolean isToState) {
+ return OVERVIEW_SPLIT_SELECT_SLIDE_IN_DURATION;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 8f1872b..8dee10a 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1330,7 +1330,8 @@
if (windowAnimation == null) {
continue;
}
- windowAnimation.start(mContext, velocityPxPerMs);
+ DeviceProfile dp = mActivity == null ? null : mActivity.getDeviceProfile();
+ windowAnimation.start(mContext, dp, velocityPxPerMs);
mRunningWindowAnim[i] = RunningWindowAnim.wrap(windowAnimation);
}
homeAnimFactory.setSwipeVelocity(velocityPxPerMs.y);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 4435eda..3e3a431 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -15,9 +15,6 @@
*/
package com.android.quickstep;
-import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
-
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
@@ -110,8 +107,6 @@
private @Nullable TaskbarManager mTaskbarManager;
private @Nullable FallbackTaskbarUIController mTaskbarUIController;
- private Configuration mOldConfig;
-
private StateManager<RecentsState> mStateManager;
// Strong refs to runners which are cleared when the activity is destroyed
@@ -163,7 +158,7 @@
@Override
public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
- onHandleConfigChanged();
+ onHandleConfigurationChanged();
super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
}
@@ -173,11 +168,8 @@
ACTIVITY_TRACKER.handleNewIntent(this);
}
- /**
- * Logic for when device configuration changes (rotation, screen size change, multi-window,
- * etc.)
- */
- protected void onHandleConfigChanged() {
+ @Override
+ protected void onHandleConfigurationChanged() {
initDeviceProfile();
AbstractFloatingView.closeOpenViews(this, true,
@@ -338,7 +330,6 @@
mStateManager = new StateManager<>(this, RecentsState.BG_LAUNCHER);
- mOldConfig = new Configuration(getResources().getConfiguration());
initDeviceProfile();
setupViews();
@@ -348,16 +339,6 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- int diff = newConfig.diff(mOldConfig);
- if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
- onHandleConfigChanged();
- }
- mOldConfig.setTo(newConfig);
- super.onConfigurationChanged(newConfig);
- }
-
- @Override
public void onStateSetEnd(RecentsState state) {
super.onStateSetEnd(state);
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index fa30b8f..3bd72fe 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -389,10 +389,39 @@
* device is considered in multiWindowMode and things like insets and stuff change
* and calculations have to be adjusted in the animations for that
*/
- public static void composeRecentsSplitLaunchAnimator(int initialTaskId,
- @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
+ public static void composeRecentsSplitLaunchAnimator(GroupedTaskView launchingTaskView,
+ @NonNull StateManager stateManager, @Nullable DepthController depthController,
+ int initialTaskId, @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
@NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t,
@NonNull Runnable finishCallback) {
+ if (launchingTaskView != null) {
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishCallback.run();
+ }
+ });
+
+ final RemoteAnimationTargetCompat[] appTargets =
+ RemoteAnimationTargetCompat.wrapApps(transitionInfo, t, null /* leashMap */);
+ final RemoteAnimationTargetCompat[] wallpaperTargets =
+ RemoteAnimationTargetCompat.wrapNonApps(
+ transitionInfo, true /* wallpapers */, t, null /* leashMap */);
+ final RemoteAnimationTargetCompat[] nonAppTargets =
+ RemoteAnimationTargetCompat.wrapNonApps(
+ transitionInfo, false /* wallpapers */, t, null /* leashMap */);
+ final RecentsView recentsView = launchingTaskView.getRecentsView();
+ composeRecentsLaunchAnimator(animatorSet, launchingTaskView,
+ appTargets, wallpaperTargets, nonAppTargets,
+ true, stateManager,
+ recentsView, depthController);
+
+ t.apply();
+ animatorSet.start();
+ return;
+ }
+
// TODO: consider initialTaskPendingIntent
TransitionInfo.Change splitRoot1 = null;
TransitionInfo.Change splitRoot2 = null;
@@ -657,6 +686,7 @@
public void onAnimationStart(Animator animation) {
if (shown) {
for (SurfaceControl leash : auxiliarySurfaces) {
+ t.setLayer(leash, Integer.MAX_VALUE);
t.setAlpha(leash, 0);
t.show(leash);
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 52a37c5..c46926e 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -270,6 +270,18 @@
MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn);
}
+ @BinderThread
+ @Override
+ public void onScreenTurningOn() {
+ MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOn);
+ }
+
+ @BinderThread
+ @Override
+ public void onScreenTurningOff() {
+ MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOff);
+ }
+
/**
* Preloads the Overview activity.
*
diff --git a/quickstep/src/com/android/quickstep/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java
index 1fef544..1bb95b9 100644
--- a/quickstep/src/com/android/quickstep/ViewUtils.java
+++ b/quickstep/src/com/android/quickstep/ViewUtils.java
@@ -17,6 +17,7 @@
import android.graphics.HardwareRenderer;
import android.os.Handler;
+import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
@@ -45,23 +46,43 @@
return new FrameHandler(view, onFinishRunnable, canceled).schedule();
}
- private static class FrameHandler implements HardwareRenderer.FrameDrawingCallback {
+ private static class FrameHandler implements HardwareRenderer.FrameDrawingCallback,
+ ViewRootImpl.SurfaceChangedCallback {
final ViewRootImpl mViewRoot;
final Runnable mFinishCallback;
final BooleanSupplier mCancelled;
final Handler mHandler;
+ boolean mFinished;
int mDeferFrameCount = 1;
FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) {
mViewRoot = view.getViewRootImpl();
+ mViewRoot.addSurfaceChangedCallback(this);
mFinishCallback = finishCallback;
mCancelled = cancelled;
mHandler = new Handler();
}
@Override
+ public void surfaceCreated(SurfaceControl.Transaction t) {
+ // Do nothing
+ }
+
+ @Override
+ public void surfaceReplaced(SurfaceControl.Transaction t) {
+ // Do nothing
+ }
+
+ @Override
+ public void surfaceDestroyed() {
+ // If the root view is detached, then the app won't get any scheduled frames so we need
+ // to force-run any pending callbacks
+ finish();
+ }
+
+ @Override
public void onFrameDraw(long frame) {
Utilities.postAsyncCallback(mHandler, this::onFrame);
}
@@ -77,9 +98,7 @@
return;
}
- if (mFinishCallback != null) {
- mFinishCallback.run();
- }
+ finish();
}
private boolean schedule() {
@@ -90,5 +109,17 @@
}
return false;
}
+
+ private void finish() {
+ if (mFinished) {
+ return;
+ }
+ mFinished = true;
+ mDeferFrameCount = 0;
+ if (mFinishCallback != null) {
+ mFinishCallback.run();
+ }
+ mViewRoot.removeSurfaceChangedCallback(this);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index b70c411..fa7bc04 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -358,7 +358,7 @@
};
RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift,
homeAnimFactory)[0];
- windowAnim.start(mContext, velocityPxPerMs);
+ windowAnim.start(mContext, mDp, velocityPxPerMs);
return windowAnim;
}
}
diff --git a/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java b/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
index 3777c65..8f79ccf 100644
--- a/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
+++ b/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
@@ -39,6 +39,16 @@
mListeners.forEach(ScreenListener::onScreenTurnedOn);
}
+ /** Called when the screen is starting to turn on. */
+ public void onScreenTurningOn() {
+ mListeners.forEach(ScreenListener::onScreenTurningOn);
+ }
+
+ /** Called when the screen is starting to turn off. */
+ public void onScreenTurningOff() {
+ mListeners.forEach(ScreenListener::onScreenTurningOff);
+ }
+
@Override
public void addCallback(@NonNull ScreenListener listener) {
mListeners.add(listener);
diff --git a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
index c4909de..68739ba 100644
--- a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -214,7 +214,7 @@
* @param context The activity context.
* @param velocityPxPerMs Velocity of swipe in px/ms.
*/
- public void start(Context context, PointF velocityPxPerMs) {
+ public void start(Context context, @Nullable DeviceProfile profile, PointF velocityPxPerMs) {
// Only tell caller that we ended if both x and y animations have ended.
OnAnimationEndListener onXEndListener = ((animation, canceled, centerX, velocityX) -> {
mRectXAnimEnded = true;
@@ -252,7 +252,13 @@
float minVisibleChange = Math.abs(1f / mStartRect.height());
ResourceProvider rp = DynamicResource.provider(context);
float damping = rp.getFloat(R.dimen.swipe_up_rect_scale_damping_ratio);
- float stiffness = rp.getFloat(R.dimen.swipe_up_rect_scale_stiffness);
+
+ // Increase the stiffness for devices where we want the window size to transform quicker.
+ boolean shouldUseHigherStiffness = profile != null
+ && (profile.isLandscape || profile.isTablet);
+ float stiffness = shouldUseHigherStiffness
+ ? rp.getFloat(R.dimen.swipe_up_rect_scale_higher_stiffness)
+ : rp.getFloat(R.dimen.swipe_up_rect_scale_stiffness);
mRectScaleAnim = new SpringAnimation(this, RECT_SCALE_PROGRESS)
.setSpring(new SpringForce(1f)
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index d2a2f36..0ca5574 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -242,8 +242,9 @@
@Override
public void startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
- TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTaskId,
- mInitialTaskPendingIntent, mSecondTaskId, info, t, () -> {
+ TaskViewUtils.composeRecentsSplitLaunchAnimator(mLaunchingTaskView, mStateManager,
+ mDepthController, mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId,
+ info, t, () -> {
finishCallback.run();
if (mSuccessCallback != null) {
mSuccessCallback.accept(true);
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index 7a66ea0..d93f015 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -2,8 +2,9 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.anim.Interpolators.FASTER_OUT_SLOWER_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -200,10 +201,16 @@
RectF floatingTaskViewBounds = new RectF();
if (fadeWithThumbnail) {
- animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
- 0, 1, ACCEL);
+ // FloatingTaskThumbnailView: thumbnail fades out to transparent
animation.addFloat(mThumbnailView, LauncherAnimUtils.VIEW_ALPHA,
- 1, 0, DEACCEL_3);
+ 1, 0, clampToProgress(LINEAR, 0, 0.267f));
+
+ // SplitPlaceholderView: gray background fades in at the same time, then new icon fades
+ // in
+ animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
+ 0, 1, clampToProgress(LINEAR, 0, 0.267f));
+ animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ICON_ALPHA,
+ 0, 1, clampToProgress(LINEAR, 0.333f, 0.5f));
} else if (isStagedTask) {
// Fade in the placeholder view when split is initiated from homescreen / all apps
// icons.
@@ -214,12 +221,15 @@
}
MultiValueUpdateListener listener = new MultiValueUpdateListener() {
- final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration, LINEAR);
- final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration, LINEAR);
+ // SplitPlaceholderView: rectangle translates and stretches to new position
+ final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration,
+ clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f));
+ final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration,
+ clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f));
final FloatProp mTaskViewScaleX = new FloatProp(1f, prop.finalTaskViewScaleX, 0,
- animDuration, LINEAR);
+ animDuration, clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f));
final FloatProp mTaskViewScaleY = new FloatProp(1f, prop.finalTaskViewScaleY, 0,
- animDuration, LINEAR);
+ animDuration, clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f));
@Override
public void onUpdate(float percent, boolean initOnly) {
// Calculate the icon position.
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 64068ad..a153f26 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -35,6 +35,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -113,6 +114,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.core.graphics.ColorUtils;
+import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener;
@@ -125,6 +127,7 @@
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
@@ -442,6 +445,9 @@
private static final float ANIMATION_DISMISS_PROGRESS_MIDPOINT = 0.5f;
private static final float END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.75f;
+ private static final float INITIAL_SPRING_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.133f;
+ private static final float ADDITIONAL_SPRING_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.033f;
+
private static final float SIGNIFICANT_MOVE_SCREEN_WIDTH_PERCENTAGE = 0.15f;
protected final RecentsOrientedState mOrientationState;
@@ -2825,7 +2831,20 @@
RectF startingTaskRect = new RectF();
if (mSplitHiddenTaskView != null) {
- mSplitHiddenTaskView.setVisibility(INVISIBLE);
+ // Split staging is initiated, hide the original TaskView except for the icon (which
+ // needs to animate out over time)
+ // If needed, visibility should be toggled back on in resetFromSplitSelectionState().
+ for (int i = 0; i < mSplitHiddenTaskView.getChildCount(); i++) {
+ View child = mSplitHiddenTaskView.getChildAt(i);
+ if (child != mSplitHiddenTaskView.mIconView) {
+ child.setVisibility(INVISIBLE);
+ }
+ }
+
+ // Icon animates out over time
+ anim.addFloat(mSplitHiddenTaskView, TaskView.ICON_ALPHA, 1, 0,
+ clampToProgress(LINEAR, 0, 0.167f));
+
mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
mSplitHiddenTaskView.getThumbnail(),
mSplitHiddenTaskView.getThumbnail().getThumbnail(),
@@ -2842,9 +2861,15 @@
false /* fadeWithThumbnail */, true /* isStagedTask */);
}
+ // SplitInstructionsView: animate in
mSplitInstructionsView = SplitInstructionsView.getSplitInstructionsView(mActivity);
mSplitInstructionsView.setAlpha(0);
- anim.addFloat(mSplitInstructionsView, SplitInstructionsView.ALPHA_FLOAT, 0, 1, ACCEL);
+ anim.addFloat(mSplitInstructionsView, SplitInstructionsView.CONTAINER_ALPHA, 0, 1,
+ clampToProgress(LINEAR, 0, 0.167f));
+ anim.addFloat(mSplitInstructionsView, SplitInstructionsView.TEXT_ALPHA, 0, 1,
+ clampToProgress(LINEAR, 0.1f, 0.267f));
+ anim.addFloat(mSplitInstructionsView, mSplitInstructionsView.UNFOLD, 0.1f, 1,
+ clampToProgress(EMPHASIZED_DECELERATE, 0, 0.667f));
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "First tile selected");
@@ -3124,11 +3149,22 @@
// Animate task with index >= dismissed index and in the same row as the
// dismissed index or next focused index. Offset successive task dismissal
// durations for a staggered effect.
- float animationStartProgress = Utilities.boundToRange(
- INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- + ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- * ++distanceFromDismissedTask, 0f,
- dismissTranslationInterpolationEnd);
+ distanceFromDismissedTask++;
+ // If user is initiating splitscreen from the focused (large) task, we use a
+ // spring-based animation and timings. For other, smaller, repositions, we currently
+ // fall back on a less complicated linear animation and timings.
+ float animationStartProgress = isFocusedTaskDismissed && nextFocusedTaskView == null
+ ? Utilities.boundToRange(
+ INITIAL_SPRING_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ + ADDITIONAL_SPRING_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ * (int) Math.ceil(distanceFromDismissedTask / 2f), 0f,
+ dismissTranslationInterpolationEnd)
+ : Utilities.boundToRange(
+ INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ + ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ * distanceFromDismissedTask, 0f,
+ dismissTranslationInterpolationEnd);
+
if (taskView == nextFocusedTaskView) {
// Enlarge the task to be focused next, and translate into focus position.
float scale = mTaskWidth / (float) mLastComputedGridTaskSize.width();
@@ -3163,12 +3199,36 @@
primaryTranslation +=
mIsRtl ? -mSplitPlaceholderSize : mSplitPlaceholderSize;
}
- }
- anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
- mIsRtl ? primaryTranslation : -primaryTranslation,
- clampToProgress(LINEAR, animationStartProgress,
- dismissTranslationInterpolationEnd));
+ // Transitioning to split select -- set up staggered spring animation for
+ // other TaskViews.
+ Animator taskSlideIn = new SpringAnimationBuilder(taskView.mActivity)
+ .setDampingRatio(0.85f)
+ .setStiffness(SpringForce.STIFFNESS_LOW)
+ .setEndValue(mIsRtl ? primaryTranslation : -primaryTranslation)
+ .setStartValue(
+ taskView.getPrimaryDismissTranslationProperty()
+ .get(taskView)
+ )
+ .build(taskView, taskView.getPrimaryDismissTranslationProperty());
+ long taskSlideInDuration = taskSlideIn.getDuration();
+ anim.add(taskSlideIn);
+ taskSlideIn
+ .setDuration(taskSlideInDuration)
+ .setStartDelay(
+ Math.round(animationStartProgress * anim.getDuration()));
+ } else {
+ // Task was dismissed individually -- translate other TaskViews to fill the
+ // vacant space.
+
+ // TODO (b/242075836): This dismiss animation uses a linear transition.
+ // When the above bug is fixed, it can use the same (nicer) spring
+ // transition as the focused task split case above.
+ anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
+ mIsRtl ? primaryTranslation : -primaryTranslation,
+ clampToProgress(LINEAR, animationStartProgress,
+ dismissTranslationInterpolationEnd));
+ }
}
}
}
@@ -3189,7 +3249,8 @@
final boolean finalCloseGapBetweenClearAll = closeGapBetweenClearAll;
final boolean finalSnapToLastTask = snapToLastTask;
final boolean finalIsFocusedTaskDismissed = isFocusedTaskDismissed;
- mPendingAnimation.addEndListener(new Consumer<Boolean>() {
+
+ Consumer endConsumer = new Consumer<Boolean>() {
@Override
public void accept(Boolean success) {
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
@@ -3399,7 +3460,9 @@
onDismissAnimationEnds();
mPendingAnimation = null;
}
- });
+ };
+
+ mPendingAnimation.addListener(AnimatorListeners.forEndCallback(endConsumer));
return anim;
}
@@ -4220,7 +4283,6 @@
resetTaskVisuals();
mSplitHiddenTaskViewIndex = -1;
if (mSplitHiddenTaskView != null) {
- mSplitHiddenTaskView.setVisibility(VISIBLE);
mSplitHiddenTaskView = null;
}
}
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 7d94505..d0d715f 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -25,6 +25,7 @@
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
@@ -40,9 +41,10 @@
*/
public class SplitInstructionsView extends FrameLayout {
private final StatefulActivity mLauncher;
+ private AppCompatTextView mTextView;
- public static final FloatProperty<SplitInstructionsView> ALPHA_FLOAT =
- new FloatProperty<SplitInstructionsView>("SplitInstructionsAlpha") {
+ public static final FloatProperty<SplitInstructionsView> CONTAINER_ALPHA =
+ new FloatProperty<SplitInstructionsView>("SplitInstructionsContainerAlpha") {
@Override
public void setValue(SplitInstructionsView splitInstructionsView, float v) {
splitInstructionsView.setVisibility(v != 0 ? VISIBLE : GONE);
@@ -55,6 +57,32 @@
}
};
+ public static final FloatProperty<SplitInstructionsView> UNFOLD =
+ new FloatProperty<SplitInstructionsView>("SplitInstructionsUnfold") {
+ @Override
+ public void setValue(SplitInstructionsView splitInstructionsView, float v) {
+ splitInstructionsView.setScaleY(v);
+ }
+
+ @Override
+ public Float get(SplitInstructionsView splitInstructionsView) {
+ return splitInstructionsView.getScaleY();
+ }
+ };
+
+ public static final FloatProperty<SplitInstructionsView> TEXT_ALPHA =
+ new FloatProperty<SplitInstructionsView>("SplitInstructionsTextAlpha") {
+ @Override
+ public void setValue(SplitInstructionsView splitInstructionsView, float v) {
+ splitInstructionsView.mTextView.setAlpha(v);
+ }
+
+ @Override
+ public Float get(SplitInstructionsView splitInstructionsView) {
+ return splitInstructionsView.mTextView.getAlpha();
+ }
+ };
+
public SplitInstructionsView(Context context) {
this(context, null);
}
@@ -77,6 +105,9 @@
false
);
+ splitInstructionsView.mTextView = splitInstructionsView.findViewById(
+ R.id.split_instructions_text);
+
dragLayer.addView(splitInstructionsView);
return splitInstructionsView;
}
diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
index 28080d4..ae6aae1 100644
--- a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
@@ -47,6 +47,19 @@
}
};
+ public static final FloatProperty<SplitPlaceholderView> ICON_ALPHA =
+ new FloatProperty<SplitPlaceholderView>("SplitViewIconAlpha") {
+ @Override
+ public void setValue(SplitPlaceholderView splitPlaceholderView, float v) {
+ splitPlaceholderView.mIconView.setAlpha(v);
+ }
+
+ @Override
+ public Float get(SplitPlaceholderView splitPlaceholderView) {
+ return splitPlaceholderView.mIconView.getAlpha();
+ }
+ };
+
@Nullable
private IconView mIconView;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index b089155..d2c2988 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -323,6 +323,19 @@
}
};
+ public static final FloatProperty<TaskView> ICON_ALPHA =
+ new FloatProperty<TaskView>("iconAlpha") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.mIconView.setAlpha(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mIconView.getAlpha();
+ }
+ };
+
@Nullable
protected Task mTask;
protected TaskThumbnailView mSnapshotView;
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 4bd627a..159b65f 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -181,6 +181,21 @@
@Test
@PortraitLandscape
+ public void testSplitFromOverview() {
+ assumeTrue(!mLauncher.isTablet());
+
+ startTestActivity(2);
+ startTestActivity(3);
+
+ mLauncher.goHome().switchToOverview().getCurrentTask()
+ .tapMenu()
+ .tapSplitMenuItem()
+ .getTestActivityTask(2)
+ .open();
+ }
+
+ @Test
+ @PortraitLandscape
public void testSplitFromOverviewForTablet() {
assumeTrue(mLauncher.isTablet());
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 28d094b..df1ca14 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -281,6 +281,8 @@
<!-- defaults to iconImageSize, if not specified -->
<attr name="allAppsIconSize" format="float" />
<!-- defaults to allAppsIconSize, if not specified -->
+ <attr name="allAppsIconSizeLandscape" format="float" />
+ <!-- defaults to allAppsIconSize, if not specified -->
<attr name="allAppsIconSizeTwoPanelPortrait" format="float" />
<!-- defaults to allAppsIconSize, if not specified -->
<attr name="allAppsIconSizeTwoPanelLandscape" format="float" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 3f94c34..1415ed0 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -156,6 +156,7 @@
<item name="swipe_up_rect_scale_damping_ratio" type="dimen" format="float">0.75</item>
<item name="swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
+ <item name="swipe_up_rect_scale_higher_stiffness" type="dimen" format="float">400</item>
<item name="swipe_up_rect_xy_fling_friction" type="dimen" format="float">1.5</item>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 596b15e..20352a3 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -405,7 +405,7 @@
// Add a bit of space between nav bar and hotseat in vertical bar layout.
hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
updateHotseatSizes(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
- if (areNavButtonsInline) {
+ if (areNavButtonsInline && !isPhone) {
/*
* 3 nav buttons +
* Spacing between nav buttons +
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 21eba00..cf2a3f8 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -1004,7 +1004,9 @@
allAppsIconSizes[INDEX_DEFAULT] = a.getFloat(
R.styleable.ProfileDisplayOption_allAppsIconSize, iconSizes[INDEX_DEFAULT]);
- allAppsIconSizes[INDEX_LANDSCAPE] = allAppsIconSizes[INDEX_DEFAULT];
+ allAppsIconSizes[INDEX_LANDSCAPE] = a.getFloat(
+ R.styleable.ProfileDisplayOption_allAppsIconSizeLandscape,
+ iconSizes[INDEX_DEFAULT]);
allAppsIconSizes[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
R.styleable.ProfileDisplayOption_allAppsIconSizeTwoPanelPortrait,
allAppsIconSizes[INDEX_DEFAULT]);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5ee9aa8..761f198 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,8 +18,6 @@
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
-import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
@@ -310,8 +308,6 @@
private static final FloatProperty<Hotseat> HOTSEAT_WIDGET_SCALE =
HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION);
- private Configuration mOldConfig;
-
@Thunk
Workspace<?> mWorkspace;
@Thunk
@@ -466,7 +462,6 @@
super.onCreate(savedInstanceState);
LauncherAppState app = LauncherAppState.getInstance(this);
- mOldConfig = new Configuration(getResources().getConfiguration());
mModel = app.getModel();
mRotationHelper = new RotationHelper(this);
@@ -614,17 +609,6 @@
dispatchDeviceProfileChanged();
}
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- int diff = newConfig.diff(mOldConfig);
- if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
- onIdpChanged(false);
- }
-
- mOldConfig.setTo(newConfig);
- super.onConfigurationChanged(newConfig);
- }
-
/**
* Initializes the drag controller.
*/
@@ -634,6 +618,11 @@
@Override
public void onIdpChanged(boolean modelPropertiesChanged) {
+ onHandleConfigurationChanged();
+ }
+
+ @Override
+ protected void onHandleConfigurationChanged() {
if (!initDeviceProfile(mDeviceProfile.inv)) {
return;
}
@@ -1505,7 +1494,7 @@
root.getViewTreeObserver().removeOnDrawListener(mViewCapture);
}
mViewCapture = new ViewCapture(root);
- root.getViewTreeObserver().addOnDrawListener(mViewCapture);
+ mViewCapture.attach();
}
}
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index a5c5c02..1592154 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -55,6 +55,8 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ mActivity.handleConfigurationChanged(mActivity.getResources().getConfiguration());
+
insets = WindowManagerProxy.INSTANCE.get(getContext())
.normalizeWindowInsets(getContext(), insets, mTempRect);
handleSystemWindowInsets(mTempRect);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 0eaad28..b716912 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2124,7 +2124,8 @@
final ItemInfo info = (ItemInfo) cell.getTag();
boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
|| info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
- if (isWidget) {
+ if (isWidget && dropTargetLayout != null) {
+ // animate widget to a valid place
int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
ANIMATE_INTO_POSITION_AND_DISAPPEAR;
animateWidgetDrop(info, parent, d.dragView, null, animationType, cell, false);
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 0a77aa7..464b3ed 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -50,6 +50,8 @@
public static final Interpolator ACCEL_DEACCEL = new AccelerateDecelerateInterpolator();
public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+ public static final Interpolator FASTER_OUT_SLOWER_IN =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
public static final Interpolator AGGRESSIVE_EASE = new PathInterpolator(0.2f, 0f, 0f, 1f);
public static final Interpolator AGGRESSIVE_EASE_IN_OUT = new PathInterpolator(0.6f,0, 0.4f, 1);
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 4fd13b2..f5683d1 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -265,6 +265,11 @@
public static final BooleanFlag ENABLE_ONE_SEARCH_MOTION = new DeviceFlag(
"ENABLE_ONE_SEARCH_MOTION", true, "Enables animations in OneSearch.");
+ public static final BooleanFlag ENABLE_KEYBOARD_TRANSITION_SYNC = new DeviceFlag(
+ "ENABLE_KEYBOARD_TRANSITION_SYNC", false,
+ "Enable option to synchronize the keyboard open and close animations when transitioning"
+ + " between home and all apps");
+
public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = new DeviceFlag(
"ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", true,
"Enable option to show keyboard when going to all-apps");
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 2158dea..2a890c3 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -15,9 +15,14 @@
*/
package com.android.launcher3.statemanager;
+import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
+import android.content.res.Configuration;
+import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
@@ -30,6 +35,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.launcher3.views.BaseDragLayer;
import java.util.List;
@@ -47,6 +53,17 @@
private LauncherRootView mRootView;
+ protected Configuration mOldConfig;
+ private int mOldRotation;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mOldConfig = new Configuration(getResources().getConfiguration());
+ mOldRotation = WindowManagerProxy.INSTANCE.get(this).getRotation(this);
+ }
+
/**
* Create handlers to control the property changes for this activity
*/
@@ -186,4 +203,32 @@
public void runOnBindToTouchInteractionService(Runnable r) {
r.run();
}
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ handleConfigurationChanged(newConfig);
+ super.onConfigurationChanged(newConfig);
+ }
+
+ /**
+ * Handles configuration change when system calls {@link #onConfigurationChanged}, or on other
+ * situations that configuration might change.
+ */
+ public void handleConfigurationChanged(Configuration newConfig) {
+ int diff = newConfig.diff(mOldConfig);
+ int rotation = WindowManagerProxy.INSTANCE.get(this).getRotation(this);
+ if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0
+ || rotation != mOldRotation) {
+ onHandleConfigurationChanged();
+ }
+
+ mOldConfig.setTo(newConfig);
+ mOldRotation = rotation;
+ }
+
+ /**
+ * Logic for when device configuration changes (rotation, screen size change, multi-window,
+ * etc.)
+ */
+ protected abstract void onHandleConfigurationChanged();
}
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 9ac1c0e..2a0fe3a 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -440,7 +440,7 @@
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
out.setPivotX(0);
- out.setPivotY(0);
+ out.setPivotY(splitInstructionsHeight);
out.setRotation(getDegreesRotated());
int distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_phone_landscape);
@@ -448,8 +448,8 @@
int insetCorrectionX = dp.getInsets().left;
// Center the view in case of unbalanced insets on top or bottom of screen
int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
- out.setTranslationX(splitInstructionsHeight + distanceToEdge - insetCorrectionX);
- out.setTranslationY(((splitInstructionsHeight - splitInstructionsWidth) / 2f)
+ out.setTranslationX(distanceToEdge - insetCorrectionX);
+ out.setTranslationY(((-splitInstructionsHeight - splitInstructionsWidth) / 2f)
+ insetCorrectionY);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
// Setting gravity to LEFT instead of the lint-recommended START because we always want this
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index dd9f642..f89c0e5 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -500,7 +500,7 @@
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
out.setPivotX(0);
- out.setPivotY(0);
+ out.setPivotY(splitInstructionsHeight);
out.setRotation(getDegreesRotated());
int distanceToEdge;
if ((DisplayController.getNavigationMode(out.getContext()) == THREE_BUTTONS)
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 387e980..55bb5e8 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -190,7 +190,7 @@
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
out.setPivotX(0);
- out.setPivotY(0);
+ out.setPivotY(splitInstructionsHeight);
out.setRotation(getDegreesRotated());
int distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_phone_landscape);
@@ -198,9 +198,8 @@
int insetCorrectionX = dp.getInsets().right;
// Center the view in case of unbalanced insets on top or bottom of screen
int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
- out.setTranslationX(splitInstructionsWidth - splitInstructionsHeight - distanceToEdge
- + insetCorrectionX);
- out.setTranslationY(((splitInstructionsHeight + splitInstructionsWidth) / 2f)
+ out.setTranslationX(splitInstructionsWidth - distanceToEdge + insetCorrectionX);
+ out.setTranslationY(((-splitInstructionsHeight + splitInstructionsWidth) / 2f)
+ insetCorrectionY);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
// Setting gravity to RIGHT instead of the lint-recommended END because we always want this
diff --git a/src/com/android/launcher3/util/ViewCapture.java b/src/com/android/launcher3/util/ViewCapture.java
index cf9ea69..cf4e84a 100644
--- a/src/com/android/launcher3/util/ViewCapture.java
+++ b/src/com/android/launcher3/util/ViewCapture.java
@@ -15,10 +15,11 @@
*/
package com.android.launcher3.util;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.content.res.Resources;
import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
+import android.os.Message;
import android.os.Trace;
import android.util.Base64;
import android.util.Base64OutputStream;
@@ -28,6 +29,7 @@
import android.view.ViewTreeObserver.OnDrawListener;
import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
import com.android.launcher3.view.ViewCaptureData.ExportedData;
import com.android.launcher3.view.ViewCaptureData.FrameData;
@@ -36,7 +38,7 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.OutputStream;
-import java.util.concurrent.FutureTask;
+import java.util.concurrent.Future;
/**
* Utility class for capturing view data every frame
@@ -45,49 +47,132 @@
private static final String TAG = "ViewCapture";
+ // Number of frames to keep in memory
private static final int MEMORY_SIZE = 2000;
+ // Initial size of the reference pool. This is at least be 5 * total number of views in
+ // Launcher. This allows the first free frames avoid object allocation during view capture.
+ private static final int INIT_POOL_SIZE = 300;
private final View mRoot;
- private final long[] mFrameTimes = new long[MEMORY_SIZE];
- private final Node[] mNodes = new Node[MEMORY_SIZE];
+ private final Resources mResources;
- private int mFrameIndex = -1;
+ private final Handler mHandler;
+ private final ViewRef mViewRef = new ViewRef();
+
+ private int mFrameIndexBg = -1;
+ private final long[] mFrameTimesBg = new long[MEMORY_SIZE];
+ private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[MEMORY_SIZE];
+
+ // Pool used for capturing view tree on the UI thread.
+ private ViewRef mPool = new ViewRef();
/**
* @param root the root view for the capture data
*/
public ViewCapture(View root) {
mRoot = root;
+ mResources = root.getResources();
+ mHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::captureViewPropertiesBg);
+ }
+
+ /**
+ * Attaches the ViewCapture to the root
+ */
+ public void attach() {
+ mHandler.post(this::initPool);
}
@Override
public void onDraw() {
Trace.beginSection("view_capture");
- long now = SystemClock.elapsedRealtimeNanos();
-
- mFrameIndex++;
- if (mFrameIndex >= MEMORY_SIZE) {
- mFrameIndex = 0;
- }
- mFrameTimes[mFrameIndex] = now;
- mNodes[mFrameIndex] = captureView(mRoot, mNodes[mFrameIndex]);
+ captureViewTree(mRoot, mViewRef);
+ Message m = Message.obtain(mHandler);
+ m.obj = mViewRef.next;
+ mHandler.sendMessage(m);
Trace.endSection();
}
/**
+ * Captures the View property on the background thread, and transfer all the ViewRef objects
+ * back to the pool
+ */
+ @WorkerThread
+ private boolean captureViewPropertiesBg(Message msg) {
+ ViewRef start = (ViewRef) msg.obj;
+ long time = msg.getWhen();
+ if (start == null) {
+ return false;
+ }
+ mFrameIndexBg++;
+ if (mFrameIndexBg >= MEMORY_SIZE) {
+ mFrameIndexBg = 0;
+ }
+ mFrameTimesBg[mFrameIndexBg] = time;
+
+ ViewPropertyRef recycle = mNodesBg[mFrameIndexBg];
+
+ ViewPropertyRef result = null;
+ ViewPropertyRef resultEnd = null;
+
+ ViewRef current = start;
+ ViewRef last = start;
+ while (current != null) {
+ ViewPropertyRef propertyRef = recycle;
+ if (propertyRef == null) {
+ propertyRef = new ViewPropertyRef();
+ } else {
+ recycle = recycle.next;
+ propertyRef.next = null;
+ }
+
+ propertyRef.transfer(current);
+ last = current;
+ current = current.next;
+
+ if (result == null) {
+ result = propertyRef;
+ resultEnd = result;
+ } else {
+ resultEnd.next = propertyRef;
+ resultEnd = propertyRef;
+ }
+ }
+ mNodesBg[mFrameIndexBg] = result;
+ ViewRef end = last;
+ Executors.MAIN_EXECUTOR.execute(() -> addToPool(start, end));
+ return true;
+ }
+
+ @UiThread
+ private void addToPool(ViewRef start, ViewRef end) {
+ end.next = mPool;
+ mPool = start;
+ }
+
+ @WorkerThread
+ private void initPool() {
+ ViewRef start = new ViewRef();
+ ViewRef current = start;
+
+ for (int i = 0; i < INIT_POOL_SIZE; i++) {
+ current.next = new ViewRef();
+ current = current.next;
+ }
+
+ ViewRef end = current;
+ Executors.MAIN_EXECUTOR.execute(() -> {
+ addToPool(start, end);
+ if (mRoot.isAttachedToWindow()) {
+ mRoot.getViewTreeObserver().addOnDrawListener(this);
+ }
+ });
+ }
+
+ /**
* Creates a proto of all the data captured so far.
*/
public void dump(FileDescriptor out) {
- Handler handler = mRoot.getHandler();
- if (handler == null) {
- handler = Executors.MAIN_EXECUTOR.getHandler();
- }
- FutureTask<ExportedData> task = new FutureTask<>(this::dumpToProtoUI);
- if (Looper.myLooper() == handler.getLooper()) {
- task.run();
- } else {
- handler.post(task);
- }
+ Future<ExportedData> task = UI_HELPER_EXECUTOR.submit(this::dumpToProto);
try (OutputStream os = new FileOutputStream(out)) {
ExportedData data = task.get();
Base64OutputStream encodedOS = new Base64OutputStream(os,
@@ -100,70 +185,53 @@
}
}
- @UiThread
- private ExportedData dumpToProtoUI() {
+ @WorkerThread
+ private ExportedData dumpToProto() {
ExportedData.Builder dataBuilder = ExportedData.newBuilder();
- Resources res = mRoot.getResources();
+ Resources res = mResources;
- int size = (mNodes[MEMORY_SIZE - 1] == null) ? mFrameIndex + 1 : MEMORY_SIZE;
+ int size = (mNodesBg[MEMORY_SIZE - 1] == null) ? mFrameIndexBg + 1 : MEMORY_SIZE;
for (int i = size - 1; i >= 0; i--) {
- int index = (MEMORY_SIZE + mFrameIndex - i) % MEMORY_SIZE;
+ int index = (MEMORY_SIZE + mFrameIndexBg - i) % MEMORY_SIZE;
+ ViewNode.Builder nodeBuilder = ViewNode.newBuilder();
+ mNodesBg[index].toProto(res, nodeBuilder);
dataBuilder.addFrameData(FrameData.newBuilder()
- .setNode(mNodes[index].toProto(res))
- .setTimestamp(mFrameTimes[index]));
+ .setNode(nodeBuilder)
+ .setTimestamp(mFrameTimesBg[index]));
}
return dataBuilder.build();
}
- private Node captureView(View view, Node recycle) {
- Node result = recycle == null ? new Node() : recycle;
-
- result.clazz = view.getClass();
- result.hashCode = view.hashCode();
- result.id = view.getId();
- result.left = view.getLeft();
- result.top = view.getTop();
- result.right = view.getRight();
- result.bottom = view.getBottom();
- result.scrollX = view.getScrollX();
- result.scrollY = view.getScrollY();
-
- result.translateX = view.getTranslationX();
- result.translateY = view.getTranslationY();
- result.scaleX = view.getScaleX();
- result.scaleY = view.getScaleY();
- result.alpha = view.getAlpha();
-
- result.visibility = view.getVisibility();
- result.willNotDraw = view.willNotDraw();
-
- if (view instanceof ViewGroup) {
- ViewGroup parent = (ViewGroup) view;
- result.clipChildren = parent.getClipChildren();
- int childCount = parent.getChildCount();
- if (childCount == 0) {
- result.children = null;
- } else {
- result.children = captureView(parent.getChildAt(0), result.children);
- Node lastChild = result.children;
- for (int i = 1; i < childCount; i++) {
- lastChild.sibling = captureView(parent.getChildAt(i), lastChild.sibling);
- lastChild = lastChild.sibling;
- }
- lastChild.sibling = null;
- }
+ private ViewRef captureViewTree(View view, ViewRef start) {
+ ViewRef ref;
+ if (mPool != null) {
+ ref = mPool;
+ mPool = mPool.next;
+ ref.next = null;
} else {
- result.clipChildren = false;
- result.children = null;
+ ref = new ViewRef();
}
- return result;
+ ref.view = view;
+ start.next = ref;
+ if (view instanceof ViewGroup) {
+ ViewRef result = ref;
+ ViewGroup parent = (ViewGroup) view;
+ int childCount = ref.childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ result = captureViewTree(parent.getChildAt(i), result);
+ }
+ return result;
+ } else {
+ ref.childCount = 0;
+ return ref;
+ }
}
- private static class Node {
-
+ private static class ViewPropertyRef {
// We store reference in memory to avoid generating and storing too many strings
public Class clazz;
public int hashCode;
+ public int childCount = 0;
public int id;
public int left, top, right, bottom;
@@ -177,10 +245,41 @@
public boolean willNotDraw;
public boolean clipChildren;
- public Node sibling;
- public Node children;
+ public ViewPropertyRef next;
- public ViewNode toProto(Resources res) {
+ public void transfer(ViewRef viewRef) {
+ childCount = viewRef.childCount;
+
+ View view = viewRef.view;
+ viewRef.view = null;
+
+ clazz = view.getClass();
+ hashCode = view.hashCode();
+ id = view.getId();
+ left = view.getLeft();
+ top = view.getTop();
+ right = view.getRight();
+ bottom = view.getBottom();
+ scrollX = view.getScrollX();
+ scrollY = view.getScrollY();
+
+ translateX = view.getTranslationX();
+ translateY = view.getTranslationY();
+ scaleX = view.getScaleX();
+ scaleY = view.getScaleY();
+ alpha = view.getAlpha();
+
+ visibility = view.getVisibility();
+ willNotDraw = view.willNotDraw();
+ }
+
+ /**
+ * Converts the data to the proto representation and returns the next property ref
+ * at the end of the iteration.
+ * @param res
+ * @return
+ */
+ public ViewPropertyRef toProto(Resources res, ViewNode.Builder outBuilder) {
String resolvedId;
if (id >= 0) {
try {
@@ -191,9 +290,7 @@
} else {
resolvedId = "NO_ID";
}
-
- ViewNode.Builder result = ViewNode.newBuilder()
- .setClassname(clazz.getName() + "@" + hashCode)
+ outBuilder.setClassname(clazz.getName() + "@" + hashCode)
.setId(resolvedId)
.setLeft(left)
.setTop(top)
@@ -207,13 +304,20 @@
.setVisibility(visibility)
.setWillNotDraw(willNotDraw)
.setClipChildren(clipChildren);
- Node child = children;
- while (child != null) {
- result.addChildren(child.toProto(res));
- child = child.sibling;
- }
- return result.build();
- }
+ ViewPropertyRef result = next;
+ for (int i = 0; (i < childCount) && (result != null); i++) {
+ ViewNode.Builder childBuilder = ViewNode.newBuilder();
+ result = result.toProto(res, childBuilder);
+ outBuilder.addChildren(childBuilder);
+ }
+ return result;
+ }
+ }
+
+ private static class ViewRef {
+ public View view;
+ public int childCount = 0;
+ public ViewRef next;
}
}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
index ff03a50..1326389 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
@@ -20,46 +20,65 @@
import java.util.Map;
public class FullReorderCase {
+
+ /** 5x5 Test
+ **/
private static final String START_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "222mm\n"
+ "222mm\n"
+ "ad111\n"
+ "bc111";
-
private static final Point MOVE_TO_5x5 = new Point(0, 4);
-
private static final String END_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "222ad\n"
+ "222bc\n"
+ "mm111\n"
+ "mm111";
-
private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
MOVE_TO_5x5,
END_BOARD_STR_5x5);
+ /** 6x5 Test
+ **/
private static final String START_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
+ "2222mm\n"
+ "2222mm\n"
+ "ad1111\n"
+ "bc1111";
-
private static final Point MOVE_TO_6x5 = new Point(0, 4);
-
private static final String END_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
+ "2222ad\n"
+ "2222bc\n"
+ "mm1111\n"
+ "mm1111";
-
private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
MOVE_TO_6x5,
END_BOARD_STR_6x5);
+ /** 4x4 Test
+ **/
+ private static final String START_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "22mm\n"
+ + "admm\n"
+ + "bc11";
+ private static final Point MOVE_TO_4x4 = new Point(0, 3);
+ private static final String END_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "22ad\n"
+ + "mmbc\n"
+ + "mm11";
+
+ private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,
+ MOVE_TO_4x4,
+ END_BOARD_STR_4x4);
+
public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
- Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5);
+ Map.of(new Point(5, 5), TEST_CASE_5x5,
+ new Point(6, 5), TEST_CASE_6x5,
+ new Point(4, 4), TEST_CASE_4x4);
}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
index 32bf05a..1701390 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
@@ -20,47 +20,64 @@
import java.util.Map;
public class MoveOutReorderCase {
+
+ /** 5x5 Test
+ **/
private static final String START_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "34-m-\n"
+ "35111\n"
+ "32111\n"
+ "32111";
-
private static final Point MOVE_TO_5x5 = new Point(1, 2);
-
private static final String END_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "345--\n"
+ "3m111\n"
+ "32111\n"
+ "32111";
-
private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
MOVE_TO_5x5,
END_BOARD_STR_5x5);
-
+ /** 6x5 Test
+ **/
private static final String START_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
+ "34-m--\n"
+ "351111\n"
+ "321111\n"
+ "321111";
-
private static final Point MOVE_TO_6x5 = new Point(1, 2);
-
private static final String END_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
+ "345---\n"
+ "3m1111\n"
+ "321111\n"
+ "321111";
-
private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
MOVE_TO_6x5,
END_BOARD_STR_6x5);
+ /** 4x4 Test
+ **/
+ private static final String START_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "34-m\n"
+ + "3511\n"
+ + "3211";
+ private static final Point MOVE_TO_4x4 = new Point(1, 2);
+ private static final String END_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "345-\n"
+ + "3m11\n"
+ + "3211";
+ private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,
+ MOVE_TO_4x4,
+ END_BOARD_STR_4x4);
+
public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
- Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5);
+ Map.of(new Point(5, 5), TEST_CASE_5x5,
+ new Point(6, 5), TEST_CASE_6x5,
+ new Point(4, 4), TEST_CASE_4x4);
}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
index b386946..bb8d5e9 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
@@ -17,60 +17,68 @@
import android.graphics.Point;
-import com.android.launcher3.celllayout.CellLayoutBoard;
-
import java.util.Map;
public class PushReorderCase {
+
+ /** 5x5 Test
+ **/
private static final String START_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "222m-\n"
+ "--111\n"
+ "--333\n"
+ "-----";
- private static final CellLayoutBoard START_BOARD_5x5 = CellLayoutBoard.boardFromString(
- START_BOARD_STR_5x5);
-
private static final Point MOVE_TO_5x5 = new Point(2, 1);
-
private static final String END_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "--m--\n"
+ "222--\n"
+ "--111\n"
+ "--333";
- private static final CellLayoutBoard END_BOARD_5x5 = CellLayoutBoard.boardFromString(
+ private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
+ MOVE_TO_5x5,
END_BOARD_STR_5x5);
- private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_5x5,
- MOVE_TO_5x5,
- END_BOARD_5x5);
-
+ /** 6x5 Test
+ **/
private static final String START_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
+ "2222m-\n"
+ "--111-\n"
+ "--333-\n"
+ "------";
- private static final CellLayoutBoard START_BOARD_6x5 = CellLayoutBoard.boardFromString(
- START_BOARD_STR_6x5);
-
private static final Point MOVE_TO_6x5 = new Point(2, 1);
-
private static final String END_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
+ "--m---\n"
+ "2222--\n"
+ "--111-\n"
+ "--333-";
- private static final CellLayoutBoard END_BOARD_6x5 = CellLayoutBoard.boardFromString(
+ private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
+ MOVE_TO_6x5,
END_BOARD_STR_6x5);
- private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_6x5,
- MOVE_TO_6x5,
- END_BOARD_6x5);
+ /** 4x4 Test
+ **/
+ private static final String START_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "222m\n"
+ + "-111\n"
+ + "----";
+ private static final Point MOVE_TO_4x4 = new Point(2, 1);
+ private static final String END_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "--m-\n"
+ + "222-\n"
+ + "-111";
+ private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,
+ MOVE_TO_4x4,
+ END_BOARD_STR_4x4);
public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
- Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5);
+ Map.of(new Point(5, 5), TEST_CASE_5x5,
+ new Point(6, 5), TEST_CASE_6x5,
+ new Point(4, 4), TEST_CASE_4x4);
}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
index 57e1398..30269a0 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
@@ -17,35 +17,47 @@
import android.graphics.Point;
-import com.android.launcher3.celllayout.CellLayoutBoard;
-
import java.util.Map;
public class SimpleReorderCase {
- private static final String START_BOARD_STR = ""
+
+ /** 5x5 Test
+ **/
+ private static final String START_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "--mm-\n"
+ "--mm-\n"
+ "-----\n"
+ "-----";
- private static final CellLayoutBoard START_BOARD_5x5 = CellLayoutBoard.boardFromString(
- START_BOARD_STR);
-
private static final Point MOVE_TO_5x5 = new Point(4, 4);
-
private static final String END_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "-----\n"
+ "-----\n"
+ "---mm\n"
+ "---mm";
- private static final CellLayoutBoard END_BOARD_5x5 = CellLayoutBoard.boardFromString(
+ private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
+ MOVE_TO_5x5,
END_BOARD_STR_5x5);
- private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_5x5,
- MOVE_TO_5x5,
- END_BOARD_5x5);
+ /** 4x4 Test
+ **/
+ private static final String START_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "--mm\n"
+ + "--mm\n"
+ + "----";
+ private static final Point MOVE_TO_4x4 = new Point(3, 3);
+ private static final String END_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "----\n"
+ + "--mm\n"
+ + "--mm";
+ private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,
+ MOVE_TO_4x4,
+ END_BOARD_STR_4x4);
public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
- Map.of(new Point(5, 5), TEST_CASE_5x5);
+ Map.of(new Point(5, 5), TEST_CASE_5x5,
+ new Point(4, 4), TEST_CASE_4x4);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 6a11336..f47f710 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -247,8 +247,16 @@
return mLauncher.hasLauncherObject(mLauncher.getOverviewObjectSelector("clear_all"));
}
+ protected boolean isActionsViewVisible() {
+ OverviewTask task = mLauncher.isTablet() ? getFocusedTaskForTablet() : getCurrentTask();
+ if (task == null) {
+ return false;
+ }
+ return !task.isTaskSplit();
+ }
+
private void verifyActionsViewVisibility() {
- if (!hasTasks()) {
+ if (!hasTasks() || !isActionsViewVisible()) {
return;
}
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
@@ -265,13 +273,11 @@
* Returns if focused task is currently snapped task in tablet grid overview.
*/
private boolean isOverviewSnappedToFocusedTaskForTablet() {
- UiObject2 focusedTask = getFocusedTaskForTablet();
+ OverviewTask focusedTask = getFocusedTaskForTablet();
if (focusedTask == null) {
return false;
}
- return Math.abs(
- focusedTask.getVisibleBounds().exactCenterX() - mLauncher.getExactScreenCenterX())
- < 1;
+ return Math.abs(focusedTask.getExactCenterX() - mLauncher.getExactScreenCenterX()) < 1;
}
/**
@@ -279,7 +285,7 @@
*
* @throws IllegalStateException if not run on a tablet device.
*/
- UiObject2 getFocusedTaskForTablet() {
+ OverviewTask getFocusedTaskForTablet() {
if (!mLauncher.isTablet()) {
throw new IllegalStateException("Must be run on tablet device.");
}
@@ -290,9 +296,9 @@
int focusedTaskHeight = mLauncher.getFocusedTaskHeightForTablet();
for (UiObject2 task : taskViews) {
if (task.getVisibleBounds().height() == focusedTaskHeight) {
- return task;
+ return new OverviewTask(mLauncher, task, this);
}
}
return null;
}
-}
\ No newline at end of file
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index caaeef8..fa7e8e9 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -169,6 +169,7 @@
private static final String WIDGETS_RES_ID = "primary_widgets_list_view";
private static final String CONTEXT_MENU_RES_ID = "popup_container";
private static final String TASKBAR_RES_ID = "taskbar_view";
+ private static final String SPLIT_PLACEHOLDER_RES_ID = "split_placeholder";
public static final int WAIT_TIME_MS = 30000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static final String ANDROID_PACKAGE = "android";
@@ -724,6 +725,7 @@
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+ waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
return waitForLauncherObject(WORKSPACE_RES_ID);
}
@@ -732,6 +734,7 @@
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+ waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
return waitForLauncherObject(WIDGETS_RES_ID);
}
@@ -741,16 +744,26 @@
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+ waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
return waitForLauncherObject(APPS_RES_ID);
}
- case OVERVIEW:
+ case OVERVIEW: {
+ waitUntilLauncherObjectGone(APPS_RES_ID);
+ waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+ waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+ waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+
+ return waitForLauncherObject(OVERVIEW_RES_ID);
+ }
case SPLIT_SCREEN_SELECT: {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+ waitForLauncherObject(SPLIT_PLACEHOLDER_RES_ID);
return waitForLauncherObject(OVERVIEW_RES_ID);
}
case FALLBACK_OVERVIEW: {
@@ -758,6 +771,7 @@
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+ waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
}
@@ -766,6 +780,7 @@
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
if (isTablet() && !isFallbackOverview()) {
waitForLauncherObject(TASKBAR_RES_ID);
@@ -1109,6 +1124,16 @@
}
@Nullable
+ UiObject2 findObjectInContainer(UiObject2 container, String resName) {
+ try {
+ return container.findObject(getLauncherObjectSelector(resName));
+ } catch (StaleObjectException e) {
+ fail("The container disappeared from screen");
+ return null;
+ }
+ }
+
+ @Nullable
UiObject2 findObjectInContainer(UiObject2 container, BySelector selector) {
try {
return container.findObject(selector);
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 72a39df..adc993d 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -20,6 +20,7 @@
import android.graphics.Rect;
+import androidx.annotation.NonNull;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
@@ -67,6 +68,10 @@
return mTask.getVisibleCenter().x;
}
+ float getExactCenterX() {
+ return mTask.getVisibleBounds().exactCenterX();
+ }
+
/**
* Dismisses the task by swiping up.
*/
@@ -159,4 +164,24 @@
}
}
}
+
+ /** Taps the task menu. */
+ @NonNull
+ public OverviewTaskMenu tapMenu() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to tap the task menu")) {
+ mLauncher.clickLauncherObject(
+ mLauncher.waitForObjectInContainer(mTask.getParent(), "icon"));
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "tapped the task menu")) {
+ return new OverviewTaskMenu(mLauncher);
+ }
+ }
+ }
+
+ boolean isTaskSplit() {
+ return mLauncher.findObjectInContainer(mTask.getParent(), "bottomright_snapshot") != null;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
new file mode 100644
index 0000000..8cdc8a0
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
@@ -0,0 +1,52 @@
+/*
+ * 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.tapl;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiObject2;
+
+/** Represents the menu of an overview task. */
+public class OverviewTaskMenu {
+
+ private final LauncherInstrumentation mLauncher;
+ private final UiObject2 mMenu;
+
+ OverviewTaskMenu(LauncherInstrumentation launcher) {
+ mLauncher = launcher;
+
+ mMenu = mLauncher.waitForLauncherObject("menu_option_layout");
+ mLauncher.assertTrue("The overview task menus is not visible",
+ !mMenu.getVisibleBounds().isEmpty());
+ }
+
+ /** Taps the split menu item from the overview task menu. */
+ @NonNull
+ public SplitScreenSelect tapSplitMenuItem() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "tap split menu item")) {
+ mLauncher.clickLauncherObject(
+ mLauncher.findObjectInContainer(mMenu, By.textStartsWith("Split")));
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "tapped split menu item")) {
+ return new SplitScreenSelect(mLauncher);
+ }
+ }
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java b/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java
index 3cf3ed6..9fa74e4 100644
--- a/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java
+++ b/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java
@@ -32,4 +32,10 @@
protected ContainerType getContainerType() {
return ContainerType.SPLIT_SCREEN_SELECT;
}
+
+ @Override
+ protected boolean isActionsViewVisible() {
+ // We don't show overview actions in split select state.
+ return false;
+ }
}