Merge "Removing ENABLE_CACHED_WIDGET since we now have always listenign widget" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 210cfd0..228c34a 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -67,7 +67,7 @@
name: "enable_taskbar_pinning"
namespace: "launcher"
description: "Enables taskbar pinning to allow user to switch between transient and persistent taskbar flavors."
- bug: "270396583"
+ bug: "296231746"
}
flag {
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 232c441..2a1f39f 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -322,7 +322,6 @@
<!-- Taskbar -->
<dimen name="taskbar_size">@*android:dimen/taskbar_frame_height</dimen>
- <dimen name="taskbar_phone_size">@*android:dimen/navigation_bar_frame_height</dimen>
<dimen name="taskbar_ime_size">48dp</dimen>
<dimen name="taskbar_icon_min_touch_size">48dp</dimen>
<!-- Note that this applies to both sides of all icons, so visible space is double this. -->
diff --git a/quickstep/src/com/android/launcher3/HomeTransitionController.java b/quickstep/src/com/android/launcher3/HomeTransitionController.java
index b264115..2b50283 100644
--- a/quickstep/src/com/android/launcher3/HomeTransitionController.java
+++ b/quickstep/src/com/android/launcher3/HomeTransitionController.java
@@ -28,19 +28,16 @@
*/
public class HomeTransitionController {
- private final QuickstepLauncher mLauncher;
+ @Nullable private QuickstepLauncher mLauncher;
@Nullable private IHomeTransitionListener mHomeTransitionListener;
- public HomeTransitionController(QuickstepLauncher launcher) {
+ public void registerHomeTransitionListener(QuickstepLauncher launcher) {
mLauncher = launcher;
- }
-
- public void registerHomeTransitionListener() {
mHomeTransitionListener = new IHomeTransitionListener.Stub() {
@Override
public void onHomeVisibilityChanged(boolean isVisible) {
MAIN_EXECUTOR.execute(() -> {
- if (mLauncher.getTaskbarUIController() != null) {
+ if (mLauncher != null && mLauncher.getTaskbarUIController() != null) {
mLauncher.getTaskbarUIController().onLauncherVisibilityChanged(isVisible);
}
});
@@ -53,5 +50,6 @@
public void unregisterHomeTransitionListener() {
SystemUiProxy.INSTANCE.get(mLauncher).setHomeTransitionListener(null);
mHomeTransitionListener = null;
+ mLauncher = null;
}
}
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index f25b652..2ebbe6f 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1290,7 +1290,7 @@
/**
* Returns view on launcher that corresponds to the closing app in the list of app targets
*/
- private @Nullable View findLauncherView(RemoteAnimationTarget[] appTargets) {
+ public @Nullable View findLauncherView(RemoteAnimationTarget[] appTargets) {
for (RemoteAnimationTarget appTarget : appTargets) {
if (appTarget.mode == MODE_CLOSING) {
View launcherView = findLauncherView(appTarget);
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 1440498..ea1d286 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -23,6 +23,7 @@
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
@@ -31,6 +32,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderRow;
@@ -199,8 +201,12 @@
icon.setOnFocusChangeListener(mFocusHelper);
LayoutParams lp = (LayoutParams) icon.getLayoutParams();
- // Ensure the all apps icon height matches the workspace icons in portrait mode.
- lp.height = mActivityContext.getDeviceProfile().allAppsCellHeightPx;
+ if (Flags.enableFocusOutline()) {
+ lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ } else {
+ // Ensure the all apps icon height matches the workspace icons in portrait mode.
+ lp.height = mActivityContext.getDeviceProfile().allAppsCellHeightPx;
+ }
lp.width = 0;
lp.weight = 1;
addView(icon);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index 3f9b66a..dab9950 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -16,7 +16,6 @@
package com.android.launcher3.taskbar
import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
@@ -197,32 +196,19 @@
mActivityContext.deviceProfile.taskbarIconSize) / 2 + verticalOffsetForPopupView
}
- override fun animateClose() {
- if (!mIsOpen) {
- return
+ override fun onCreateCloseAnimation(anim: AnimatorSet?) {
+ // If taskbar pinning preference changed insert custom close animation for popup menu.
+ if (didPreferenceChange) {
+ mOpenCloseAnimator = getCloseAnimator()
}
- if (mOpenCloseAnimator != null) {
- mOpenCloseAnimator.cancel()
- }
- mIsOpen = false
-
- mOpenCloseAnimator = getCloseAnimator()
-
- mOpenCloseAnimator.addListener(
- object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- mOpenCloseAnimator = null
- if (mDeferContainerRemoval) {
- setVisibility(INVISIBLE)
- } else {
- closeComplete()
- }
- }
- }
- )
onCloseCallback(didPreferenceChange)
onCloseCallback = {}
- mOpenCloseAnimator.start()
+ }
+
+ /** Aligning the view pivot to center for animation. */
+ override fun setPivotForOpenCloseAnimation() {
+ pivotX = measuredWidth / 2f
+ pivotY = measuredHeight.toFloat()
}
private fun getCloseAnimator(): AnimatorSet {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 2f7f6f3..48c83da 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -21,9 +21,7 @@
import static com.android.app.animation.Interpolators.FINAL_FRAME;
import static com.android.app.animation.Interpolators.INSTANT;
import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
-import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW;
import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED;
@@ -59,7 +57,6 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.launcher3.Alarm;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
@@ -949,8 +946,7 @@
return false;
}
// Do not stash if pinned taskbar and hardware keyboard is attached.
- if (mActivity.isHardwareKeyboard() && enableTaskbarPinning()
- && LauncherPrefs.get(mActivity).get(TASKBAR_PINNING)) {
+ if (mActivity.isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)) {
return false;
}
return mIsImeShowing || mIsImeSwitcherShowing;
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
index b516d6f..23e3310 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -65,18 +65,19 @@
fun getParamsToCenterView(): FrameLayout.LayoutParams {
val params = FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
params.gravity = Gravity.CENTER
return params;
}
- open fun repositionContextualContainer(contextualContainer: ViewGroup, barAxisMargin: Int,
+ open fun repositionContextualContainer(contextualContainer: ViewGroup, buttonSize: Int,
+ barAxisMarginStart: Int, barAxisMarginEnd: Int,
gravity: Int) {
val contextualContainerParams = FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ buttonSize, ViewGroup.LayoutParams.MATCH_PARENT)
contextualContainerParams.apply {
- marginStart = barAxisMargin
- marginEnd = barAxisMargin
+ marginStart = barAxisMarginStart
+ marginEnd = barAxisMarginEnd
topMargin = 0
bottomMargin = 0
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
index 65b77ac..f31af09 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
@@ -21,6 +21,7 @@
import android.graphics.drawable.PaintDrawable
import android.view.Gravity
import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
@@ -103,8 +104,9 @@
val contextualMargin = resources.getDimensionPixelSize(
R.dimen.taskbar_contextual_button_padding)
- repositionContextualContainer(endContextualContainer, 0, Gravity.END)
- repositionContextualContainer(startContextualContainer, contextualMargin, Gravity.START)
+ repositionContextualContainer(endContextualContainer, WRAP_CONTENT, 0, 0, Gravity.END)
+ repositionContextualContainer(startContextualContainer, WRAP_CONTENT, contextualMargin,
+ contextualMargin, Gravity.START)
if (imeSwitcher != null) {
startContextualContainer.addView(imeSwitcher)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
index 7583cc1..b1b50d6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
@@ -22,10 +22,8 @@
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
-import androidx.core.view.children
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.launcher3.util.DimensionUtils
import com.android.systemui.shared.rotation.RotationButton
open class PhoneLandscapeNavLayoutter(
@@ -48,48 +46,67 @@
) {
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
- // TODO(b/230395757): Polish pending, this is just to make it usable
- val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
- val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(context.deviceProfile,
- resources, context.isPhoneMode)
- navButtonContainer.removeAllViews()
- navButtonContainer.orientation = LinearLayout.VERTICAL
+ val totalHeight = context.deviceProfile.heightPx
+ val homeButtonHeight = resources.getDimensionPixelSize(
+ R.dimen.taskbar_phone_home_button_size)
+ val roundedCornerContentMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_phone_rounded_corner_content_margin)
+ val contentPadding = resources.getDimensionPixelSize(R.dimen.taskbar_phone_content_padding)
+ val contentWidth = totalHeight - roundedCornerContentMargin * 2 - contentPadding * 2
+
+ // left:back:space(reserved for home):overview:right = 0.25:0.5:1:0.5:0.25
+ val contextualButtonHeight = contentWidth / (0.25f + 0.5f + 1f + 0.5f + 0.25f) * 0.25f
+ val sideButtonHeight = contextualButtonHeight * 2
+ val navButtonContainerHeight = contentWidth - contextualButtonHeight * 2
val navContainerParams = FrameLayout.LayoutParams(
- taskbarDimensions.x, ViewGroup.LayoutParams.MATCH_PARENT)
+ ViewGroup.LayoutParams.MATCH_PARENT, navButtonContainerHeight.toInt())
navContainerParams.apply {
- topMargin = endStartMargins
- bottomMargin = endStartMargins
+ topMargin =
+ (contextualButtonHeight + contentPadding + roundedCornerContentMargin).toInt()
+ bottomMargin =
+ (contextualButtonHeight + contentPadding + roundedCornerContentMargin).toInt()
marginEnd = 0
marginStart = 0
}
- navButtonContainer.layoutParams = navContainerParams
- navButtonContainer.gravity = Gravity.CENTER
+ // Ensure order of buttons is correct
+ navButtonContainer.removeAllViews()
+ navButtonContainer.orientation = LinearLayout.VERTICAL
addThreeButtons()
+ navButtonContainer.layoutParams = navContainerParams
+ navButtonContainer.gravity = Gravity.CENTER
+
// Add the spaces in between the nav buttons
- val spaceInBetween: Int =
- resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
- navButtonContainer.children.forEachIndexed { i, navButton ->
+ val spaceInBetween = (navButtonContainerHeight - homeButtonHeight -
+ sideButtonHeight * 2) / 2.0f
+ for (i in 0 until navButtonContainer.childCount) {
+ val navButton = navButtonContainer.getChildAt(i)
val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
- buttonLayoutParams.weight = 1f
+ val margin = (spaceInBetween / 2).toInt()
when (i) {
0 -> {
- buttonLayoutParams.bottomMargin = spaceInBetween / 2
+ // First button
+ buttonLayoutParams.bottomMargin = margin
+ buttonLayoutParams.height = sideButtonHeight.toInt()
}
navButtonContainer.childCount - 1 -> {
- buttonLayoutParams.topMargin = spaceInBetween / 2
+ // Last button
+ buttonLayoutParams.topMargin = margin
+ buttonLayoutParams.height = sideButtonHeight.toInt()
}
else -> {
- buttonLayoutParams.bottomMargin = spaceInBetween / 2
- buttonLayoutParams.topMargin = spaceInBetween / 2
+ // other buttons
+ buttonLayoutParams.topMargin = margin
+ buttonLayoutParams.bottomMargin = margin
+ buttonLayoutParams.height = homeButtonHeight
}
}
}
- repositionContextualButtons()
+ repositionContextualButtons(contextualButtonHeight.toInt())
}
open fun addThreeButtons() {
@@ -99,13 +116,15 @@
navButtonContainer.addView(backButton)
}
- open fun repositionContextualButtons() {
+ open fun repositionContextualButtons(buttonSize: Int) {
endContextualContainer.removeAllViews()
startContextualContainer.removeAllViews()
- val contextualMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_contextual_button_padding)
- repositionContextualContainer(startContextualContainer, contextualMargin, Gravity.TOP)
+ val roundedCornerContentMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_phone_rounded_corner_content_margin)
+ val contentPadding = resources.getDimensionPixelSize(R.dimen.taskbar_phone_content_padding)
+ repositionContextualContainer(startContextualContainer, buttonSize,
+ roundedCornerContentMargin + contentPadding, 0, Gravity.TOP)
if (imeSwitcher != null) {
startContextualContainer.addView(imeSwitcher)
@@ -120,15 +139,16 @@
}
}
- override fun repositionContextualContainer(contextualContainer: ViewGroup, barAxisMargin: Int,
+ override fun repositionContextualContainer(contextualContainer: ViewGroup, buttonSize: Int,
+ barAxisMarginTop: Int, barAxisMarginBottom: Int,
gravity: Int) {
val contextualContainerParams = FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+ ViewGroup.LayoutParams.MATCH_PARENT, buttonSize)
contextualContainerParams.apply {
marginStart = 0
marginEnd = 0
- topMargin = barAxisMargin
- bottomMargin = barAxisMargin
+ topMargin = barAxisMarginTop
+ bottomMargin = barAxisMarginBottom
}
contextualContainerParams.gravity = gravity or Gravity.CENTER_HORIZONTAL
contextualContainer.layoutParams = contextualContainerParams
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
index 4388ce6..05183b8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -24,7 +24,6 @@
import android.widget.LinearLayout
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.launcher3.util.DimensionUtils
import com.android.systemui.shared.rotation.RotationButton
class PhonePortraitNavLayoutter(
@@ -47,26 +46,33 @@
) {
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
- // TODO(b/230395757): Polish pending, this is just to make it usable
- val taskbarDimensions =
- DimensionUtils.getTaskbarPhoneDimensions(context.deviceProfile, resources,
- context.isPhoneMode)
- val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+ val totalWidth = context.deviceProfile.widthPx
+ val homeButtonWidth = resources.getDimensionPixelSize(R.dimen.taskbar_phone_home_button_size)
+ val roundedCornerContentMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_phone_rounded_corner_content_margin)
+ val contentPadding = resources.getDimensionPixelSize(R.dimen.taskbar_phone_content_padding)
+ val contentWidth = totalWidth - roundedCornerContentMargin * 2 - contentPadding * 2
+
+ // left:back:space(reserved for home):overview:right = 0.25:0.5:1:0.5:0.25
+ val contextualButtonWidth = contentWidth / (0.25f + 0.5f + 1f + 0.5f + 0.25f) * 0.25f
+ val sideButtonWidth = contextualButtonWidth * 2
+ val navButtonContainerWidth = contentWidth - contextualButtonWidth * 2
+
+ val navContainerParams = FrameLayout.LayoutParams(navButtonContainerWidth.toInt(),
+ ViewGroup.LayoutParams.MATCH_PARENT)
+ navContainerParams.apply {
+ topMargin = 0
+ bottomMargin = 0
+ marginEnd =
+ (contextualButtonWidth + contentPadding + roundedCornerContentMargin).toInt()
+ marginStart =
+ (contextualButtonWidth + contentPadding + roundedCornerContentMargin).toInt()
+ }
// Ensure order of buttons is correct
navButtonContainer.removeAllViews()
navButtonContainer.orientation = LinearLayout.HORIZONTAL
- val navContainerParams = FrameLayout.LayoutParams(
- taskbarDimensions.x, ViewGroup.LayoutParams.MATCH_PARENT)
- navContainerParams.apply {
- topMargin = 0
- bottomMargin = 0
- marginEnd = endStartMargins
- marginStart = endStartMargins
- }
-
- // Swap recents and back button in case we were landscape prior to this
navButtonContainer.addView(backButton)
navButtonContainer.addView(homeButton)
navButtonContainer.addView(recentsButton)
@@ -75,25 +81,28 @@
navButtonContainer.gravity = Gravity.CENTER
// Add the spaces in between the nav buttons
- val spaceInBetween =
- resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
+ val spaceInBetween = (navButtonContainerWidth - homeButtonWidth -
+ sideButtonWidth * 2) / 2.0f
for (i in 0 until navButtonContainer.childCount) {
val navButton = navButtonContainer.getChildAt(i)
val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
- buttonLayoutParams.weight = 1f
+ val margin = (spaceInBetween / 2).toInt()
when (i) {
0 -> {
// First button
- buttonLayoutParams.marginEnd = spaceInBetween / 2
+ buttonLayoutParams.marginEnd = margin
+ buttonLayoutParams.width = sideButtonWidth.toInt()
}
navButtonContainer.childCount - 1 -> {
// Last button
- buttonLayoutParams.marginStart = spaceInBetween / 2
+ buttonLayoutParams.marginStart = margin
+ buttonLayoutParams.width = sideButtonWidth.toInt()
}
else -> {
// other buttons
- buttonLayoutParams.marginStart = spaceInBetween / 2
- buttonLayoutParams.marginEnd = spaceInBetween / 2
+ buttonLayoutParams.marginStart = margin
+ buttonLayoutParams.marginEnd = margin
+ buttonLayoutParams.width = homeButtonWidth
}
}
}
@@ -101,9 +110,8 @@
endContextualContainer.removeAllViews()
startContextualContainer.removeAllViews()
- val contextualMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_contextual_button_padding)
- repositionContextualContainer(endContextualContainer, contextualMargin, Gravity.END)
+ repositionContextualContainer(endContextualContainer, contextualButtonWidth.toInt(), 0,
+ roundedCornerContentMargin + contentPadding, Gravity.END)
if (imeSwitcher != null) {
endContextualContainer.addView(imeSwitcher)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
index 0368b1d..0f52552 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
@@ -50,13 +50,15 @@
navButtonContainer.addView(recentsButton)
}
- override fun repositionContextualButtons() {
+ override fun repositionContextualButtons(buttonSize: Int) {
endContextualContainer.removeAllViews()
startContextualContainer.removeAllViews()
- val contextualMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_contextual_button_padding)
- repositionContextualContainer(endContextualContainer, contextualMargin, Gravity.BOTTOM)
+ val roundedCornerContentMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_phone_rounded_corner_content_margin)
+ val contentPadding = resources.getDimensionPixelSize(R.dimen.taskbar_phone_content_padding)
+ repositionContextualContainer(endContextualContainer, buttonSize, 0,
+ roundedCornerContentMargin + contentPadding, Gravity.BOTTOM)
if (imeSwitcher != null) {
endContextualContainer.addView(imeSwitcher)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
index 1ac0060..5111bba 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -19,6 +19,7 @@
import android.content.res.Resources
import android.view.Gravity
import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
@@ -61,8 +62,9 @@
val contextualMargin = resources.getDimensionPixelSize(
R.dimen.taskbar_contextual_button_padding)
- repositionContextualContainer(endContextualContainer, 0, Gravity.END)
- repositionContextualContainer(startContextualContainer, contextualMargin, Gravity.START)
+ repositionContextualContainer(endContextualContainer, WRAP_CONTENT, 0, 0, Gravity.END)
+ repositionContextualContainer(startContextualContainer, WRAP_CONTENT, contextualMargin,
+ contextualMargin, Gravity.START)
if (imeSwitcher != null) {
startContextualContainer.addView(imeSwitcher)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
index 5465b6b..45dbebb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -19,6 +19,7 @@
import android.content.res.Resources
import android.view.Gravity
import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
@@ -96,8 +97,9 @@
if (!context.deviceProfile.isGestureMode) {
val contextualMargin = resources.getDimensionPixelSize(
R.dimen.taskbar_contextual_button_padding)
- repositionContextualContainer(endContextualContainer, 0, Gravity.END)
- repositionContextualContainer(startContextualContainer, contextualMargin, Gravity.START)
+ repositionContextualContainer(endContextualContainer, WRAP_CONTENT, 0, 0, Gravity.END)
+ repositionContextualContainer(startContextualContainer, WRAP_CONTENT, contextualMargin,
+ contextualMargin, Gravity.START)
if (imeSwitcher != null) {
startContextualContainer.addView(imeSwitcher)
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index a065387..f45ddb6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -266,8 +266,8 @@
mAppTransitionManager.registerRemoteTransitions();
if (FeatureFlags.enableHomeTransitionListener()) {
- mHomeTransitionController = new HomeTransitionController(this);
- mHomeTransitionController.registerHomeTransitionListener();
+ mHomeTransitionController = new HomeTransitionController();
+ mHomeTransitionController.registerHomeTransitionListener(this);
}
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 1d55da3..856b519 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -62,12 +62,6 @@
@Override
public void onBackPressed(Launcher launcher) {
launcher.getStateManager().goToState(LauncherState.OVERVIEW);
- RecentsView recentsView = launcher.<RecentsView>getOverviewPanel();
- if (recentsView != null) {
- recentsView.resetModalVisuals();
- } else {
- super.onBackPressed(launcher);
- }
}
@Override
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 0f88aac..b9029ea 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -54,12 +54,14 @@
import com.android.internal.view.AppearanceRegion;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.systemui.shared.system.QuickStepContract;
@@ -108,6 +110,7 @@
private final PointF mInitialTouchPos = new PointF();
private RemoteAnimationTarget mBackTarget;
+ private View mLauncherTargetView;
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private boolean mSpringAnimationInProgress = false;
private boolean mAnimatorSetInProgress = false;
@@ -303,11 +306,22 @@
int insetBottom = mStartRect.bottom - appTarget.contentInsets.bottom;
mStartRect.set(mStartRect.left, mStartRect.top, mStartRect.right, insetBottom);
}
+ mLauncherTargetView = mQuickstepTransitionManager.findLauncherView(
+ new RemoteAnimationTarget[]{ mBackTarget });
+ setLauncherTargetViewVisible(false);
mCurrentRect.set(mStartRect);
addScrimLayer();
mTransaction.apply();
}
+ private void setLauncherTargetViewVisible(boolean isVisible) {
+ if (mLauncherTargetView instanceof BubbleTextView) {
+ ((BubbleTextView) mLauncherTargetView).setIconVisible(isVisible);
+ } else if (mLauncherTargetView instanceof LauncherAppWidgetHostView) {
+ mLauncherTargetView.setAlpha(isVisible ? 1f : 0f);
+ }
+ }
+
void addScrimLayer() {
ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
SurfaceControl parent = viewRootImpl != null
@@ -436,6 +450,8 @@
mLauncher.getStateManager().moveToRestState();
}
+ setLauncherTargetViewVisible(true);
+
// Explicitly close opened floating views (which is typically called from
// Launcher#onResumed, but in the predictive back flow launcher is not resumed until
// the transition is fully finished.)
@@ -470,6 +486,8 @@
mInitialTouchPos.set(0, 0);
mAnimatorSetInProgress = false;
mSpringAnimationInProgress = false;
+ setLauncherTargetViewVisible(true);
+ mLauncherTargetView = null;
// We don't call customizeStatusBarAppearance here to prevent the status bar update with
// the legacy appearance. It should be refreshed after the transition done.
mOverridingStatusBarFlags = false;
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 8e03a91..69db91b 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -206,8 +206,9 @@
}
}
- RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.get(mCtx)
- .onStartingSplitLegacy(appearedTaskTargets);
+ RemoteAnimationTarget[] nonAppTargets = ENABLE_SHELL_TRANSITIONS
+ ? null : SystemUiProxy.INSTANCE.get(mCtx).onStartingSplitLegacy(
+ appearedTaskTargets);
if (nonAppTargets == null) {
nonAppTargets = new RemoteAnimationTarget[0];
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 86ba7ef..6f45caf 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -305,6 +305,10 @@
@Override
public void onNavigationBarSurface(SurfaceControl surface) {
// TODO: implement
+ if (surface != null) {
+ surface.release();
+ surface = null;
+ }
}
@BinderThread
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 35fa539..1008da3 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -217,7 +217,6 @@
} else {
if (mActivity.isInState(RecentsState.MODAL_TASK)) {
mActivity.getStateManager().goToState(DEFAULT, animate);
- resetModalVisuals();
}
}
}
@@ -237,6 +236,8 @@
setOverviewFullscreenEnabled(toState.isFullScreen());
if (toState == MODAL_TASK) {
setOverviewSelectEnabled(true);
+ } else {
+ resetModalVisuals();
}
// Set border after select mode changes to avoid showing border during state transition
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 5b5d0de..9bb9775a 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
import static com.android.launcher3.LauncherState.EDIT_MODE;
@@ -144,6 +145,8 @@
setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
if (toState == OVERVIEW_MODAL_TASK) {
setOverviewSelectEnabled(true);
+ } else {
+ resetModalVisuals();
}
// Set border after select mode changes to avoid showing border during state transition
@@ -214,7 +217,6 @@
} else {
if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
mActivity.getStateManager().goToState(LauncherState.OVERVIEW, animate);
- resetModalVisuals();
}
}
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index ebb6ba8..997624f 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -4382,11 +4382,14 @@
private void updatePivots() {
if (mOverviewSelectEnabled) {
- getModalTaskSize(mTempRect);
- Rect selectedTaskPosition = getSelectedTaskBounds();
-
- Utilities.getPivotsForScalingRectToRect(mTempRect, selectedTaskPosition,
- mTempPointF);
+ if (enableGridOnlyOverview()) {
+ getModalTaskSize(mTempRect);
+ Rect selectedTaskPosition = getSelectedTaskBounds();
+ Utilities.getPivotsForScalingRectToRect(mTempRect, selectedTaskPosition,
+ mTempPointF);
+ } else {
+ mTempPointF.set(mLastComputedTaskSize.centerX(), mLastComputedTaskSize.bottom);
+ }
} else {
mTempRect.set(mLastComputedTaskSize);
// Only update pivot when it is tablet and not in grid yet, so the pivot is correct
@@ -5709,11 +5712,20 @@
}
private void updateEnabledOverlays() {
+ TaskView focusedTaskView = getFocusedTaskView();
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
TaskView taskView = requireTaskViewAt(i);
+ if (taskView == focusedTaskView) {
+ continue;
+ }
taskView.setOverlayEnabled(mOverlayEnabled && isTaskViewFullyVisible(taskView));
}
+ // Focus task overlay should be enabled and refreshed at last
+ if (focusedTaskView != null) {
+ focusedTaskView.setOverlayEnabled(
+ mOverlayEnabled && isTaskViewFullyVisible(focusedTaskView));
+ }
}
public void setOverlayEnabled(boolean overlayEnabled) {
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index edf95ea..0a325ac 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -32,6 +32,8 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.rule.ShellCommandRule.disableHeadsUpNotification;
import static com.android.launcher3.util.rule.ShellCommandRule.getLauncherCommand;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -67,7 +69,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
@@ -185,9 +186,10 @@
mLauncher.getLaunchedAppState().switchToOverview();
}
- // b/143488140
+ // Staging; will be promoted to presubmit if stable
+ @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
+
//@NavigationModeSwitch
- @Ignore
@Test
public void goToOverviewFromApp() {
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 25adb62..ebe3365 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -44,6 +44,7 @@
import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.OverviewActions;
import com.android.launcher3.tapl.OverviewTask;
+import com.android.launcher3.tapl.SelectModeButtons;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.Wait;
@@ -194,6 +195,43 @@
actionsView.clickAndDismissScreenshot();
}
+ @Test
+ public void testDismissOverviewWithEscKey() throws Exception {
+ startTestAppsWithCheck();
+ final Overview overview = mLauncher.goHome().switchToOverview();
+ assertTrue("Launcher internal state is not Overview",
+ isInState(() -> LauncherState.OVERVIEW));
+
+ overview.dismissByEscKey();
+ assertTrue("Launcher internal state is not Home",
+ isInState(() -> LauncherState.NORMAL));
+ }
+
+ @Test
+ public void testDismissModalTaskAndOverviewWithEscKey() throws Exception {
+ startTestAppsWithCheck();
+ final Overview overview = mLauncher.goHome().switchToOverview();
+
+ final SelectModeButtons selectModeButtons;
+
+ if (mLauncher.isTablet() && mLauncher.isGridOnlyOverviewEnabled()) {
+ selectModeButtons = overview.getCurrentTask().tapMenu().tapSelectMenuItem();
+ } else {
+ selectModeButtons = overview.getOverviewActions().clickSelect();
+ }
+
+ assertTrue("Launcher internal state is not Overview Modal Task",
+ isInState(() -> LauncherState.OVERVIEW_MODAL_TASK));
+
+ selectModeButtons.dismissByEscKey();
+
+ assertTrue("Launcher internal state is not Overview",
+ isInState(() -> LauncherState.OVERVIEW));
+ overview.dismissByEscKey();
+ assertTrue("Launcher internal state is not Home",
+ isInState(() -> LauncherState.NORMAL));
+ }
+
private int getCurrentOverviewPage(Launcher launcher) {
return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
}
@@ -210,11 +248,12 @@
return launcher.<RecentsView>getOverviewPanel().getBottomRowTaskCountForTablet();
}
- @Ignore
+ // Staging; will be promoted to presubmit if stable
+ @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
+
@Test
@NavigationModeSwitch
@PortraitLandscape
- @ScreenRecord // b/238461765
public void testSwitchToOverview() throws Exception {
startTestAppsWithCheck();
assertNotNull("Workspace.switchToOverview() returned null",
@@ -236,7 +275,9 @@
}
}
- @Ignore
+ // Staging; will be promoted to presubmit if stable
+ @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
+
@Test
@NavigationModeSwitch
@PortraitLandscape
@@ -295,7 +336,9 @@
@Test
@TaskbarModeSwitch
- @Ignore // b/314873201
+ @ScreenRecord // b/314873201
+ // Staging; will be promoted to presubmit if stable
+ @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
public void testQuickSwitchToPreviousAppForTablet() throws Exception {
assumeTrue(mLauncher.isTablet());
startTestActivity(2);
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 0ebcbf3..6d115b2 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -56,7 +56,7 @@
<!-- App Widget resize frame -->
<dimen name="widget_handle_margin">13dp</dimen>
<dimen name="resize_frame_background_padding">24dp</dimen>
- <dimen name="resize_frame_margin">23dp</dimen>
+ <dimen name="resize_frame_margin">22dp</dimen>
<dimen name="resize_frame_invalid_drag_across_two_panel_opacity_margin">24dp</dimen>
<!-- App widget reconfigure button -->
@@ -369,6 +369,11 @@
<!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) -->
<dimen name="taskbar_size">0dp</dimen>
<dimen name="taskbar_phone_size">@*android:dimen/navigation_bar_frame_height</dimen>
+ <dimen name="taskbar_phone_home_button_size">80dp</dimen>
+ <dimen name="taskbar_phone_content_padding">8dp</dimen>
+ <dimen name="taskbar_phone_rounded_corner_content_margin">
+ @*android:dimen/rounded_corner_content_padding
+ </dimen>
<dimen name="taskbar_stashed_size">0dp</dimen>
<dimen name="qsb_widget_height">0dp</dimen>
<dimen name="qsb_shadow_height">0dp</dimen>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 53297f2..72d2213 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -78,7 +78,6 @@
public class DeviceProfile {
private static final int DEFAULT_DOT_SIZE = 100;
- private static final float ALL_APPS_TABLET_MAX_ROWS = 5.5f;
private static final float MIN_FOLDER_TEXT_SIZE_SP = 16f;
private static final float MIN_WIDGET_PADDING_DP = 6f;
@@ -734,14 +733,9 @@
hotseatBorderSpace = cellLayoutBorderSpacePx.y;
}
- // AllApps height calculation depends on updated cellSize
if (isTablet) {
- int collapseHandleHeight =
- res.getDimensionPixelOffset(R.dimen.bottom_sheet_handle_area_height);
- int contentHeight = heightPx - collapseHandleHeight - hotseatQsbHeight;
- int targetContentHeight = (int) (allAppsCellHeightPx * ALL_APPS_TABLET_MAX_ROWS);
- allAppsPadding.top = Math.max(mInsets.top, contentHeight - targetContentHeight);
- allAppsShiftRange = heightPx - allAppsPadding.top;
+ allAppsPadding.top = mInsets.top;
+ allAppsShiftRange = heightPx;
} else {
allAppsPadding.top = 0;
allAppsShiftRange =
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 5721ed3..1cbc5b6 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -247,7 +247,7 @@
public InvariantDeviceProfile(Context context, String gridName) {
String newName = initGrid(context, gridName);
if (newName == null || !newName.equals(gridName)) {
- throw new IllegalArgumentException("Unknown grid name");
+ throw new IllegalArgumentException("Unknown grid name: " + gridName);
}
}
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 7f1d216..4ad4c71 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -1150,14 +1150,15 @@
applyAdapterSideAndBottomPaddings(grid);
- MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
- mlp.leftMargin = insets.left;
- mlp.rightMargin = insets.right;
- setLayoutParams(mlp);
+ // Ignore left/right insets on tablet because we are already centered in-screen.
+ if (grid.isPhone) {
+ MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
+ mlp.leftMargin = insets.left;
+ mlp.rightMargin = insets.right;
+ setLayoutParams(mlp);
+ }
- if (grid.isVerticalBarLayout() && !FeatureFlags.enableResponsiveWorkspace()) {
- setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
- } else {
+ if (!grid.isVerticalBarLayout() || FeatureFlags.enableResponsiveWorkspace()) {
int topPadding = grid.allAppsPadding.top;
if (isSearchBarFloating() && !grid.isTablet) {
topPadding += getResources().getDimensionPixelSize(
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 1ba5f8e..a1f6ebe 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -225,10 +225,10 @@
for (FloatingHeaderRow row : mAllRows) {
row.setup(this, mAllRows, tabsHidden);
}
- updateExpectedHeight();
mTabsHidden = tabsHidden;
maybeSetTabVisibility(VISIBLE);
+ updateExpectedHeight();
mMainRV = mainRV;
mWorkRV = workRV;
mSearchRV = searchRV;
diff --git a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
index 2945979..b2497a3 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
+++ b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
@@ -21,9 +21,14 @@
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.util.AttributeSet
+import android.util.Log
import android.view.Gravity
import android.widget.FrameLayout
import com.android.launcher3.DeviceProfile
+import com.android.launcher3.icons.BitmapInfo
+import com.android.launcher3.icons.PlaceHolderIconDrawable
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.util.Themes
/**
* A FrameLayout marking the area on an [AppPairIcon] where the visual icon will be drawn. One of
@@ -31,6 +36,8 @@
*/
class AppPairIconGraphic @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
FrameLayout(context, attrs) {
+ private val TAG = "AppPairIconGraphic"
+
companion object {
// Design specs -- the below ratios are in relation to the size of a standard app icon.
private const val OUTER_PADDING_SCALE = 1 / 30f
@@ -61,8 +68,8 @@
private lateinit var parentIcon: AppPairIcon
private lateinit var appPairBackground: Drawable
- private lateinit var appIcon1: Drawable
- private lateinit var appIcon2: Drawable
+ private var appIcon1: Drawable? = null
+ private var appIcon2: Drawable? = null
fun init(grid: DeviceProfile, icon: AppPairIcon) {
// Calculate device-specific measurements
@@ -79,12 +86,33 @@
appPairBackground = AppPairIconBackground(context, this)
appPairBackground.setBounds(0, 0, backgroundSize.toInt(), backgroundSize.toInt())
- appIcon1 = parentIcon.info.contents[0].newIcon(context)
- appIcon2 = parentIcon.info.contents[1].newIcon(context)
- appIcon1.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
- appIcon2.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
+ applyIcons(parentIcon.info.contents)
}
+ /** Sets up app pair member icons for drawing. */
+ private fun applyIcons(contents: ArrayList<WorkspaceItemInfo>) {
+ // App pair should always contain 2 members; if not 2, return to avoid a crash loop
+ if (contents.size != 2) {
+ Log.w(TAG, "AppPair contents not 2, size: " + contents.size, Throwable())
+ return
+ }
+
+ // Generate new icons, using themed flag if needed
+ val flags = if (Themes.isThemedIconEnabled(context)) BitmapInfo.FLAG_THEMED else 0
+ val newIcon1 = parentIcon.info.contents[0].newIcon(context, flags)
+ val newIcon2 = parentIcon.info.contents[1].newIcon(context, flags)
+
+ // If app icons did not draw fully last time, animate to full icon
+ (appIcon1 as? PlaceHolderIconDrawable)?.animateIconUpdate(newIcon1)
+ (appIcon2 as? PlaceHolderIconDrawable)?.animateIconUpdate(newIcon2)
+
+ appIcon1 = newIcon1
+ appIcon2 = newIcon2
+ appIcon1?.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
+ appIcon2?.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
+ }
+
+
/** Gets this icon graphic's bounds, with respect to the parent icon's coordinate system. */
fun getIconBounds(outBounds: Rect) {
outBounds.set(0, 0, backgroundSize.toInt(), backgroundSize.toInt())
@@ -110,6 +138,16 @@
// Draw background
appPairBackground.draw(canvas)
+ // Make sure icons are loaded
+ if (
+ appIcon1 == null ||
+ appIcon2 == null ||
+ appIcon1 is PlaceHolderIconDrawable ||
+ appIcon2 is PlaceHolderIconDrawable
+ ) {
+ applyIcons(parentIcon.info.contents)
+ }
+
// Draw first icon
canvas.save()
// The app icons are placed differently depending on device orientation.
@@ -118,7 +156,7 @@
} else {
canvas.translate(width / 2f - memberIconSize / 2f, innerPadding)
}
- appIcon1.draw(canvas)
+ appIcon1?.draw(canvas)
canvas.restore()
// Draw second icon
@@ -135,7 +173,7 @@
height - (innerPadding + memberIconSize)
)
}
- appIcon2.draw(canvas)
+ appIcon2?.draw(canvas)
canvas.restore()
}
}
diff --git a/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt b/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt
index bdac05d..063dad1 100644
--- a/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt
+++ b/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt
@@ -22,6 +22,8 @@
const val RESTORE_ERROR_SHORTCUT_NOT_FOUND = "shortcut_not_found"
const val RESTORE_ERROR_APP_NOT_INSTALLED = "app_not_installed"
const val RESTORE_ERROR_WIDGETS_DISABLED = "widgets_disabled"
+ const val RESTORE_ERROR_PROFILE_NOT_RESTORED = "profile_not_restored"
+ const val RESTORE_ERROR_WIDGET_REMOVED = "widget_not_found"
fun newInstance(context: Context?): LauncherRestoreEventLogger {
return ResourceBasedOverride.Overrides.getObject(
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 8d54abf..2fe3a40 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -305,7 +305,7 @@
// TODO(Block 17): Clean up flags
// Aconfig migration complete for ENABLE_TASKBAR_PINNING.
- private static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag(270396583,
+ private static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag(296231746,
"ENABLE_TASKBAR_PINNING", TEAMFOOD,
"Enables taskbar pinning to allow user to switch between transient and persistent "
+ "taskbar flavors");
@@ -472,10 +472,10 @@
// TODO(Block 33): Clean up flags
public static final BooleanFlag ENABLE_ALL_APPS_RV_PREINFLATION = getDebugFlag(288161355,
- "ENABLE_ALL_APPS_RV_PREINFLATION", TEAMFOOD,
+ "ENABLE_ALL_APPS_RV_PREINFLATION", ENABLED,
"Enables preinflating all apps icons to avoid scrolling jank.");
public static final BooleanFlag ALL_APPS_GONE_VISIBILITY = getDebugFlag(291651514,
- "ALL_APPS_GONE_VISIBILITY", TEAMFOOD,
+ "ALL_APPS_GONE_VISIBILITY", ENABLED,
"Set all apps container view's hidden visibility to GONE instead of INVISIBLE.");
// TODO(Block 34): Empty block
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index e3314d4..4d4a8f7 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -636,10 +636,10 @@
return getResources().getDimensionPixelSize(R.dimen.popup_vertical_padding);
}
- protected AnimatorSet getOpenCloseAnimator(boolean isOpening, int scaleDuration,
- int fadeStartDelay, int fadeDuration, int childFadeStartDelay, int childFadeDuration,
- Interpolator interpolator) {
-
+ /**
+ * Sets X and Y pivots for the view animation considering arrow position.
+ */
+ protected void setPivotForOpenCloseAnimation() {
int arrowCenter = mArrowOffsetHorizontal + mArrowWidth / 2;
if (mIsArrowRotated) {
setPivotX(mIsLeftAligned ? 0f : getMeasuredWidth());
@@ -648,6 +648,14 @@
setPivotX(mIsLeftAligned ? arrowCenter : getMeasuredWidth() - arrowCenter);
setPivotY(mIsAboveIcon ? getMeasuredHeight() : 0f);
}
+ }
+
+
+ protected AnimatorSet getOpenCloseAnimator(boolean isOpening, int scaleDuration,
+ int fadeStartDelay, int fadeDuration, int childFadeStartDelay, int childFadeDuration,
+ Interpolator interpolator) {
+
+ setPivotForOpenCloseAnimation();
float[] alphaValues = isOpening ? new float[] {0, 1} : new float[] {1, 0};
float[] scaleValues = isOpening ? new float[] {0.5f, 1.02f} : new float[] {1f, 0.5f};
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 20b2971..97c99e7 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -18,12 +18,18 @@
import static android.os.Process.myUserHandle;
+import static com.android.launcher3.Flags.enableLauncherBrMetrics;
import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
import static com.android.launcher3.LauncherPrefs.RESTORE_DEVICE;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_PROFILE_NOT_RESTORED;
+import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_WIDGETS_DISABLED;
+import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_WIDGET_REMOVED;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID;
@@ -53,6 +59,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
+import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.DeviceGridState;
import com.android.launcher3.model.LoaderTask;
@@ -91,7 +98,8 @@
public static final String APPWIDGET_OLD_IDS = "appwidget_old_ids";
public static final String APPWIDGET_IDS = "appwidget_ids";
- private static final String[] DB_COLUMNS_TO_LOG = {"profileId", "title", "itemType", "screen",
+ @VisibleForTesting
+ public static final String[] DB_COLUMNS_TO_LOG = {"profileId", "title", "itemType", "screen",
"container", "cellX", "cellY", "spanX", "spanY", "intent", "appWidgetProvider",
"appWidgetId", "restored"};
@@ -123,8 +131,11 @@
FileLog.d(TAG, "performRestore: starting restore from db");
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
RestoreDbTask task = new RestoreDbTask();
- task.sanitizeDB(context, controller, db, new BackupManager(context));
- task.restoreAppWidgetIdsIfExists(context, controller);
+ BackupManager backupManager = new BackupManager(context);
+ LauncherRestoreEventLogger restoreEventLogger =
+ LauncherRestoreEventLogger.Companion.newInstance(context);
+ task.sanitizeDB(context, controller, db, backupManager, restoreEventLogger);
+ task.restoreAppWidgetIdsIfExists(context, controller, restoreEventLogger);
t.commit();
return true;
} catch (Exception e) {
@@ -147,7 +158,8 @@
*/
@VisibleForTesting
protected int sanitizeDB(Context context, ModelDbController controller, SQLiteDatabase db,
- BackupManager backupManager) throws Exception {
+ BackupManager backupManager, LauncherRestoreEventLogger restoreEventLogger)
+ throws Exception {
logFavoritesTable(db, "Old Launcher Database before sanitizing:", null, null);
// Primary user ids
long myProfileId = controller.getSerialNumberForUser(myUserHandle());
@@ -186,6 +198,9 @@
Arrays.fill(args, "?");
final String where = "profileId NOT IN (" + TextUtils.join(", ", Arrays.asList(args)) + ")";
logFavoritesTable(db, "items to delete from unrestored profiles:", where, profileIds);
+ if (enableLauncherBrMetrics()) {
+ reportUnrestoredProfiles(db, where, profileIds, restoreEventLogger);
+ }
int itemsDeletedCount = db.delete(Favorites.TABLE_NAME, where, profileIds);
FileLog.d(TAG, itemsDeletedCount + " total items from unrestored user(s) were deleted");
@@ -345,21 +360,24 @@
DeviceGridState deviceGridState = new DeviceGridState(context);
FileLog.d(TAG, "restore initiated from backup: DeviceGridState=" + deviceGridState);
LauncherPrefs.get(context).putSync(RESTORE_DEVICE.to(deviceGridState.getDeviceType()));
- LauncherPrefs.get(context).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(true));
+ if (enableLauncherBrMetrics()) {
+ LauncherPrefs.get(context).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(true));
+ }
}
@WorkerThread
@VisibleForTesting
- void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller) {
+ void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller,
+ LauncherRestoreEventLogger restoreEventLogger) {
LauncherPrefs lp = LauncherPrefs.get(context);
if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) {
AppWidgetHost host = new AppWidgetHost(context, APPWIDGET_HOST_ID);
- restoreAppWidgetIds(context, controller,
+ restoreAppWidgetIds(context, controller, restoreEventLogger,
IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(),
IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(),
host);
} else {
- FileLog.d(TAG, "No app widget ids were received from backup to restore.");
+ FileLog.d(TAG, "Did not receive new app widget id map during Launcher restore");
}
lp.remove(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS);
@@ -370,10 +388,13 @@
*/
@WorkerThread
private void restoreAppWidgetIds(Context context, ModelDbController controller,
- int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) {
+ LauncherRestoreEventLogger launcherRestoreEventLogger, int[] oldWidgetIds,
+ int[] newWidgetIds, @NonNull AppWidgetHost host) {
if (WidgetsModel.GO_DISABLE_WIDGETS) {
FileLog.e(TAG, "Skipping widget ID remap as widgets not supported");
host.deleteHost();
+ launcherRestoreEventLogger.logFavoritesItemsRestoreFailed(Favorites.ITEM_TYPE_APPWIDGET,
+ oldWidgetIds.length, RESTORE_ERROR_WIDGETS_DISABLED);
return;
}
if (!RestoreDbTask.isPending(context)) {
@@ -437,11 +458,16 @@
FileLog.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: "
+ oldWidgetId);
host.deleteAppWidgetId(newWidgetIds[i]);
+ launcherRestoreEventLogger.logSingleFavoritesItemRestoreFailed(
+ ITEM_TYPE_APPWIDGET,
+ RESTORE_ERROR_WIDGET_REMOVED
+ );
}
}
}
}
+ logFavoritesTable(controller.getDb(), "launcher db after remap widget ids", null, null);
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
if (app != null) {
app.getModel().forceReload();
@@ -476,17 +502,16 @@
StringBuilder builder = new StringBuilder();
builder.append("[");
for (int i = 0; i < widgetIdList.size(); i++) {
- builder.append("[")
+ builder.append("[appWidgetId=")
.append(widgetIdList.get(i))
- .append(", ")
+ .append(", restoreFlag=")
.append(widgetRestoreList.get(i))
- .append(", ")
+ .append(", profileId=")
.append(widgetProfileIdList.get(i))
.append("]");
}
builder.append("]");
- Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: "
- + builder);
+ Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: " + builder);
} catch (Exception ex) {
Log.e(TAG, "Getting widget ids from the database failed", ex);
}
@@ -545,7 +570,7 @@
*/
public static void logFavoritesTable(SQLiteDatabase database, @NonNull String logHeader,
String where, String[] profileIds) {
- try (Cursor itemsToDelete = database.query(
+ try (Cursor cursor = database.query(
/* table */ Favorites.TABLE_NAME,
/* columns */ DB_COLUMNS_TO_LOG,
/* selection */ where,
@@ -554,26 +579,53 @@
/* having */ null,
/* orderBy */ null
)) {
- if (itemsToDelete.moveToFirst()) {
- String[] columnNames = itemsToDelete.getColumnNames();
+ if (cursor.moveToFirst()) {
+ String[] columnNames = cursor.getColumnNames();
StringBuilder stringBuilder = new StringBuilder(logHeader + "\n");
do {
for (String columnName : columnNames) {
stringBuilder.append(columnName)
.append("=")
- .append(itemsToDelete.getString(
- itemsToDelete.getColumnIndex(columnName)))
+ .append(cursor.getString(
+ cursor.getColumnIndex(columnName)))
.append(" ");
}
stringBuilder.append("\n");
- } while (itemsToDelete.moveToNext());
+ } while (cursor.moveToNext());
FileLog.d(TAG, stringBuilder.toString());
} else {
- FileLog.d(TAG, "logFavoritesTable: No items found from query for"
+ FileLog.d(TAG, "logFavoritesTable: No items found from query for "
+ "\"" + logHeader + "\"");
}
} catch (Exception e) {
FileLog.e(TAG, "logFavoritesTable: Error reading from database", e);
}
}
+
+
+ /**
+ * Queries and reports the count of each itemType to be removed due to unrestored profiles.
+ * @param database The Launcher db to query from.
+ * @param where Query being used for to find unrestored profiles
+ * @param profileIds profile ids that were not restored
+ * @param restoreEventLogger Backup/Restore Logger to report metrics
+ */
+ private void reportUnrestoredProfiles(SQLiteDatabase database, String where,
+ String[] profileIds, LauncherRestoreEventLogger restoreEventLogger) {
+ final String query = "SELECT itemType, COUNT(*) AS count FROM favorites WHERE "
+ + where + " GROUP BY itemType";
+ try (Cursor cursor = database.rawQuery(query, profileIds)) {
+ if (cursor.moveToFirst()) {
+ do {
+ restoreEventLogger.logFavoritesItemsRestoreFailed(
+ cursor.getInt(cursor.getColumnIndexOrThrow(ITEM_TYPE)),
+ cursor.getInt(cursor.getColumnIndexOrThrow("count")),
+ RESTORE_ERROR_PROFILE_NOT_RESTORED
+ );
+ } while (cursor.moveToNext());
+ }
+ } catch (Exception e) {
+ FileLog.e(TAG, "reportUnrestoredProfiles: Error reading from database", e);
+ }
+ }
}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 18f583d..1419dc4 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -175,6 +175,13 @@
sTransientTaskbarStatusForTests = enable;
}
+ /**
+ * Returns whether the taskbar is pinned in gesture navigation mode.
+ */
+ public static boolean isPinnedTaskbar(Context context) {
+ return INSTANCE.get(context).getInfo().isPinnedTaskbar();
+ }
+
@Override
public void close() {
mDestroyed = true;
@@ -423,6 +430,12 @@
}
return true;
}
+ /**
+ * Returns whether the taskbar is pinned in gesture navigation mode.
+ */
+ public boolean isPinnedTaskbar() {
+ return navigationMode == NavigationMode.NO_BUTTON && !isTransientTaskbar();
+ }
/**
* Returns {@code true} if the bounds represent a tablet.
diff --git a/src/com/android/launcher3/util/KeyboardShortcutsDelegate.java b/src/com/android/launcher3/util/KeyboardShortcutsDelegate.java
index c9db83d..e4e0bae 100644
--- a/src/com/android/launcher3/util/KeyboardShortcutsDelegate.java
+++ b/src/com/android/launcher3/util/KeyboardShortcutsDelegate.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
import android.util.Log;
@@ -27,6 +28,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
@@ -118,17 +120,22 @@
return true;
} else if (mLauncher.getAppsView().isInAllApps()) {
// Close all apps if there are no open floating views.
- closeAllApps();
+ mLauncher.getStateManager().goToState(NORMAL, true);
+ return true;
+ } else if (mLauncher.isInState(LauncherState.OVERVIEW)
+ || mLauncher.isInState(LauncherState.OVERVIEW_SPLIT_SELECT)) {
+ // Close Overview and return to home.
+ mLauncher.getStateManager().goToState(NORMAL, true);
+ return true;
+ } else if (mLauncher.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
+ // Return to the previous state (Overview) when the modal task is open.
+ mLauncher.getStateManager().goToState(OVERVIEW, true);
return true;
}
}
return null;
}
- private void closeAllApps() {
- mLauncher.getStateManager().goToState(NORMAL, true);
- }
-
/**
* Handle key up event.
* @param keyCode code of the key being pressed.
diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java
index 87e496e..7737adb 100644
--- a/src/com/android/launcher3/views/ClipIconView.java
+++ b/src/com/android/launcher3/views/ClipIconView.java
@@ -112,7 +112,7 @@
float scaleY = rect.height() / minSize;
float scale = Math.max(1f, Math.min(scaleX, scaleY));
- if (Float.isNaN(scale)) {
+ if (Float.isNaN(scale) || Float.isInfinite(scale)) {
// Views are no longer laid out, do not update.
return;
}
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt
index 0f27893..92caf23 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt
@@ -55,7 +55,7 @@
bottomSheetCloseDuration: 500
bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 0.0
- allAppsShiftRange: 1496.0px (748.0dp)
+ allAppsShiftRange: 1600.0px (800.0dp)
allAppsOpenDuration: 500
allAppsCloseDuration: 500
allAppsIconSizePx: 120.0px (60.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt
index 85f7ca1..3815fa9 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt
@@ -55,7 +55,7 @@
bottomSheetCloseDuration: 500
bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 0.0
- allAppsShiftRange: 1496.0px (748.0dp)
+ allAppsShiftRange: 1600.0px (800.0dp)
allAppsOpenDuration: 500
allAppsCloseDuration: 500
allAppsIconSizePx: 120.0px (60.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt
index bd47777..7e0f316 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt
@@ -55,7 +55,7 @@
bottomSheetCloseDuration: 500
bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 0.0
- allAppsShiftRange: 2019.0px (1009.5dp)
+ allAppsShiftRange: 2560.0px (1280.0dp)
allAppsOpenDuration: 500
allAppsCloseDuration: 500
allAppsIconSizePx: 120.0px (60.0dp)
@@ -66,7 +66,7 @@
allAppsBorderSpacePxX: 16.0px (8.0dp)
allAppsBorderSpacePxY: 32.0px (16.0dp)
numShownAllAppsColumns: 6
- allAppsPadding.top: 541.0px (270.5dp)
+ allAppsPadding.top: 104.0px (52.0dp)
allAppsPadding.left: 32.0px (16.0dp)
allAppsPadding.right: 32.0px (16.0dp)
allAppsLeftRightMargin: 152.0px (76.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt
index 902885a..58c3890 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt
@@ -55,7 +55,7 @@
bottomSheetCloseDuration: 500
bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 0.0
- allAppsShiftRange: 2019.0px (1009.5dp)
+ allAppsShiftRange: 2560.0px (1280.0dp)
allAppsOpenDuration: 500
allAppsCloseDuration: 500
allAppsIconSizePx: 120.0px (60.0dp)
@@ -66,7 +66,7 @@
allAppsBorderSpacePxX: 16.0px (8.0dp)
allAppsBorderSpacePxY: 32.0px (16.0dp)
numShownAllAppsColumns: 6
- allAppsPadding.top: 541.0px (270.5dp)
+ allAppsPadding.top: 104.0px (52.0dp)
allAppsPadding.left: 32.0px (16.0dp)
allAppsPadding.right: 32.0px (16.0dp)
allAppsLeftRightMargin: 152.0px (76.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt
index 43e4a60..1e363a2 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt
@@ -55,7 +55,7 @@
bottomSheetCloseDuration: 500
bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 1.0
- allAppsShiftRange: 1730.0px (659.0476dp)
+ allAppsShiftRange: 1840.0px (700.9524dp)
allAppsOpenDuration: 500
allAppsCloseDuration: 500
allAppsIconSizePx: 141.0px (53.714287dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt
index e7ea839..617b54b 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt
@@ -55,7 +55,7 @@
bottomSheetCloseDuration: 500
bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 1.0
- allAppsShiftRange: 1730.0px (659.0476dp)
+ allAppsShiftRange: 1840.0px (700.9524dp)
allAppsOpenDuration: 500
allAppsCloseDuration: 500
allAppsIconSizePx: 141.0px (53.714287dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt
index 043380c..483b5e7 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt
@@ -55,7 +55,7 @@
bottomSheetCloseDuration: 500
bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 1.0
- allAppsShiftRange: 2075.0px (790.4762dp)
+ allAppsShiftRange: 2208.0px (841.1429dp)
allAppsOpenDuration: 500
allAppsCloseDuration: 500
allAppsIconSizePx: 141.0px (53.714287dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt
index a1b3e95..8d0640c 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt
@@ -55,7 +55,7 @@
bottomSheetCloseDuration: 500
bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 1.0
- allAppsShiftRange: 2075.0px (790.4762dp)
+ allAppsShiftRange: 2208.0px (841.1429dp)
allAppsOpenDuration: 500
allAppsCloseDuration: 500
allAppsIconSizePx: 141.0px (53.714287dp)
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 10d9133..733f1e9 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -59,6 +59,7 @@
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LauncherModelHelper;
@@ -90,6 +91,7 @@
private SQLiteDatabase mMockDb;
private Cursor mMockCursor;
private LauncherPrefs mPrefs;
+ private LauncherRestoreEventLogger mMockRestoreEventLogger;
@Before
public void setup() {
@@ -100,6 +102,7 @@
mMockDb = mock(SQLiteDatabase.class);
mMockCursor = mock(Cursor.class);
mPrefs = new LauncherPrefs(mContext);
+ mMockRestoreEventLogger = mock(LauncherRestoreEventLogger.class);
}
@After
@@ -178,7 +181,7 @@
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
- mTask.sanitizeDB(mContext, controller, controller.getDb(), bm);
+ mTask.sanitizeDB(mContext, controller, controller.getDb(), bm, mMockRestoreEventLogger);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -207,7 +210,7 @@
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
- mTask.sanitizeDB(mContext, controller, controller.getDb(), bm);
+ mTask.sanitizeDB(mContext, controller, controller.getDb(), bm, mMockRestoreEventLogger);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -219,7 +222,7 @@
@Test
public void givenLauncherPrefsHasNoIds_whenRestoreAppWidgetIdsIfExists_thenIdsAreRemoved() {
// When
- mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
+ mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
}
@@ -235,7 +238,7 @@
// When
setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
- mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
+ mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds);
@@ -257,7 +260,7 @@
// When
setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
- mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
+ mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds);
@@ -280,12 +283,13 @@
when(mMockDb.query(any(), any(), any(), any(), any(), any(), any()))
.thenReturn(mMockCursor);
when(mMockCursor.moveToFirst()).thenReturn(true);
+ when(mMockCursor.getColumnNames()).thenReturn(new String[] {});
when(mMockCursor.isAfterLast()).thenReturn(true);
RestoreDbTask.setPending(mContext);
// When
setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
- mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
+ mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(expectedHost.getAppWidgetIds()).isEqualTo(allExpectedIds);
diff --git a/tests/src/com/android/launcher3/tapl/TaplUtilityTest.java b/tests/src/com/android/launcher3/tapl/TaplUtilityTest.java
index 4a1888a..b6ded97 100644
--- a/tests/src/com/android/launcher3/tapl/TaplUtilityTest.java
+++ b/tests/src/com/android/launcher3/tapl/TaplUtilityTest.java
@@ -23,16 +23,43 @@
public class TaplUtilityTest {
@Test
- public void testNewStringWithRegex() {
+ public void testMakeMultilinePattern() {
+ // Original title will match.
assertTrue(AppIcon.makeMultilinePattern("Play Store")
- .matcher("Play Store has 7 notifications").matches());
+ .matcher("Play Store").matches());
+ assertTrue(AppIcon.makeMultilinePattern("PlayStore")
+ .matcher("PlayStore").matches());
+
+ // Original title with whitespace added will match.
+ assertTrue(AppIcon.makeMultilinePattern("PlayStore")
+ .matcher("Play\nStore").matches());
+ assertTrue(AppIcon.makeMultilinePattern("PlayStore")
+ .matcher("Play Store").matches());
+ // Original title with whitespace removed will also match.
assertTrue(AppIcon.makeMultilinePattern("Play Store")
- .matcher("Play Store!").matches());
- assertFalse(AppIcon.makeMultilinePattern("Play Store")
- .matcher("play store").matches());
- assertFalse(AppIcon.makeMultilinePattern("Play Store")
- .matcher("").matches());
+ .matcher("PlayStore").matches());
+ // Or whitespace replaced with a different kind of whitespace (both of above conditions).
+ assertTrue(AppIcon.makeMultilinePattern("Play Store")
+ .matcher("Play\nStore").matches());
assertTrue(AppIcon.makeMultilinePattern("Play Store")
.matcher("Play \n Store").matches());
+
+ // Any non-whitespace character added to the title will not match.
+ assertFalse(AppIcon.makeMultilinePattern("Play Store")
+ .matcher("Play Store has 7 notifications").matches());
+ assertFalse(AppIcon.makeMultilinePattern("Play Store")
+ .matcher("Play Store!").matches());
+ // Title is case-sensitive.
+ assertFalse(AppIcon.makeMultilinePattern("Play Store")
+ .matcher("play store").matches());
+ assertFalse(AppIcon.makeMultilinePattern("Play Store")
+ .matcher("play store").matches());
+ // Removing non whitespace characters will not match.
+ assertFalse(AppIcon.makeMultilinePattern("Play Store")
+ .matcher("").matches());
+ assertFalse(AppIcon.makeMultilinePattern("Play Store")
+ .matcher("Play Stor").matches());
+ assertFalse(AppIcon.makeMultilinePattern("Play Store")
+ .matcher("Play").matches());
}
}
diff --git a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
index f818564..cb30854 100644
--- a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
@@ -19,7 +19,10 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
+import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
import static com.android.launcher3.util.TestUtil.installDummyAppForUser;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -39,12 +42,13 @@
import com.android.launcher3.allapps.WorkProfileManager;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.rule.TestStabilityRule;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
+import java.io.IOException;
import java.util.Objects;
import java.util.function.Predicate;
@@ -98,7 +102,17 @@
launcher.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
});
TestUtil.uninstallDummyApp();
- mDevice.executeShellCommand("pm remove-user " + mProfileUserId);
+
+ mLauncher.runToState(
+ () -> {
+ try {
+ mDevice.executeShellCommand("pm remove-user " + mProfileUserId);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ NORMAL_STATE_ORDINAL,
+ "executing pm 'remove-user' command");
}
private void waitForWorkTabSetup() {
@@ -123,8 +137,10 @@
LauncherInstrumentation.WAIT_TIME_MS);
}
+ // Staging; will be promoted to presubmit if stable
+ @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
+
@Test
- @Ignore("b/243855320")
public void toggleWorks() {
assumeTrue(mWorkProfileSetupSuccessful);
waitForWorkTabSetup();
diff --git a/tests/src/com/android/launcher3/util/ExecutorRunnableTest.kt b/tests/src/com/android/launcher3/util/ExecutorRunnableTest.kt
index 6d00253..972b592 100644
--- a/tests/src/com/android/launcher3/util/ExecutorRunnableTest.kt
+++ b/tests/src/com/android/launcher3/util/ExecutorRunnableTest.kt
@@ -63,8 +63,8 @@
fun run_and_complete() {
awaitAllExecutorCompleted()
- assertTrue(isTaskExecuted)
- assertTrue(isCallbackExecuted)
+ assertTrue("task should be executed", isTaskExecuted)
+ assertTrue("callback should be executed", isCallbackExecuted)
assertEquals(2, result)
}
@@ -76,7 +76,7 @@
underTest.cancel(false)
awaitAllExecutorCompleted()
- assertFalse(isCallbackExecuted)
+ assertFalse("callback should not be executed.", isCallbackExecuted)
assertEquals(0, result)
}
@@ -86,8 +86,8 @@
underTest.cancel(false)
- assertTrue(isTaskExecuted)
- assertTrue(isCallbackExecuted)
+ assertTrue("task should be executed", isTaskExecuted)
+ assertTrue("callback should be executed", isCallbackExecuted)
assertEquals(2, result)
}
diff --git a/tests/src/com/android/launcher3/util/ModelTestExtensions.kt b/tests/src/com/android/launcher3/util/ModelTestExtensions.kt
index 61ec669..9d9bd517 100644
--- a/tests/src/com/android/launcher3/util/ModelTestExtensions.kt
+++ b/tests/src/com/android/launcher3/util/ModelTestExtensions.kt
@@ -1,7 +1,27 @@
package com.android.launcher3.util
+import android.content.ContentValues
import com.android.launcher3.LauncherModel
+import com.android.launcher3.LauncherSettings.Favorites
+import com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_ID
+import com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_PROVIDER
+import com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_SOURCE
+import com.android.launcher3.LauncherSettings.Favorites.CELLX
+import com.android.launcher3.LauncherSettings.Favorites.CELLY
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
+import com.android.launcher3.LauncherSettings.Favorites.INTENT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+import com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID
+import com.android.launcher3.LauncherSettings.Favorites.RESTORED
+import com.android.launcher3.LauncherSettings.Favorites.SCREEN
+import com.android.launcher3.LauncherSettings.Favorites.SPANX
+import com.android.launcher3.LauncherSettings.Favorites.SPANY
+import com.android.launcher3.LauncherSettings.Favorites.TITLE
+import com.android.launcher3.LauncherSettings.Favorites._ID
import com.android.launcher3.model.BgDataModel
+import com.android.launcher3.model.ModelDbController
object ModelTestExtensions {
/** Clears and reloads Launcher db to cleanup the workspace */
@@ -20,6 +40,7 @@
loadModelSync()
}
+ /** Loads the model in memory synchronously */
fun LauncherModel.loadModelSync() {
val mockCb: BgDataModel.Callbacks = object : BgDataModel.Callbacks {}
TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) { addCallbacksAndLoad(mockCb) }
@@ -27,4 +48,54 @@
TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) {}
TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) { removeCallbacks(mockCb) }
}
+
+ /** Adds and commits a new item to Launcher.db */
+ fun LauncherModel.addItem(
+ title: String = "LauncherTestApp",
+ intent: String =
+ "#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;component=com.google.android.apps.nexuslauncher.tests/com.android.launcher3.testcomponent.BaseTestingActivity;launchFlags=0x10200000;end",
+ type: Int = ITEM_TYPE_APPLICATION,
+ restoreFlags: Int = 0,
+ screen: Int = 0,
+ container: Int = CONTAINER_DESKTOP,
+ x: Int,
+ y: Int,
+ spanX: Int = 1,
+ spanY: Int = 1,
+ id: Int = 0,
+ profileId: Int = 0,
+ tableName: String = Favorites.TABLE_NAME,
+ appWidgetId: Int = -1,
+ appWidgetSource: Int = -1,
+ appWidgetProvider: String? = null
+ ) {
+ loadModelSync()
+ TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
+ val controller: ModelDbController = modelDbController
+ controller.tryMigrateDB()
+ modelDbController.newTransaction().use { transaction ->
+ val values =
+ ContentValues().apply {
+ values[_ID] = id
+ values[TITLE] = title
+ values[PROFILE_ID] = profileId
+ values[CONTAINER] = container
+ values[SCREEN] = screen
+ values[CELLX] = x
+ values[CELLY] = y
+ values[SPANX] = spanX
+ values[SPANY] = spanY
+ values[ITEM_TYPE] = type
+ values[RESTORED] = restoreFlags
+ values[INTENT] = intent
+ values[APPWIDGET_ID] = appWidgetId
+ values[APPWIDGET_SOURCE] = appWidgetSource
+ values[APPWIDGET_PROVIDER] = appWidgetProvider
+ }
+ // Migrate any previous data so that the DB state is correct
+ controller.insert(tableName, values)
+ transaction.commit()
+ }
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 85098c8..867a1a8 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -36,9 +36,23 @@
super(launcher, icon);
}
+ /**
+ * Find an app icon with the given name.
+ *
+ * @param appName app icon to look for
+ */
+ static BySelector getAppIconSelector(String appName) {
+ return By.clazz(TextView.class).text(makeMultilinePattern(appName));
+ }
+
+ /**
+ * Find an app icon with the given name.
+ *
+ * @param appName app icon to look for
+ * @param launcher (optional) - only match ui elements from Launcher's package
+ */
static BySelector getAppIconSelector(String appName, LauncherInstrumentation launcher) {
- return By.clazz(TextView.class).desc(makeMultilinePattern(appName))
- .pkg(launcher.getLauncherPackageName());
+ return getAppIconSelector(appName).pkg(launcher.getLauncherPackageName());
}
static BySelector getMenuItemSelector(String text, LauncherInstrumentation launcher) {
@@ -109,13 +123,16 @@
}
/**
- * Create a regular expression pattern that matches strings starting with the app name, where
- * spaces in the app name are replaced with zero or more occurrences of the "\s" character
- * (which represents a whitespace character in regular expressions), followed by any characters
- * after the app name.
+ * Create a regular expression pattern that matches strings containing all of the non-whitespace
+ * characters of the app name, with any amount of whitespace added between characters (e.g.
+ * newline for multiline app labels).
*/
static Pattern makeMultilinePattern(String appName) {
- return Pattern.compile(appName.replaceAll("\\s+", "\\\\s*") + ".*",
- Pattern.DOTALL);
+ // Remove any existing whitespace.
+ appName = appName.replaceAll("\\s", "");
+ // Allow whitespace between characters, e.g. newline for 2 line app label.
+ StringBuilder regexBuldier = new StringBuilder("\\s*");
+ appName.chars().forEach(letter -> regexBuldier.append((char) letter).append("\\s*"));
+ return Pattern.compile(regexBuldier.toString());
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index b6b4a47..6c0010d 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -16,7 +16,10 @@
package com.android.launcher3.tapl;
+import static android.view.KeyEvent.KEYCODE_ESCAPE;
+
import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
+import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
import android.graphics.Rect;
@@ -27,15 +30,24 @@
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.shared.TestProtocol;
+
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Common overview panel for both Launcher and fallback recents
*/
public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
+
+ private static final Pattern EVENT_ALT_ESC_DOWN = Pattern.compile(
+ "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
+ private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
+ "Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
+
private static final int FLINGS_FOR_DISMISS_LIMIT = 40;
BaseOverview(LauncherInstrumentation launcher) {
@@ -367,6 +379,23 @@
return !task.isTaskSplit();
}
+ /**
+ * Presses the esc key to dismiss Overview.
+ */
+ public Workspace dismissByEscKey() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
+ mLauncher.runToState(
+ () -> mLauncher.getDevice().pressKeyCode(KEYCODE_ESCAPE),
+ NORMAL_STATE_ORDINAL, "pressing esc key");
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "pressed esc key")) {
+ return mLauncher.getWorkspace();
+ }
+ }
+ }
+
private void verifyActionsViewVisibility() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to assert overview actions view visibility")) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index b68fc4e..28e2590 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -62,7 +62,13 @@
+ mObject.getVisibleCenter() + " in "
+ mLauncher.getVisibleBounds(mObject));
- performClick();
+ if (launcherStopsAfterLaunch()) {
+ mLauncher.executeAndWaitForLauncherStop(
+ () -> mLauncher.clickLauncherObject(mObject),
+ "clicking the launchable");
+ } else {
+ mLauncher.clickLauncherObject(mObject);
+ }
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
expectActivityStartEvents();
@@ -72,16 +78,6 @@
}
}
- private void performClick() {
- if (launcherStopsAfterLaunch()) {
- mLauncher.executeAndWaitForLauncherStop(
- () -> mLauncher.clickLauncherObject(mObject),
- "clicking the launchable");
- } else {
- mLauncher.clickLauncherObject(mObject);
- }
- }
-
protected abstract void expectActivityStartEvents();
protected abstract String launchableType();
@@ -100,7 +96,9 @@
+ mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(
mObject));
- performClick();
+ mLauncher.executeAndWaitForLauncherStop(
+ () -> mLauncher.clickLauncherObject(mObject),
+ "clicking the launchable");
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, OverviewTask.SPLIT_START_EVENT);
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index edc6aac..508f39b 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -21,6 +21,7 @@
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.view.KeyEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_SCROLL;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT;
@@ -277,21 +278,35 @@
assertNotNull("Cannot find content provider for " + testProviderAuthority, pi);
ComponentName cn = new ComponentName(pi.packageName, pi.name);
+ final int iterations = isLauncherTest ? 300 : 100;
+
if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
if (TestHelpers.isInLauncherProcess()) {
pm.setComponentEnabledSetting(cn, COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
} else {
try {
final int userId = getContext().getUserId();
+ final String launcherPidCommand = "pidof " + pi.packageName;
+ final String initialPid = mDevice.executeShellCommand(launcherPidCommand);
+
mDevice.executeShellCommand(
"pm enable --user " + userId + " " + cn.flattenToString());
+
+ // Wait for Launcher restart after enabling test provider.
+ for (int i = 0; i < iterations; ++i) {
+ final String currentPid = mDevice.executeShellCommand(launcherPidCommand)
+ .replaceAll("\\s", "");
+ if (!currentPid.isEmpty() && !currentPid.equals(initialPid)) break;
+ if (i == iterations - 1) {
+ fail("Launcher didn't restart after enabling test provider");
+ }
+ SystemClock.sleep(100);
+ }
} catch (IOException e) {
fail(e.toString());
}
}
- final int iterations = isLauncherTest ? 300 : 100;
-
// Wait for Launcher content provider to become enabled.
for (int i = 0; i < iterations; ++i) {
final ContentProviderClient testProvider = getContext().getContentResolver()
@@ -1529,7 +1544,8 @@
}
}
- void runToState(Runnable command, int expectedState, String actionName) {
+ /** Run an action and wait for the specified Launcher state. */
+ public void runToState(Runnable command, int expectedState, String actionName) {
final List<Integer> actualEvents = new ArrayList<>();
executeAndWaitForLauncherEvent(
command,
@@ -1702,6 +1718,16 @@
"scrolling");
}
+ void pointerScroll(float pointerX, float pointerY, Direction direction) {
+ executeAndWaitForLauncherEvent(
+ () -> injectEvent(getPointerMotionEvent(
+ ACTION_SCROLL, pointerX, pointerY, direction)),
+ event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
+ () -> "Didn't receive a scroll end message: " + direction + " scroll from ("
+ + pointerX + ", " + pointerY + ")",
+ "scrolling");
+ }
+
// Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a
// fixed interval each time.
public void linearGesture(int startX, int startY, int endX, int endY, int steps,
@@ -1765,6 +1791,41 @@
return getContext().getResources();
}
+ private static MotionEvent getPointerMotionEvent(
+ int action, float x, float y, Direction direction) {
+ MotionEvent.PointerCoords[] coordinates = new MotionEvent.PointerCoords[1];
+ coordinates[0] = new MotionEvent.PointerCoords();
+ coordinates[0].x = x;
+ coordinates[0].y = y;
+ boolean isVertical = direction == Direction.UP || direction == Direction.DOWN;
+ boolean isForward = direction == Direction.RIGHT || direction == Direction.DOWN;
+ coordinates[0].setAxisValue(
+ isVertical ? MotionEvent.AXIS_VSCROLL : MotionEvent.AXIS_HSCROLL,
+ isForward ? 1f : -1f);
+
+ MotionEvent.PointerProperties[] properties = new MotionEvent.PointerProperties[1];
+ properties[0] = new MotionEvent.PointerProperties();
+ properties[0].id = 0;
+ properties[0].toolType = MotionEvent.TOOL_TYPE_MOUSE;
+
+ final long downTime = SystemClock.uptimeMillis();
+ return MotionEvent.obtain(
+ downTime,
+ downTime,
+ action,
+ /* pointerCount= */ 1,
+ properties,
+ coordinates,
+ /* metaState= */ 0,
+ /* buttonState= */ 0,
+ /* xPrecision= */ 1f,
+ /* yPrecision= */ 1f,
+ /* deviceId= */ 0,
+ /* edgeFlags= */ 0,
+ InputDevice.SOURCE_CLASS_POINTER,
+ /* flags= */ 0);
+ }
+
private static MotionEvent getTrackpadMotionEvent(long downTime, long eventTime,
int action, float x, float y, int pointerCount, TrackpadGestureType gestureType) {
MotionEvent.PointerProperties[] pointerProperties =
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
index bd2c9c1..d7c40a0 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -89,8 +89,7 @@
private SelectModeButtons getSelectModeButtons() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get select mode buttons")) {
- UiObject2 selectModeButtons = mLauncher.waitForLauncherObject("select_mode_buttons");
- return new SelectModeButtons(selectModeButtons, mLauncher);
+ return new SelectModeButtons(mLauncher);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
index 3d2914d..c6fa31d 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
@@ -16,6 +16,8 @@
package com.android.launcher3.tapl;
+import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL;
+
import androidx.annotation.NonNull;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
@@ -72,6 +74,25 @@
}
}
+ /** Taps the select menu item from the overview task menu. */
+ @NonNull
+ public SelectModeButtons tapSelectMenuItem() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "before tapping the select menu item")) {
+
+ mLauncher.runToState(
+ () -> mLauncher.clickLauncherObject(
+ mLauncher.findObjectInContainer(mMenu, By.text("Select"))),
+ OVERVIEW_MODAL_TASK_STATE_ORDINAL, "tapping select menu item");
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "select menu item opened")) {
+ return new SelectModeButtons(mLauncher);
+ }
+ }
+ }
+
/** Returns true if an item matching the given string is present in the menu. */
public boolean hasMenuItem(String expectedMenuItemText) {
UiObject2 menuItem = mLauncher.findObjectInContainer(mMenu, By.text(expectedMenuItemText));
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index f0a8aa2..963bf79 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -39,7 +39,7 @@
/** Find the app from search results with app name. */
public AppIcon findAppIcon(String appName) {
- UiObject2 icon = mLauncher.waitForLauncherObject(By.clazz(TextView.class).text(appName));
+ UiObject2 icon = mLauncher.waitForLauncherObject(AppIcon.getAppIconSelector(appName));
return createAppIcon(icon);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
index e1b73a4..146a67c 100644
--- a/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
+++ b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
@@ -16,9 +16,15 @@
package com.android.launcher3.tapl;
+import static android.view.KeyEvent.KEYCODE_ESCAPE;
+
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.shared.TestProtocol;
+
+import java.util.regex.Pattern;
+
/**
* View containing select mode buttons
*/
@@ -26,9 +32,14 @@
private final UiObject2 mSelectModeButtons;
private final LauncherInstrumentation mLauncher;
- SelectModeButtons(UiObject2 selectModeButtons,
- LauncherInstrumentation launcherInstrumentation) {
- mSelectModeButtons = selectModeButtons;
+ private static final Pattern EVENT_ALT_ESC_DOWN = Pattern.compile(
+ "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
+ private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
+ "Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
+
+
+ SelectModeButtons(LauncherInstrumentation launcherInstrumentation) {
+ mSelectModeButtons = launcherInstrumentation.waitForLauncherObject("select_mode_buttons");
mLauncher = launcherInstrumentation;
}
@@ -48,4 +59,22 @@
}
}
}
+
+ /**
+ * Close select mode when ESC key is pressed.
+ * @return The Overview
+ */
+ @NonNull
+ public Overview dismissByEscKey() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
+ mLauncher.getDevice().pressKeyCode(KEYCODE_ESCAPE);
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "pressed esc key")) {
+ return new Overview(mLauncher);
+ }
+ }
+ }
+
}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
index 064f80c..d05c112 100644
--- a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
@@ -68,6 +68,7 @@
@Override
protected boolean launcherStopsAfterLaunch() {
+ // false because if taskbar is showing then launcher is already stopped.
return false;
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 75d6ed1..ada0a7f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -708,10 +708,9 @@
*/
public void flingForward() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- final UiObject2 workspace = verifyActiveContainer();
- mLauncher.scroll(workspace, Direction.RIGHT,
- new Rect(0, 0, mLauncher.getEdgeSensitivityWidth() + 1, 0),
- FLING_STEPS, false);
+ Rect workspaceBounds = mLauncher.getVisibleBounds(verifyActiveContainer());
+ mLauncher.pointerScroll(
+ workspaceBounds.centerX(), workspaceBounds.centerY(), Direction.RIGHT);
verifyActiveContainer();
}
}
@@ -722,10 +721,9 @@
*/
public void flingBackward() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- final UiObject2 workspace = verifyActiveContainer();
- mLauncher.scroll(workspace, Direction.LEFT,
- new Rect(mLauncher.getEdgeSensitivityWidth() + 1, 0, 0, 0),
- FLING_STEPS, false);
+ Rect workspaceBounds = mLauncher.getVisibleBounds(verifyActiveContainer());
+ mLauncher.pointerScroll(
+ workspaceBounds.centerX(), workspaceBounds.centerY(), Direction.LEFT);
verifyActiveContainer();
}
}