diff --git a/quickstep/res/drawable/ic_sysbar_back.xml b/quickstep/res/drawable/ic_sysbar_back.xml
index 1eea6774b1..7d2bdee6e0 100644
--- a/quickstep/res/drawable/ic_sysbar_back.xml
+++ b/quickstep/res/drawable/ic_sysbar_back.xml
@@ -16,13 +16,13 @@
-->
+ android:viewportWidth="20"
+ android:viewportHeight="20">
+ android:pathData="M15.5417 1.66669C15.1833 1.66669 14.8417 1.76669 14.5333 1.94169L3.21667 8.74169C2.775 9.00002 2.5 9.48335 2.5 10C2.5 10.5167 2.775 11 3.21667 11.2584L14.5333 18.05C14.8417 18.2334 15.1833 18.325 15.5417 18.325C16.625 18.325 17.5 17.45 17.5 16.3667V3.62502C17.5 2.54169 16.625 1.66669 15.5417 1.66669Z" />
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_home.xml b/quickstep/res/drawable/ic_sysbar_home.xml
index b4b397b59c..987f2f6ae8 100644
--- a/quickstep/res/drawable/ic_sysbar_home.xml
+++ b/quickstep/res/drawable/ic_sysbar_home.xml
@@ -16,12 +16,12 @@
-->
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20">
+ android:pathData="M10.0001 18.3334C5.40008 18.3334 1.66675 14.6 1.66675 10C1.66675 5.40002 5.40008 1.66669 10.0001 1.66669C14.6001 1.66669 18.3334 5.40002 18.3334 10C18.3334 14.6 14.6001 18.3334 10.0001 18.3334Z" />
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_recent.xml b/quickstep/res/drawable/ic_sysbar_recent.xml
index f8c4778a2b..26c4dc813c 100644
--- a/quickstep/res/drawable/ic_sysbar_recent.xml
+++ b/quickstep/res/drawable/ic_sysbar_recent.xml
@@ -16,12 +16,12 @@
-->
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20">
+ android:pathData="M4.47634 2.5H15.5241C16.6164 2.5 17.5002 3.38382 17.5002 4.4761V15.5239C17.5002 16.6162 16.6164 17.5 15.5241 17.5H4.47634C3.38407 17.5 2.50024 16.6162 2.50024 15.5239V4.4761C2.50024 3.38382 3.38407 2.5 4.47634 2.5Z" />
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_edu_tooltip.xml b/quickstep/res/layout/taskbar_edu_tooltip.xml
index 29f49566f6..3fcd713147 100644
--- a/quickstep/res/layout/taskbar_edu_tooltip.xml
+++ b/quickstep/res/layout/taskbar_edu_tooltip.xml
@@ -21,6 +21,8 @@
android:clipChildren="false"
android:clipToPadding="false"
android:fitsSystemWindows="true"
+ android:focusable="true"
+ android:importantForAccessibility="yes"
android:gravity="center"
android:orientation="vertical">
diff --git a/quickstep/res/values-sw720dp/dimens.xml b/quickstep/res/values-sw720dp/dimens.xml
index f093185d14..0832a91981 100644
--- a/quickstep/res/values-sw720dp/dimens.xml
+++ b/quickstep/res/values-sw720dp/dimens.xml
@@ -43,7 +43,7 @@
52dp
- 30dp
+ 30dp
100dp
180dp
300dp
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 599095b0c4..c6e90c4295 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -305,10 +305,12 @@
16dp
- 40dp
+ 40dp
88dp
156dp
264dp
+
+ 24dp
24dp
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
index 6db58397c3..5eec6a47ae 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
@@ -69,6 +69,10 @@ public class StashedHandleView extends View {
*/
public void updateSampledRegion(Rect stashedHandleBounds) {
getLocationOnScreen(mTmpArr);
+ // Translations are temporary due to animations, remove them for the purpose of determining
+ // the final region we want sampled.
+ mTmpArr[0] -= Math.round(getTranslationX());
+ mTmpArr[1] -= Math.round(getTranslationY());
mSampledRegion.set(stashedHandleBounds);
mSampledRegion.offset(mTmpArr[0], mTmpArr[1]);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 79f0a64c4e..924d0c6255 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -30,8 +30,7 @@ import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLA
import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
import static com.android.launcher3.taskbar.TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW;
import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import android.animation.AnimatorSet;
@@ -572,9 +571,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
public void updateSysuiStateFlags(int systemUiStateFlags, boolean fromInit) {
mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags,
fromInit);
- int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
- | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
- onNotificationShadeExpandChanged((systemUiStateFlags & shadeExpandedFlags) != 0, fromInit);
+ boolean isShadeVisible = (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) != 0;
+ onNotificationShadeExpandChanged(isShadeVisible, fromInit);
mControllers.taskbarViewController.setRecentsButtonDisabled(
mControllers.navbarButtonsViewController.isRecentsDisabled()
|| isNavBarKidsModeActive());
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 82b455d24a..f3b3054000 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -22,6 +22,7 @@ import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
import com.android.launcher3.R
+import com.android.launcher3.Utilities
import com.android.launcher3.Utilities.mapRange
import com.android.launcher3.Utilities.mapToRange
import com.android.launcher3.anim.Interpolators
@@ -31,6 +32,9 @@ import com.android.launcher3.util.DisplayController
/** Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. */
class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
+ private val DARK_THEME_SHADOW_ALPHA = 51f
+ private val LIGHT_THEME_SHADOW_ALPHA = 25f
+
val paint = Paint()
val lastDrawnTransientRect = RectF()
var backgroundHeight = context.deviceProfile.taskbarSize.toFloat()
@@ -42,6 +46,7 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
private val isTransientTaskbar = DisplayController.isTransientTaskbar(context)
+ private val shadowAlpha: Float
private var shadowBlur = 0f
private var keyShadowDistance = 0f
private var bottomMargin = 0
@@ -73,6 +78,10 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance)
}
+ shadowAlpha =
+ if (Utilities.isDarkTheme(context)) DARK_THEME_SHADOW_ALPHA
+ else LIGHT_THEME_SHADOW_ALPHA
+
setCornerRoundness(DEFAULT_ROUNDNESS)
}
@@ -142,13 +151,13 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
-mapRange(1f - progress, 0f, stashedHandleHeight / 2f)
// Draw shadow.
- val shadowAlpha =
- mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, 25f, Interpolators.LINEAR)
+ val newShadowAlpha =
+ mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, shadowAlpha, Interpolators.LINEAR)
paint.setShadowLayer(
shadowBlur,
0f,
keyShadowDistance,
- setColorAlphaBound(Color.BLACK, Math.round(shadowAlpha))
+ setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha))
)
lastDrawnTransientRect.set(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 6b9297d446..1b3972d0ca 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -116,22 +116,6 @@ public class TaskbarDragLayer extends BaseDragLayer {
return true;
}
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mControllerCallbacks != null) {
- mControllerCallbacks.tryStashBasedOnMotionEvent(ev);
- }
- return super.onInterceptTouchEvent(ev);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (mControllerCallbacks != null) {
- mControllerCallbacks.tryStashBasedOnMotionEvent(ev);
- }
- return super.onTouchEvent(ev);
- }
-
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 2badc2405a..7f8879469a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -18,15 +18,12 @@ package com.android.launcher3.taskbar;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
-import android.view.MotionEvent;
import android.view.ViewTreeObserver;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.DimensionUtils;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
import java.io.PrintWriter;
@@ -40,7 +37,6 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
private final TaskbarActivityContext mActivity;
private final TaskbarDragLayer mTaskbarDragLayer;
private final int mFolderMargin;
- private float mGestureHeightYThreshold;
// Alpha properties for taskbar background.
private final AnimatedFloat mBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha);
@@ -59,6 +55,7 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
// Initialized in init.
private TaskbarControllers mControllers;
+ private TaskbarStashViaTouchController mTaskbarStashViaTouchController;
private AnimatedFloat mOnBackgroundNavButtonColorIntensity;
private float mLastSetBackgroundAlpha;
@@ -69,11 +66,11 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
mTaskbarDragLayer = taskbarDragLayer;
final Resources resources = mTaskbarDragLayer.getResources();
mFolderMargin = resources.getDimensionPixelSize(R.dimen.taskbar_folder_margin);
- updateGestureHeight();
}
public void init(TaskbarControllers controllers) {
mControllers = controllers;
+ mTaskbarStashViaTouchController = new TaskbarStashViaTouchController(mControllers);
mTaskbarDragLayer.init(new TaskbarDragLayerCallbacks());
mOnBackgroundNavButtonColorIntensity = mControllers.navbarButtonsViewController
@@ -130,17 +127,11 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
return mBgOffset;
}
- private void updateGestureHeight() {
- int gestureHeight = ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
- mActivity.getResources());
- mGestureHeightYThreshold = mActivity.getDeviceProfile().heightPx - gestureHeight;
- }
-
/**
* Make updates when configuration changes.
*/
public void onConfigurationChanged() {
- updateGestureHeight();
+ mTaskbarStashViaTouchController.updateGestureHeight();
}
private void updateBackgroundAlpha() {
@@ -213,8 +204,6 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
*/
public class TaskbarDragLayerCallbacks {
- private final int[] mTempOutLocation = new int[2];
-
/**
* Called to update the touchable insets.
* @see ViewTreeObserver.InternalInsetsInfo#setTouchableInsets(int)
@@ -223,37 +212,6 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
mControllers.taskbarInsetsController.updateInsetsTouchability(insetsInfo);
}
- /**
- * Listens to TaskbarDragLayer touch events and responds accordingly.
- */
- public void tryStashBasedOnMotionEvent(MotionEvent ev) {
- if (!DisplayController.isTransientTaskbar(mActivity)) {
- return;
- }
- if (mControllers.taskbarStashController.isStashed()) {
- return;
- }
-
- boolean stashTaskbar = false;
-
- MotionEvent screenCoordinates = MotionEvent.obtain(ev);
- if (ev.getAction() == MotionEvent.ACTION_OUTSIDE) {
- stashTaskbar = true;
- } else if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mTaskbarDragLayer.getLocationOnScreen(mTempOutLocation);
- screenCoordinates.offsetLocation(mTempOutLocation[0], mTempOutLocation[1]);
-
- if (!mControllers.taskbarViewController.isEventOverAnyItem(screenCoordinates)
- && screenCoordinates.getY() < mGestureHeightYThreshold) {
- stashTaskbar = true;
- }
- }
-
- if (stashTaskbar) {
- mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
- }
- }
-
/**
* Called when a child is removed from TaskbarDragLayer.
*/
@@ -283,9 +241,12 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
* Returns touch controllers.
*/
public TouchController[] getTouchControllers() {
- return new TouchController[]{mActivity.getDragController(),
+ return new TouchController[] {
+ mActivity.getDragController(),
mControllers.taskbarForceVisibleImmersiveController,
- mControllers.navbarButtonsViewController.getTouchController()};
+ mControllers.navbarButtonsViewController.getTouchController(),
+ mTaskbarStashViaTouchController,
+ };
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index bc582e2b65..e56e258d12 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -17,10 +17,13 @@ package com.android.launcher3.taskbar
import android.graphics.PorterDuff.Mode.SRC_ATOP
import android.graphics.PorterDuffColorFilter
+import android.os.Bundle
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
import androidx.annotation.IntDef
import androidx.annotation.LayoutRes
import com.airbnb.lottie.LottieAnimationView
@@ -153,6 +156,7 @@ class TaskbarEduTooltipController(val activityContext: TaskbarActivityContext) :
FLAG_AUTOHIDE_SUSPEND_EDU_OPEN,
true
)
+
tooltip.onCloseCallback = {
this.tooltip = null
controllers.taskbarAutohideSuspendController.updateFlag(
@@ -161,11 +165,47 @@ class TaskbarEduTooltipController(val activityContext: TaskbarActivityContext) :
)
controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
}
+ tooltip.accessibilityDelegate = createAccessibilityDelegate()
overlayContext.layoutInflater.inflate(contentResId, tooltip.content, true)
this.tooltip = tooltip
}
+ private fun createAccessibilityDelegate() =
+ object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View?,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ if (action == R.id.close) {
+ hide()
+ return true
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+
+ override fun onPopulateAccessibilityEvent(host: View?, event: AccessibilityEvent?) {
+ super.onPopulateAccessibilityEvent(host, event)
+ if (event?.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ event.text?.add(host?.context?.getText(R.string.taskbar_edu_a11y_title))
+ }
+ }
+
+ override fun onInitializeAccessibilityNodeInfo(
+ host: View?,
+ info: AccessibilityNodeInfo?
+ ) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ info?.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ R.id.close,
+ host?.context?.getText(R.string.taskbar_edu_close)
+ )
+ )
+ }
+ }
+
override fun dumpLogs(prefix: String?, pw: PrintWriter?) {
pw?.println(prefix + "TaskbarEduTooltipController:")
pw?.println("$prefix\tisTooltipEnabled=$isTooltipEnabled")
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
index 8cc8965d53..edcd4c8019 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
@@ -5,6 +5,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_ON;
@@ -34,7 +35,8 @@ public class TaskbarKeyguardController implements TaskbarControllers.LoggableTas
// locked.
public static final int MASK_ANY_SYSUI_LOCKED = SYSUI_STATE_BOUNCER_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
- | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+ | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
+ | SYSUI_STATE_DEVICE_DREAMING;
private final TaskbarActivityContext mContext;
private int mKeyguardSysuiFlags;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 9adbd7f415..cf8148e6c6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -457,8 +457,13 @@ public class TaskbarLauncherStateController {
animatorSet.play(mTaskbarCornerRoundness.animateToValue(cornerRoundness));
}
- if (hasAnyFlag(changedFlags, FLAG_DEVICE_LOCKED)) {
- // When transitioning between locked/unlocked, there is no stashing animation.
+ // Keep isUnlockTransition in sync with its counterpart in
+ // TaskbarStashController#createAnimToIsStashed.
+ boolean isUnlockTransition =
+ hasAnyFlag(changedFlags, FLAG_DEVICE_LOCKED) && !hasAnyFlag(FLAG_DEVICE_LOCKED);
+ if (isUnlockTransition) {
+ // When transitioning to unlocked, ensure the hotseat is fully visible from the
+ // beginning. The hotseat itself is animated by LauncherUnlockAnimationController.
mIconAlignment.cancelAnimation();
// updateValue ensures onIconAlignmentRatioChanged will be called if there is an actual
// change in value
@@ -553,12 +558,16 @@ public class TaskbarLauncherStateController {
animatorSet.play(stashAnimator);
}
- if (isAnimatingToLauncher() || mLauncherState == LauncherState.NORMAL) {
- // Translate back to 0 at a shorter or same duration as the icon alignment animation.
- // This ensures there is no jump after switching to hotseat, e.g. when swiping up from
- // overview to home. Currently we do duration / 2 just to make it feel snappier.
+ // Translate back to 0 at a shorter or same duration as the icon alignment animation.
+ // This ensures there is no jump after switching to hotseat, e.g. when swiping up from
+ // overview to home. When not in app, we do duration / 2 just to make it feel snappier.
+ long resetDuration = mControllers.taskbarStashController.isInApp()
+ ? duration
+ : duration / 2;
+ if (!mControllers.taskbarTranslationController.willAnimateToZeroBefore(resetDuration)
+ && (isAnimatingToLauncher() || mLauncherState == LauncherState.NORMAL)) {
animatorSet.play(mControllers.taskbarTranslationController
- .createAnimToResetTranslation(duration / 2));
+ .createAnimToResetTranslation(resetDuration));
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 0ed453818e..cfc7813305 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -201,14 +201,12 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
// Create a Stream of all applicable system shortcuts
private Stream getSystemShortcuts() {
- // concat a Stream of split options with a Stream of APP_INFO
- Stream appInfo = Stream.of(APP_INFO);
-
+ // append split options to APP_INFO shortcut, the order here will reflect in the popup
return Stream.concat(
+ Stream.of(APP_INFO),
Utilities.getSplitPositionOptions(mContext.getDeviceProfile())
.stream()
- .map(this::createSplitShortcutFactory),
- appInfo
+ .map(this::createSplitShortcutFactory)
);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index d26fbc4459..1e1769c614 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -32,8 +32,7 @@ import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import android.animation.Animator;
@@ -586,9 +585,12 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
// already stashed Taskbar.
boolean hotseatTopElement = mControllers.uiController.isHotseatIconOnTopWhenAligned()
|| !hasAnyFlag(changedFlags, FLAG_IN_APP);
- // If transitioning between locked/unlocked device, do not play a stash animation.
- boolean unLockedTransition = hasAnyFlag(changedFlags, FLAG_STASHED_DEVICE_LOCKED);
- boolean skipStashAnimation = !hotseatTopElement || unLockedTransition;
+ // If transitioning to unlocked device, do not play a stash animation.
+ // Keep isUnlockTransition in sync with its counterpart in
+ // TaskbarLauncherStateController#onStateChangeApplied.
+ boolean isUnlockTransition = hasAnyFlag(changedFlags, FLAG_STASHED_DEVICE_LOCKED)
+ && !hasAnyFlag(FLAG_STASHED_DEVICE_LOCKED);
+ boolean skipStashAnimation = !hotseatTopElement || isUnlockTransition;
if (isTransientTaskbar) {
createTransientAnimToIsStashed(mAnimator, isStashed, duration, animateBg, changedFlags,
@@ -907,12 +909,20 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
long startDelay = 0;
updateStateForFlag(FLAG_STASHED_IN_APP_SYSUI, hasAnyFlag(systemUiStateFlags,
- SYSUI_STATE_QUICK_SETTINGS_EXPANDED
- | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED));
+ SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE));
updateStateForFlag(FLAG_STASHED_SYSUI,
hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING));
- updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED,
- hasAnyFlag(systemUiStateFlags, MASK_ANY_SYSUI_LOCKED));
+
+ boolean isLocked = hasAnyFlag(systemUiStateFlags, MASK_ANY_SYSUI_LOCKED);
+ boolean wasLocked = hasAnyFlag(FLAG_STASHED_DEVICE_LOCKED);
+ updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, isLocked);
+
+ if (isLocked && !wasLocked && DisplayController.isTransientTaskbar(mActivity)) {
+ // Stash the transient taskbar when locking the device. This improves the transition
+ // to AoD (otherwise the taskbar stays a bit too long above the collapsing AoD scrim),
+ // and ensures the taskar state is reset when unlocking the device afterwards.
+ updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, true);
+ }
// Only update FLAG_STASHED_IN_APP_IME when system gesture is not in progress.
mIsImeShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SHOWING);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
new file mode 100644
index 0000000000..0e5fa380e7
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar
+
+import android.view.MotionEvent
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.anim.Interpolators.LINEAR
+import com.android.launcher3.testing.shared.ResourceUtils
+import com.android.launcher3.touch.SingleAxisSwipeDetector
+import com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE
+import com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.TouchController
+import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer
+
+/**
+ * A helper [TouchController] for [TaskbarDragLayerController], specifically to handle touch events
+ * to stash Transient Taskbar. There are two cases to handle:
+ * - A touch outside of Transient Taskbar bounds will immediately stash on [MotionEvent.ACTION_DOWN]
+ * or [MotionEvent.ACTION_OUTSIDE].
+ * - Touches inside Transient Taskbar bounds will stash if it is detected as a swipe down gesture.
+ *
+ * Note: touches to *unstash* Taskbar are handled by [TaskbarStashInputConsumer].
+ */
+class TaskbarStashViaTouchController(val controllers: TaskbarControllers) : TouchController {
+
+ private val activity: TaskbarActivityContext = controllers.taskbarActivityContext
+ private val enabled = DisplayController.isTransientTaskbar(activity)
+ private val swipeDownDetector: SingleAxisSwipeDetector
+ private val translationCallback = controllers.taskbarTranslationController.transitionCallback
+ /** Interpolator to apply resistance as user swipes down to the bottom of the screen. */
+ private val displacementInterpolator = LINEAR
+ /** How far we can translate the TaskbarView before it's offscreen. */
+ private val maxVisualDisplacement =
+ activity.resources.getDimensionPixelSize(R.dimen.transient_taskbar_margin).toFloat()
+ /** How far the swipe could go, if user swiped from the very top of TaskbarView. */
+ private val maxTouchDisplacement = maxVisualDisplacement + activity.deviceProfile.taskbarSize
+ private val touchDisplacementToStash =
+ activity.resources.getDimensionPixelSize(R.dimen.taskbar_to_nav_threshold).toFloat()
+
+ /** The height of the system gesture region, so we don't stash when touching down there. */
+ private var gestureHeightYThreshold = 0f
+
+ init {
+ updateGestureHeight()
+ swipeDownDetector = SingleAxisSwipeDetector(activity, createSwipeListener(), VERTICAL)
+ swipeDownDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false)
+ }
+
+ fun updateGestureHeight() {
+ if (!enabled) return
+
+ val gestureHeight: Int =
+ ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
+ activity.resources
+ )
+ gestureHeightYThreshold = (activity.deviceProfile.heightPx - gestureHeight).toFloat()
+ }
+
+ private fun createSwipeListener() =
+ object : SingleAxisSwipeDetector.Listener {
+ private var lastDisplacement = 0f
+
+ override fun onDragStart(start: Boolean, startDisplacement: Float) {}
+
+ override fun onDrag(displacement: Float): Boolean {
+ lastDisplacement = displacement
+ if (displacement < 0) return false
+ // Apply resistance so that the visual displacement doesn't go beyond the screen.
+ translationCallback.onActionMove(
+ Utilities.mapToRange(
+ displacement,
+ 0f,
+ maxTouchDisplacement,
+ 0f,
+ maxVisualDisplacement,
+ displacementInterpolator
+ )
+ )
+ return false
+ }
+
+ override fun onDragEnd(velocity: Float) {
+ val isFlingDown = swipeDownDetector.isFling(velocity) && velocity > 0
+ val isSignificantDistance = lastDisplacement > touchDisplacementToStash
+ if (isFlingDown || isSignificantDistance) {
+ // Successfully triggered stash.
+ controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
+ }
+ translationCallback.onActionEnd()
+ swipeDownDetector.finishedScrolling()
+ }
+ }
+
+ override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
+ if (!enabled || controllers.taskbarStashController.isStashed) {
+ return false
+ }
+
+ val screenCoordinatesEv = MotionEvent.obtain(ev)
+ screenCoordinatesEv.setLocation(ev.rawX, ev.rawY)
+ if (ev.action == MotionEvent.ACTION_OUTSIDE) {
+ controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
+ } else if (controllers.taskbarViewController.isEventOverAnyItem(screenCoordinatesEv)) {
+ swipeDownDetector.onTouchEvent(ev)
+ if (swipeDownDetector.isDraggingState) {
+ return true
+ }
+ } else if (ev.action == MotionEvent.ACTION_DOWN) {
+ if (screenCoordinatesEv.y < gestureHeightYThreshold) {
+ controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
+ }
+ }
+ return false
+ }
+
+ override fun onControllerTouchEvent(ev: MotionEvent) = swipeDownDetector.onTouchEvent(ev)
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index b9b63dbac6..065d1117c8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -133,6 +133,21 @@ public class TaskbarTranslationController implements TaskbarControllers.Loggable
return mCallback;
}
+ /**
+ * Returns true if we will animate to zero before the input duration.
+ */
+ public boolean willAnimateToZeroBefore(long duration) {
+ if (mSpringBounce != null && mSpringBounce.isRunning()) {
+ long springDuration = mSpringBounce.getDuration();
+ long current = mSpringBounce.getCurrentPlayTime();
+ return (springDuration - current < duration);
+ }
+ if (mTranslationYForSwipe.isAnimatingToValue(0)) {
+ return mTranslationYForSwipe.getRemainingTime() < duration;
+ }
+ return false;
+ }
+
/**
* Returns an animation to reset the taskbar translation to {@code 0}.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index c5b6cdf54d..0eecc5664b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -424,20 +424,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
}
}
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- mControllerCallbacks.onInterceptTouchEvent(ev);
- return super.onInterceptTouchEvent(ev);
- }
-
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (mIconLayoutBounds.left <= event.getX()
- && event.getX() <= mIconLayoutBounds.right
- && !DisplayController.isTransientTaskbar(mActivityContext)) {
- // Don't allow long pressing between icons, or above/below them
- // unless its transient taskbar.
- mControllerCallbacks.clearTouchInProgress();
+ if (mIconLayoutBounds.left <= event.getX() && event.getX() <= mIconLayoutBounds.right) {
+ // Don't allow long pressing between icons, or above/below them.
return true;
}
if (mControllerCallbacks.onTouchEvent(event)) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index fafd0bd4cd..f2ba54af3f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -26,8 +26,6 @@ import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_ALIGNMENT_ANIM;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_REVEAL_ANIM;
@@ -61,7 +59,6 @@ import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
@@ -116,9 +113,6 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
private final TaskbarModelCallbacks mModelCallbacks;
- // Captures swipe down action to close transient Taskbar.
- protected @Nullable SingleAxisSwipeDetector mSwipeDownDetector;
-
// Initialized in init.
private TaskbarControllers mControllers;
@@ -155,31 +149,6 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
colorHSL[2] = TASKBAR_DARK_THEME_ICONS_BACKGROUND_LUMINANCE;
mTaskbarThemedIconsBackgroundColor = ColorUtils.HSLToColor(colorHSL);
}
-
- if (DisplayController.isTransientTaskbar(mActivity)) {
- mSwipeDownDetector = new SingleAxisSwipeDetector(activity,
- new SingleAxisSwipeDetector.Listener() {
- private float mLastDisplacement;
-
- @Override
- public boolean onDrag(float displacement) {
- mLastDisplacement = displacement;
- return false;
- }
-
- @Override
- public void onDragEnd(float velocity) {
- if (mLastDisplacement > 0) {
- mControllers.taskbarStashController
- .updateAndAnimateTransientTaskbar(true);
- }
- }
-
- @Override
- public void onDragStart(boolean start, float startDisplacement) {}
- }, VERTICAL);
- mSwipeDownDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
- }
mIsRtl = Utilities.isRtl(mTaskbarView.getResources());
}
@@ -659,8 +628,6 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
private float mDownX, mDownY;
private boolean mCanceledStashHint;
- private boolean mTouchInProgress;
-
public View.OnClickListener getIconOnClickListener() {
return mActivity.getItemOnClickListener();
}
@@ -681,76 +648,40 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
.updateAndAnimateIsManuallyStashedInApp(true);
}
- /**
- * Simply listens to all intercept touch events passed to TaskbarView.
- */
- public void onInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mTouchInProgress = true;
- }
-
- if (mTouchInProgress && mSwipeDownDetector != null) {
- mSwipeDownDetector.onTouchEvent(ev);
- }
-
- if (ev.getAction() == MotionEvent.ACTION_UP
- || ev.getAction() == MotionEvent.ACTION_CANCEL) {
- clearTouchInProgress();
- }
- }
-
/**
* Get the first chance to handle TaskbarView#onTouchEvent, and return whether we want to
* consume the touch so TaskbarView treats it as an ACTION_CANCEL.
+ * TODO(b/270395798): We can remove this entirely once we remove the Transient Taskbar flag.
*/
public boolean onTouchEvent(MotionEvent motionEvent) {
- boolean shouldConsumeTouch = false;
- boolean clearTouchInProgress = false;
-
final float x = motionEvent.getRawX();
final float y = motionEvent.getRawY();
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
- mTouchInProgress = true;
mDownX = x;
mDownY = y;
mControllers.taskbarStashController.startStashHint(/* animateForward = */ true);
mCanceledStashHint = false;
break;
case MotionEvent.ACTION_MOVE:
- if (mTouchInProgress
- && !mCanceledStashHint
+ if (!mCanceledStashHint
&& squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
mControllers.taskbarStashController.startStashHint(
/* animateForward= */ false);
mCanceledStashHint = true;
- shouldConsumeTouch = true;
+ return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (mTouchInProgress && !mCanceledStashHint) {
+ if (!mCanceledStashHint) {
mControllers.taskbarStashController.startStashHint(
/* animateForward= */ false);
}
- clearTouchInProgress = true;
break;
}
- if (mTouchInProgress && mSwipeDownDetector != null) {
- mSwipeDownDetector.onTouchEvent(motionEvent);
- }
- if (clearTouchInProgress) {
- clearTouchInProgress();
- }
- return shouldConsumeTouch;
- }
-
- /**
- * Ensures that we do not pass any more touch events to the SwipeDetector.
- */
- public void clearTouchInProgress() {
- mTouchInProgress = false;
+ return false;
}
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 847114a960..f1c4f68e28 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
@@ -161,6 +162,9 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) {
return false;
}
+ if (isTrackpadMultiFingerSwipe(ev)) {
+ return false;
+ }
int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) {
return false;
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 81b818e926..66d1e1e2cf 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -18,7 +18,6 @@ package com.android.quickstep;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Surface.ROTATION_0;
-import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
@@ -243,8 +242,8 @@ public class RotationTouchHelper implements DisplayInfoChangeListener {
*/
public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex,
BaseActivityInterface activity) {
- if (isTrackpadMotionEvent(event)) {
- return isTrackpadMultiFingerSwipe(event) && !activity.isResumed();
+ if (isTrackpadMultiFingerSwipe(event)) {
+ return true;
}
return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(pointerIndex),
event.getY(pointerIndex));
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
index 6637463eed..51c2b4829a 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
@@ -75,7 +75,7 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer {
Resources res = context.getResources();
mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
- mTaskbarNavThreshold = res.getDimensionPixelSize(R.dimen.taskbar_nav_threshold);
+ mTaskbarNavThreshold = res.getDimensionPixelSize(R.dimen.taskbar_from_nav_threshold);
mTaskbarNavThresholdY = taskbarActivityContext.getDeviceProfile().heightPx
- mTaskbarNavThreshold;
mIsTaskbarAllAppsOpen =
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index fe50d9aeff..9a25e9ca64 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -225,10 +225,6 @@ public abstract class RecentsView CONTENT_ALPHA =
new FloatProperty("contentAlpha") {
@Override
@@ -3893,14 +3889,13 @@ public abstract class RecentsView {
ActivityManagerWrapper.getInstance().removeTask(primaryTaskId);
if (secondaryTaskId != -1) {
ActivityManagerWrapper.getInstance().removeTask(secondaryTaskId);
}
- },
- REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
+ });
}
protected void onDismissAnimationEnds() {
@@ -3923,9 +3918,8 @@ public abstract class RecentsView {
- UI_HELPER_EXECUTOR.getHandler().postDelayed(
- ActivityManagerWrapper.getInstance()::removeAllRecentTasks,
- REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
+ UI_HELPER_EXECUTOR.getHandler().post(
+ ActivityManagerWrapper.getInstance()::removeAllRecentTasks);
removeTasksViewsAndClearAllButton();
startHome();
});
diff --git a/res/color-night-v31/taskbar_background.xml b/res/color-night-v31/taskbar_background.xml
index 8df168678c..3409d52b25 100644
--- a/res/color-night-v31/taskbar_background.xml
+++ b/res/color-night-v31/taskbar_background.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
-
+
diff --git a/res/color-v31/taskbar_background.xml b/res/color-v31/taskbar_background.xml
index c2bcab8c0d..5c53e4a0c9 100644
--- a/res/color-v31/taskbar_background.xml
+++ b/res/color-v31/taskbar_background.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
-
+
diff --git a/res/layout/widgets_full_sheet_large_screen.xml b/res/layout/widgets_full_sheet_large_screen.xml
index 0626e60cc5..f563baa70e 100644
--- a/res/layout/widgets_full_sheet_large_screen.xml
+++ b/res/layout/widgets_full_sheet_large_screen.xml
@@ -12,8 +12,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-
+
diff --git a/res/values/id.xml b/res/values/id.xml
index dc819443a6..7b812de71e 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -28,6 +28,7 @@
+
@@ -41,4 +42,5 @@
+
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 4472383339..ad95f7e240 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -285,6 +285,10 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: " + action +
+ " user: " + user);
+ }
if (user != null) {
if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
diff --git a/src/com/android/launcher3/anim/AnimatedFloat.java b/src/com/android/launcher3/anim/AnimatedFloat.java
index 2380af4e03..2f3fa63321 100644
--- a/src/com/android/launcher3/anim/AnimatedFloat.java
+++ b/src/com/android/launcher3/anim/AnimatedFloat.java
@@ -132,6 +132,15 @@ public class AnimatedFloat {
return isAnimating() && mEndValue != null && mEndValue == endValue;
}
+ /**
+ * Returns the remaining time of the existing animation (if any).
+ */
+ public long getRemainingTime() {
+ return isAnimating() && mValueAnimator.isRunning()
+ ? Math.max(0, mValueAnimator.getDuration() - mValueAnimator.getCurrentPlayTime())
+ : 0;
+ }
+
/**
* Returns whether we are currently not animating, and the animation's value matches the given.
*/
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 2c8f1f3a2a..ae5d8d489a 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -66,6 +66,9 @@ public class GridCustomizationsProvider extends ContentProvider {
private static final String KEY_SURFACE_PACKAGE = "surface_package";
private static final String KEY_CALLBACK = "callback";
+ public static final String KEY_HIDE_BOTTOM_ROW = "hide_bottom_row";
+
+ private static final int MESSAGE_ID_UPDATE_PREVIEW = 1337;
private final ArrayMap mActivePreviews = new ArrayMap<>();
@@ -79,7 +82,7 @@ public class GridCustomizationsProvider extends ContentProvider {
String[] selectionArgs, String sortOrder) {
switch (uri.getPath()) {
case KEY_LIST_OPTIONS: {
- MatrixCursor cursor = new MatrixCursor(new String[] {
+ MatrixCursor cursor = new MatrixCursor(new String[]{
KEY_NAME, KEY_ROWS, KEY_COLS, KEY_PREVIEW_COUNT, KEY_IS_DEFAULT});
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
for (GridOption gridOption : idp.parseAllGridOptions(getContext())) {
@@ -95,7 +98,7 @@ public class GridCustomizationsProvider extends ContentProvider {
}
case GET_ICON_THEMED:
case ICON_THEMED: {
- MatrixCursor cursor = new MatrixCursor(new String[] {BOOLEAN_VALUE});
+ MatrixCursor cursor = new MatrixCursor(new String[]{BOOLEAN_VALUE});
cursor.newRow().add(BOOLEAN_VALUE, isThemedIconEnabled(getContext()) ? 1 : 0);
return cursor;
}
@@ -224,7 +227,14 @@ public class GridCustomizationsProvider extends ContentProvider {
@Override
public boolean handleMessage(Message message) {
- destroyObserver(this);
+ if (destroyed) {
+ return true;
+ }
+ if (message.what == MESSAGE_ID_UPDATE_PREVIEW) {
+ renderer.hideBottomRow(message.getData().getBoolean(KEY_HIDE_BOTTOM_ROW));
+ } else {
+ destroyObserver(this);
+ }
return true;
}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index b061f8f643..7f49aa9cc1 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -344,6 +344,25 @@ public class LauncherPreviewRenderer extends ContextWrapper
return mHotseat;
}
+ /**
+ * Hides the components in the bottom row.
+ *
+ * @param hide True to hide and false to show.
+ */
+ public void hideBottomRow(boolean hide) {
+ mUiHandler.post(() -> {
+ if (mDp.isTaskbarPresent) {
+ // hotseat icons on bottom
+ mHotseat.setIconsAlpha(hide ? 0 : 1);
+ if (mDp.isQsbInline) {
+ mHotseat.setQsbAlpha(hide ? 0 : 1);
+ }
+ } else {
+ mHotseat.setQsbAlpha(hide ? 0 : 1);
+ }
+ });
+ }
+
@Override
public CellLayout getScreenWithId(int screenId) {
return mWorkspaceScreens.get(screenId);
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 85c0a7a565..0767e69378 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -87,6 +87,8 @@ public class PreviewSurfaceRenderer {
private final SurfaceControlViewHost mSurfaceControlViewHost;
private boolean mDestroyed = false;
+ private LauncherPreviewRenderer mRenderer;
+ private boolean mHideQsb;
public PreviewSurfaceRenderer(Context context, Bundle bundle) throws Exception {
mContext = context;
@@ -97,6 +99,7 @@ public class PreviewSurfaceRenderer {
gridName = InvariantDeviceProfile.getCurrentGridName(context);
}
mWallpaperColors = bundle.getParcelable(KEY_COLORS);
+ mHideQsb = bundle.getBoolean(GridCustomizationsProvider.KEY_HIDE_BOTTOM_ROW);
mIdp = new InvariantDeviceProfile(context, gridName);
mHostToken = bundle.getBinder(KEY_HOST_TOKEN);
@@ -174,6 +177,17 @@ public class PreviewSurfaceRenderer {
MODEL_EXECUTOR.execute(this::loadModelData);
}
+ /**
+ * Hides the components in the bottom row.
+ *
+ * @param hide True to hide and false to show.
+ */
+ public void hideBottomRow(boolean hide) {
+ if (mRenderer != null) {
+ mRenderer.hideBottomRow(hide);
+ }
+ }
+
@WorkerThread
private void loadModelData() {
final boolean migrated = doGridMigrationIfNecessary();
@@ -209,8 +223,8 @@ public class PreviewSurfaceRenderer {
DeviceProfile deviceProfile = mIdp.getDeviceProfile(previewContext);
String query =
LauncherSettings.Favorites.SCREEN + " = " + Workspace.FIRST_SCREEN_ID
- + " or " + LauncherSettings.Favorites.CONTAINER + " = "
- + LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ + " or " + LauncherSettings.Favorites.CONTAINER + " = "
+ + LauncherSettings.Favorites.CONTAINER_HOTSEAT;
if (deviceProfile.isTwoPanels) {
query += " or " + LauncherSettings.Favorites.SCREEN + " = "
+ Workspace.SECOND_SCREEN_ID;
@@ -254,8 +268,10 @@ public class PreviewSurfaceRenderer {
if (mDestroyed) {
return;
}
- View view = new LauncherPreviewRenderer(inflationContext, mIdp, mWallpaperColors,
- launcherWidgetSpanInfo).getRenderedView(dataModel, widgetProviderInfoMap);
+ mRenderer = new LauncherPreviewRenderer(inflationContext, mIdp,
+ mWallpaperColors, launcherWidgetSpanInfo);
+ mRenderer.hideBottomRow(mHideQsb);
+ View view = mRenderer.getRenderedView(dataModel, widgetProviderInfoMap);
// This aspect scales the view to fit in the surface and centers it
final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(),
mHeight / (float) view.getMeasuredHeight());
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 9d06d4a6fa..0edf2921ae 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -140,13 +140,11 @@ public abstract class ArrowPopup
mOutlineRadius = Themes.getDialogCornerRadius(context);
mActivityContext = ActivityContext.lookupContext(context);
mIsRtl = Utilities.isRtl(getResources());
-
- int popupPrimaryColor = Themes.getAttrColor(context, R.attr.popupColorPrimary);
- mArrowColor = popupPrimaryColor;
mElevation = getResources().getDimension(R.dimen.deep_shortcuts_elevation);
// Initialize arrow view
final Resources resources = getResources();
+ mArrowColor = getColorStateList(getContext(), R.color.popup_shade_first).getDefaultColor();
mMargin = resources.getDimensionPixelSize(R.dimen.popup_margin);
mArrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
mArrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
@@ -159,6 +157,7 @@ public abstract class ArrowPopup
int smallerRadius = resources.getDimensionPixelSize(R.dimen.popup_smaller_radius);
mRoundedTop = new GradientDrawable();
+ int popupPrimaryColor = Themes.getAttrColor(context, R.attr.popupColorPrimary);
mRoundedTop.setColor(popupPrimaryColor);
mRoundedTop.setCornerRadii(new float[] { mOutlineRadius, mOutlineRadius, mOutlineRadius,
mOutlineRadius, smallerRadius, smallerRadius, smallerRadius, smallerRadius});
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index 2ffe34f415..a917b68e56 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -197,13 +197,13 @@ public class SecondaryDragLayer extends BaseDragLayer
return false;
}
+ // order of this list will reflect in the popup
List systemShortcuts = new ArrayList<>();
-
+ systemShortcuts.add(APP_INFO.getShortcut(mActivity, item, v));
// Hide redundant pin shortcut for app drawer icons if drag-n-drop is enabled.
if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
systemShortcuts.add(mPinnedAppsAdapter.getSystemShortcut(item, v));
}
- systemShortcuts.add(APP_INFO.getShortcut(mActivity, item, v));
int deepShortcutCount = popupDataProvider.getShortcutCountForItem(item);
final PopupContainerWithArrow container;
if (ENABLE_MATERIAL_U_POPUP.get()) {
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 8ff68880a6..823003ce06 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -18,6 +18,7 @@ package com.android.launcher3.views;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_OUTSIDE;
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
@@ -260,7 +261,10 @@ public abstract class BaseDragLayer
mTouchCompleteListener = null;
}
- if (mActiveController != null) {
+ if (mActiveController != null && ev.getAction() != ACTION_OUTSIDE) {
+ // For some reason, once we intercept touches and have an mActiveController, we won't
+ // get onInterceptTouchEvent() for ACTION_OUTSIDE. Thus, we must recalculate a new
+ // TouchController (if any) to handle the ACTION_OUTSIDE here in onTouchEvent() as well.
return mActiveController.onControllerTouchEvent(ev);
} else {
// In case no child view handled the touch event, we may not get onIntercept anymore
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index cfd241c130..07c6ba4628 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -32,7 +32,6 @@ import android.view.WindowInsets;
import android.view.animation.Interpolator;
import android.widget.Toast;
-import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.core.view.ViewCompat;
@@ -182,7 +181,6 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView
* Measures the dimension of this view and its children by taking system insets, navigation bar,
* status bar, into account.
*/
- @GuardedBy("MainThread")
protected void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
int widthUsed;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index bd75ce6061..5293c3dda0 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -30,7 +30,6 @@ import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Outline;
import android.graphics.Rect;
import android.os.Build;
import android.os.Process;
@@ -43,13 +42,10 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
import android.view.WindowInsets;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import android.widget.TextView;
import android.window.BackEvent;
@@ -68,10 +64,7 @@ import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.model.UserManagerState;
import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.recyclerview.ViewHolderBinder;
-import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout;
@@ -80,8 +73,6 @@ import com.android.launcher3.views.WidgetsEduView;
import com.android.launcher3.widget.BaseWidgetSheet;
import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
-import com.android.launcher3.widget.model.WidgetsListContentEntry;
-import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.picker.search.SearchModeListener;
import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
import com.android.launcher3.widget.util.WidgetsTableUtils;
@@ -89,7 +80,6 @@ import com.android.launcher3.workprofile.PersonalWorkPagedView;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.IntStream;
@@ -105,27 +95,22 @@ public class WidgetsFullSheet extends BaseWidgetSheet
private static final long EDUCATION_TIP_DELAY_MS = 200;
private static final long EDUCATION_DIALOG_DELAY_MS = 500;
private static final float VERTICAL_START_POSITION = 0.3f;
- private static final int PERSONAL_TAB = 0;
- private static final int WORK_TAB = 1;
- private static final String SUGGESTIONS_PACKAGE_NAME = "widgets_list_suggestions_entry";
// The widget recommendation table can easily take over the entire screen on devices with small
// resolution or landscape on phone. This ratio defines the max percentage of content area that
// the table can display.
private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.75f;
-
private static final String KEY_WIDGETS_EDUCATION_DIALOG_SEEN =
"launcher.widgets_education_dialog_seen";
private final UserManagerState mUserManagerState = new UserManagerState();
-
- private final boolean mHasWorkProfile;
- private final SparseArray mAdapters = new SparseArray();
private final UserHandle mCurrentUser = Process.myUserHandle();
private final Predicate mPrimaryWidgetsFilter =
entry -> mCurrentUser.equals(entry.mPkgItem.user);
private final Predicate mWorkWidgetsFilter =
entry -> !mCurrentUser.equals(entry.mPkgItem.user)
&& !mUserManagerState.isUserQuiet(entry.mPkgItem.user);
+ protected final boolean mHasWorkProfile;
+ protected final SparseArray mAdapters = new SparseArray();
@Nullable private ArrowTipView mLatestEducationalTip;
private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
new OnLayoutChangeListener() {
@@ -175,52 +160,27 @@ public class WidgetsFullSheet extends BaseWidgetSheet
@Px private final int mTabsHeight;
@Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
- @Nullable private PersonalWorkPagedView mViewPager;
+ @Nullable private WidgetsRecyclerView mCurrentTouchEventRecyclerView;
+ @Nullable PersonalWorkPagedView mViewPager;
private boolean mIsInSearchMode;
private boolean mIsNoWidgetsViewNeeded;
@Px private int mMaxSpanPerRow;
- private TextView mNoWidgetsView;
-
- private StickyHeaderLayout mSearchScrollView;
- private WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
- private LinearLayout mSuggestedWidgetsContainer;
- private WidgetsListHeader mSuggestedWidgetsHeader;
- private View mTabBar;
- private View mSearchBarContainer;
- private WidgetsSearchBar mSearchBar;
- private TextView mHeaderTitle;
- private LinearLayout mRightPane;
- private FrameLayout mRightPaneScrollView;
- private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
private DeviceProfile mDeviceProfile;
- private final boolean mIsTwoPane;
private int mOrientation;
- private @Nullable WidgetsRecyclerView mCurrentTouchEventRecyclerView;
- private RecyclerViewFastScroller mFastScroller;
-
- private final ViewOutlineProvider mViewOutlineProviderRightPane = new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(
- 0,
- 0,
- view.getMeasuredWidth(),
- view.getMeasuredHeight() - getResources().getDimensionPixelSize(
- R.dimen.widget_list_horizontal_margin_large_screen),
- view.getResources().getDimensionPixelSize(
- R.dimen.widget_list_top_bottom_corner_radius)
- );
- }
- };
+ protected TextView mNoWidgetsView;
+ protected StickyHeaderLayout mSearchScrollView;
+ protected WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
+ protected View mTabBar;
+ protected View mSearchBarContainer;
+ protected WidgetsSearchBar mSearchBar;
+ protected TextView mHeaderTitle;
+ protected RecyclerViewFastScroller mFastScroller;
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDeviceProfile = Launcher.getLauncher(context).getDeviceProfile();
- mIsTwoPane = mDeviceProfile.isTablet
- && mDeviceProfile.isLandscape
- && LARGE_SCREEN_WIDGET_PICKER.get();
mHasWorkProfile = context.getSystemService(LauncherApps.class).getProfiles().size() > 1;
mOrientation = Launcher.getLauncher(context).getOrientation();
mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
@@ -243,37 +203,39 @@ public class WidgetsFullSheet extends BaseWidgetSheet
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+
mContent = findViewById(R.id.container);
setContentBackgroundWithParent(getContext().getDrawable(R.drawable.bg_widgets_full_sheet),
mContent);
-
mContent.setOutlineProvider(mViewOutlineProvider);
mContent.setClipToOutline(true);
+ setupSheet();
+ }
+ protected void setupSheet() {
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+ int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
+ : R.layout.widgets_full_sheet_recyclerview;
+ layoutInflater.inflate(contentLayoutRes, mContent, true);
- if (mIsTwoPane) {
- layoutInflater.inflate(
- mHasWorkProfile
- ? R.layout.widgets_full_sheet_paged_view_large_screen
- : R.layout.widgets_full_sheet_recyclerview_large_screen,
- findViewById(R.id.recycler_view_container),
- true);
- } else {
- layoutInflater.inflate(
- mHasWorkProfile
- ? R.layout.widgets_full_sheet_paged_view
- : R.layout.widgets_full_sheet_recyclerview,
- mContent,
- true);
- }
+ setupViews();
+ mRecommendedWidgetsTable = mSearchScrollView.findViewById(R.id.recommended_widget_table);
+ mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
+ mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
+ mHeaderTitle = mSearchScrollView.findViewById(R.id.title);
+
+ onRecommendedWidgetsBound();
+ onWidgetsBound();
+ setUpEducationViewsIfNeeded();
+ }
+
+ protected void setupViews() {
+ mSearchScrollView = findViewById(R.id.search_and_recommendations_container);
+ mSearchScrollView.setCurrentRecyclerView(findViewById(R.id.primary_widgets_list_view));
+ mNoWidgetsView = findViewById(R.id.no_widgets_text);
mFastScroller = findViewById(R.id.fast_scroller);
- if (mIsTwoPane) {
- mFastScroller.setVisibility(GONE);
- }
mFastScroller.setPopupView(findViewById(R.id.fast_scroller_popup));
-
mAdapters.get(AdapterHolder.PRIMARY).setup(findViewById(R.id.primary_widgets_list_view));
mAdapters.get(AdapterHolder.SEARCH).setup(findViewById(R.id.search_widgets_list_view));
if (mHasWorkProfile) {
@@ -294,78 +256,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mViewPager = null;
}
- mNoWidgetsView = findViewById(R.id.no_widgets_text);
-
- mSearchScrollView = findViewById(R.id.search_and_recommendations_container);
- mSearchScrollView.setCurrentRecyclerView(findViewById(R.id.primary_widgets_list_view));
-
- mRecommendedWidgetsTable = mIsTwoPane
- ? mContent.findViewById(R.id.recommended_widget_table)
- : mSearchScrollView.findViewById(R.id.recommended_widget_table);
-
- mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
- mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
-
- // Add suggested widgets.
- if (mIsTwoPane) {
- mSuggestedWidgetsContainer = mSearchScrollView.findViewById(R.id.suggestions_header);
-
- // Inflate the suggestions header.
- mSuggestedWidgetsHeader = (WidgetsListHeader) layoutInflater.inflate(
- R.layout.widgets_list_row_header_two_pane,
- mSuggestedWidgetsContainer,
- false);
- mSuggestedWidgetsHeader.setExpanded(true);
-
- PackageItemInfo packageItemInfo = new PackageItemInfo(
- /* packageName= */ SUGGESTIONS_PACKAGE_NAME,
- Process.myUserHandle()) {
- @Override
- public boolean usingLowResIcon() {
- return false;
- }
- };
- packageItemInfo.title = getContext().getString(R.string.suggested_widgets_header_title);
- WidgetsListHeaderEntry widgetsListHeaderEntry = WidgetsListHeaderEntry.create(
- packageItemInfo,
- getContext().getString(R.string.suggested_widgets_header_title),
- mActivityContext.getPopupDataProvider().getRecommendedWidgets())
- .withWidgetListShown();
-
- mSuggestedWidgetsHeader.applyFromItemInfoWithIcon(widgetsListHeaderEntry);
- mSuggestedWidgetsHeader.setIcon(
- getContext().getDrawable(R.drawable.widget_suggestions_icon));
- mSuggestedWidgetsHeader.setOnClickListener(view -> {
- mSuggestedWidgetsHeader.setExpanded(true);
- resetExpandedHeaders();
- mRightPane.removeAllViews();
- mRightPane.addView(mRecommendedWidgetsTable);
- });
- mSuggestedWidgetsContainer.addView(mSuggestedWidgetsHeader);
- }
-
mTabBar = mSearchScrollView.findViewById(R.id.tabs);
mSearchBarContainer = mSearchScrollView.findViewById(R.id.search_bar_container);
mSearchBar = mSearchScrollView.findViewById(R.id.widgets_search_bar);
- mHeaderTitle = mIsTwoPane
- ? mContent.findViewById(R.id.title)
- : mSearchScrollView.findViewById(R.id.title);
- mRightPane = mIsTwoPane ? mContent.findViewById(R.id.right_pane) : null;
- if (mRightPane != null) {
- mRightPane.setOutlineProvider(mViewOutlineProviderRightPane);
- }
- mRightPaneScrollView = mIsTwoPane
- ? mContent.findViewById(R.id.right_pane_scroll_view) : null;
-
- mWidgetsListTableViewHolderBinder =
- new WidgetsListTableViewHolderBinder(mActivityContext, layoutInflater, this, this);
- onRecommendedWidgetsBound();
- onWidgetsBound();
mSearchBar.initialize(
mActivityContext.getPopupDataProvider(), /* searchModeListener= */ this);
-
- setUpEducationViewsIfNeeded();
}
private void setDeviceManagementResources() {
@@ -380,14 +276,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet
@Override
public void onActivePageChanged(int currentActivePage) {
-
- // if the current active page changes to personal or work we set suggestions
- // to be the selected widget
- if (mIsTwoPane && mSuggestedWidgetsHeader != null
- && (currentActivePage == PERSONAL_TAB || currentActivePage == WORK_TAB)) {
- mSuggestedWidgetsHeader.callOnClick();
- }
-
AdapterHolder currentAdapterHolder = mAdapters.get(currentActivePage);
WidgetsRecyclerView currentRecyclerView =
mAdapters.get(currentActivePage).mWidgetsRecyclerView;
@@ -415,14 +303,9 @@ public class WidgetsFullSheet extends BaseWidgetSheet
}
}
- private void updateRecyclerViewVisibility(AdapterHolder adapterHolder) {
+ protected void updateRecyclerViewVisibility(AdapterHolder adapterHolder) {
// The first item is always an empty space entry. Look for any more items.
boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.hasVisibleEntries();
-
- if (mIsTwoPane) {
- mRightPane.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
- }
-
adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
if (adapterHolder.mAdapterType == AdapterHolder.SEARCH) {
@@ -468,9 +351,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
super.onAttachedToWindow();
mActivityContext.getAppWidgetHolder().addProviderChangeListener(this);
notifyWidgetProvidersChanged();
- if (!mIsTwoPane) {
- onRecommendedWidgetsBound();
- }
+ onRecommendedWidgetsBound();
}
@Override
@@ -558,14 +439,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
private boolean updateMaxSpansPerRow() {
if (getMeasuredWidth() == 0) return false;
- View content = mHasWorkProfile
- ? mViewPager
- : mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView;
- if (mIsTwoPane && mRightPane != null) {
- content = mRightPane;
- }
-
- @Px int maxHorizontalSpan = content.getMeasuredWidth() - (2 * mContentHorizontalMargin);
+ @Px int maxHorizontalSpan = getContentView().getMeasuredWidth()
+ - (2 * mContentHorizontalMargin);
if (mMaxSpanPerRow != maxHorizontalSpan) {
mMaxSpanPerRow = maxHorizontalSpan;
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPxPerRow(
@@ -582,6 +457,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
return false;
}
+ protected View getContentView() {
+ return mHasWorkProfile
+ ? mViewPager
+ : mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView;
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int width = r - l;
@@ -656,19 +537,13 @@ public class WidgetsFullSheet extends BaseWidgetSheet
public void onSearchResults(List entries) {
mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setWidgetsOnSearch(entries);
updateRecyclerViewVisibility(mAdapters.get(AdapterHolder.SEARCH));
- if (mIsTwoPane) {
- mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.selectFirstHeaderEntry();
- }
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
}
- private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
+ protected void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
mIsInSearchMode = isInSearchMode;
if (isInSearchMode) {
mRecommendedWidgetsTable.setVisibility(GONE);
- if (mIsTwoPane) {
- mSuggestedWidgetsContainer.setVisibility(GONE);
- }
if (mHasWorkProfile) {
mViewPager.setVisibility(GONE);
mTabBar.setVisibility(GONE);
@@ -680,10 +555,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mNoWidgetsView.setVisibility(GONE);
} else {
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.setVisibility(GONE);
- if (mIsTwoPane) {
- mSuggestedWidgetsContainer.setVisibility(VISIBLE);
- mSuggestedWidgetsHeader.callOnClick();
- }
// Visibility of recommended widgets, recycler views and headers are handled in methods
// below.
onRecommendedWidgetsBound();
@@ -691,7 +562,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
}
}
- private void resetExpandedHeaders() {
+ protected void resetExpandedHeaders() {
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.resetExpandedHeader();
mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.resetExpandedHeader();
}
@@ -713,14 +584,14 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mNoWidgetsView.getText().length(), noWidgetsViewTextBounds);
noWidgetsViewHeight = noWidgetsViewTextBounds.height();
}
- doMeasure(
- makeMeasureSpec(mActivityContext.getDeviceProfile().availableWidthPx,
- MeasureSpec.EXACTLY),
- makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
- MeasureSpec.EXACTLY));
- float maxTableHeight = mIsTwoPane ? Float.MAX_VALUE : (mContent.getMeasuredHeight()
- - mTabsHeight - getHeaderViewHeight()
- - noWidgetsViewHeight) * RECOMMENDATION_TABLE_HEIGHT_RATIO;
+ if (!isTwoPane()) {
+ doMeasure(
+ makeMeasureSpec(mActivityContext.getDeviceProfile().availableWidthPx,
+ MeasureSpec.EXACTLY),
+ makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
+ MeasureSpec.EXACTLY));
+ }
+ float maxTableHeight = getMaxTableHeight(noWidgetsViewHeight);
List> recommendedWidgetsInTable =
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithoutReordering(
@@ -732,13 +603,21 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mRecommendedWidgetsTable.setRecommendedWidgets(
recommendedWidgetsInTable, maxTableHeight);
} else {
- mRecommendedWidgetsTable.setVisibility(GONE);
- if (mSuggestedWidgetsContainer != null) {
- mSuggestedWidgetsContainer.setVisibility(GONE);
- }
+ hideRecommendations();
}
}
+ protected void hideRecommendations() {
+ mRecommendedWidgetsTable.setVisibility(GONE);
+ }
+
+ protected float getMaxTableHeight(float noWidgetsViewHeight) {
+ return (mContent.getMeasuredHeight()
+ - mTabsHeight - getHeaderViewHeight()
+ - noWidgetsViewHeight)
+ * RECOMMENDATION_TABLE_HEIGHT_RATIO;
+ }
+
private void open(boolean animate) {
if (animate) {
if (getPopupContainer().getInsets().bottom > 0) {
@@ -779,37 +658,49 @@ public class WidgetsFullSheet extends BaseWidgetSheet
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- // Disable swipe down when recycler view is scrolling or scroll view is scrolling
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mNoIntercept = false;
- WidgetsRecyclerView recyclerView = getRecyclerView();
- RecyclerViewFastScroller scroller = recyclerView.getScrollbar();
-
- if (scroller.getThumbOffsetY() >= 0
- && getPopupContainer().isEventOverView(scroller, ev)) {
- mNoIntercept = true;
- } else if (getPopupContainer().isEventOverView(recyclerView, ev)) {
- mNoIntercept = !recyclerView.shouldContainerScroll(ev, getPopupContainer());
- } else if (mIsTwoPane && mRightPaneScrollView != null
- && getPopupContainer().isEventOverView(mRightPaneScrollView, ev)) {
- mNoIntercept = mRightPaneScrollView.canScrollVertically(-1);
- }
+ mNoIntercept = shouldScroll(ev);
if (mSearchBar.isSearchBarFocused()
&& !getPopupContainer().isEventOverView(mSearchBarContainer, ev)) {
mSearchBar.clearSearchBarFocus();
}
}
+
return super.onControllerInterceptTouchEvent(ev);
}
+ protected boolean shouldScroll(MotionEvent ev) {
+ boolean intercept = false;
+ WidgetsRecyclerView recyclerView = getRecyclerView();
+ RecyclerViewFastScroller scroller = recyclerView.getScrollbar();
+ // Disable swipe down when recycler view is scrolling
+ if (scroller.getThumbOffsetY() >= 0 && getPopupContainer().isEventOverView(scroller, ev)) {
+ intercept = true;
+ } else if (getPopupContainer().isEventOverView(recyclerView, ev)) {
+ intercept = !recyclerView.shouldContainerScroll(ev, getPopupContainer());
+ }
+ return intercept;
+ }
+
/** Shows the {@link WidgetsFullSheet} on the launcher. */
public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
- WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
- .inflate(LARGE_SCREEN_WIDGET_PICKER.get()
- && launcher.getDeviceProfile().isTablet
- && launcher.getDeviceProfile().isLandscape
- ? R.layout.widgets_full_sheet_large_screen
- : R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
+ boolean isTwoPane = LARGE_SCREEN_WIDGET_PICKER.get()
+ && launcher.getDeviceProfile().isTablet
+ && launcher.getDeviceProfile().isLandscape;
+
+ WidgetsFullSheet sheet;
+ if (isTwoPane) {
+ sheet = (WidgetsTwoPaneSheet) launcher.getLayoutInflater().inflate(
+ R.layout.widgets_full_sheet_large_screen,
+ launcher.getDragLayer(),
+ false);
+ } else {
+ sheet = (WidgetsFullSheet) launcher.getLayoutInflater().inflate(
+ R.layout.widgets_full_sheet,
+ launcher.getDragLayer(),
+ false);
+ }
+
sheet.attachToContainer();
sheet.mIsOpen = true;
sheet.open(animate);
@@ -973,7 +864,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|| Utilities.isRunningInTestHarness();
}
- private void setUpEducationViewsIfNeeded() {
+ protected void setUpEducationViewsIfNeeded() {
if (!hasSeenEducationDialog()) {
postDelayed(() -> {
WidgetsEduView eduDialog = showEducationDialog();
@@ -991,14 +882,18 @@ public class WidgetsFullSheet extends BaseWidgetSheet
}
}
+ protected boolean isTwoPane() {
+ return false;
+ }
+
/** A holder class for holding adapters & their corresponding recycler view. */
- private final class AdapterHolder {
+ final class AdapterHolder {
static final int PRIMARY = 0;
static final int WORK = 1;
static final int SEARCH = 2;
private final int mAdapterType;
- private final WidgetsListAdapter mWidgetsListAdapter;
+ final WidgetsListAdapter mWidgetsListAdapter;
private final DefaultItemAnimator mWidgetsListItemAnimator;
private WidgetsRecyclerView mWidgetsRecyclerView;
@@ -1006,43 +901,14 @@ public class WidgetsFullSheet extends BaseWidgetSheet
AdapterHolder(int adapterType) {
mAdapterType = adapterType;
Context context = getContext();
- HeaderChangeListener headerChangeListener = new HeaderChangeListener() {
- @Override
- public void onHeaderChanged(@NonNull PackageUserKey selectedHeader) {
- WidgetsListContentEntry contentEntry = mActivityContext.getPopupDataProvider()
- .getSelectedAppWidgets(selectedHeader);
- if (contentEntry == null || mRightPane == null) {
- return;
- }
-
- if (mSuggestedWidgetsHeader != null) {
- mSuggestedWidgetsHeader.setExpanded(false);
- }
- WidgetsRowViewHolder widgetsRowViewHolder =
- mWidgetsListTableViewHolderBinder.newViewHolder(mRightPane);
- mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
- contentEntry,
- ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
- Collections.EMPTY_LIST);
- widgetsRowViewHolder.mDataCallback = data -> {
- mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
- contentEntry.withMaxSpanSize(mMaxSpanPerRow),
- ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
- Collections.singletonList(data));
- };
-
- mRightPane.removeAllViews();
- mRightPane.addView(widgetsRowViewHolder.itemView);
- }
- };
mWidgetsListAdapter = new WidgetsListAdapter(
context,
LayoutInflater.from(context),
this::getEmptySpaceHeight,
/* iconClickListener= */ WidgetsFullSheet.this,
/* iconLongClickListener= */ WidgetsFullSheet.this,
- mIsTwoPane ? headerChangeListener : null);
+ isTwoPane());
mWidgetsListAdapter.setHasStableIds(true);
switch (mAdapterType) {
case PRIMARY:
@@ -1073,7 +939,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mWidgetsRecyclerView.bindFastScrollbar(mFastScroller);
mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator);
mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
- if (!mIsTwoPane) {
+ if (!isTwoPane()) {
mWidgetsRecyclerView.setEdgeEffectFactory(
((SpringRelativeLayout) mContent).createEdgeEffectFactory());
}
@@ -1086,15 +952,4 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mWidgetsListAdapter.setMaxHorizontalSpansPxPerRow(mMaxSpanPerRow);
}
}
-
- /**
- * This is a listener for when the selected header gets changed in the left pane.
- */
- public interface HeaderChangeListener {
- /**
- * Sets the right pane to have the widgets for the currently selected header from
- * the left pane.
- */
- void onHeaderChanged(@NonNull PackageUserKey selectedHeader);
- }
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index c89eea8521..723ea17147 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -89,7 +89,7 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC
private final SparseArray mViewHolderBinders = new SparseArray<>();
private final WidgetListBaseRowEntryComparator mRowComparator =
new WidgetListBaseRowEntryComparator();
- @Nullable private final WidgetsFullSheet.HeaderChangeListener mHeaderChangeListener;
+ @Nullable private WidgetsTwoPaneSheet.HeaderChangeListener mHeaderChangeListener;
private final List mAllEntries = new ArrayList<>();
private ArrayList mVisibleEntries = new ArrayList<>();
@@ -107,8 +107,7 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
IntSupplier emptySpaceHeightProvider, OnClickListener iconClickListener,
OnLongClickListener iconLongClickListener,
- WidgetsFullSheet.HeaderChangeListener headerChangeListener) {
- mHeaderChangeListener = headerChangeListener;
+ boolean isTwoPane) {
mContext = context;
mMaxHorizontalSpan = WidgetSizes.getWidgetSizePx(
ActivityContext.lookupContext(context).getDeviceProfile(),
@@ -122,12 +121,17 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC
VIEW_TYPE_WIDGETS_HEADER,
new WidgetsListHeaderViewHolderBinder(
layoutInflater, /* onHeaderClickListener= */ this,
- headerChangeListener != null));
+ isTwoPane));
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_SPACE,
new WidgetsSpaceViewHolderBinder(emptySpaceHeightProvider));
}
+ public void setHeaderChangeListener(WidgetsTwoPaneSheet.HeaderChangeListener
+ headerChangeListener) {
+ mHeaderChangeListener = headerChangeListener;
+ }
+
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
mRecyclerView = recyclerView;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
new file mode 100644
index 0000000000..de7497a1a4
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget.picker;
+
+import android.content.Context;
+import android.graphics.Outline;
+import android.os.Process;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Popup for showing the full list of available widgets with a two-pane layout.
+ */
+public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
+
+ private static final int PERSONAL_TAB = 0;
+ private static final int WORK_TAB = 1;
+ private static final String SUGGESTIONS_PACKAGE_NAME = "widgets_list_suggestions_entry";
+
+ private LinearLayout mSuggestedWidgetsContainer;
+ private WidgetsListHeader mSuggestedWidgetsHeader;
+ private LinearLayout mRightPane;
+
+ private FrameLayout mRightPaneScrollView;
+ private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
+
+ private final ViewOutlineProvider mViewOutlineProviderRightPane = new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(
+ 0,
+ 0,
+ view.getMeasuredWidth(),
+ view.getMeasuredHeight() - getResources().getDimensionPixelSize(
+ R.dimen.widget_list_horizontal_margin_large_screen),
+ view.getResources().getDimensionPixelSize(
+ R.dimen.widget_list_top_bottom_corner_radius)
+ );
+ }
+ };
+
+ public WidgetsTwoPaneSheet(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public WidgetsTwoPaneSheet(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void setupSheet() {
+ // Set the header change listener in the adapter
+ mAdapters.get(AdapterHolder.PRIMARY)
+ .mWidgetsListAdapter.setHeaderChangeListener(getHeaderChangeListener());
+ mAdapters.get(AdapterHolder.WORK)
+ .mWidgetsListAdapter.setHeaderChangeListener(getHeaderChangeListener());
+ mAdapters.get(AdapterHolder.SEARCH)
+ .mWidgetsListAdapter.setHeaderChangeListener(getHeaderChangeListener());
+
+ LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+
+ int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view_large_screen
+ : R.layout.widgets_full_sheet_recyclerview_large_screen;
+ layoutInflater.inflate(contentLayoutRes, findViewById(R.id.recycler_view_container), true);
+
+ setupViews();
+
+ mWidgetsListTableViewHolderBinder =
+ new WidgetsListTableViewHolderBinder(mActivityContext, layoutInflater, this, this);
+ mRecommendedWidgetsTable = mContent.findViewById(R.id.recommended_widget_table);
+ mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
+ mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
+ mHeaderTitle = mContent.findViewById(R.id.title);
+ mRightPane = mContent.findViewById(R.id.right_pane);
+ mRightPane.setOutlineProvider(mViewOutlineProviderRightPane);
+ mRightPaneScrollView = mContent.findViewById(R.id.right_pane_scroll_view);
+ mRightPaneScrollView.setOverScrollMode(View.OVER_SCROLL_NEVER);
+
+ setupSuggestedWidgets(layoutInflater);
+ onRecommendedWidgetsBound();
+ onWidgetsBound();
+ setUpEducationViewsIfNeeded();
+
+ // Set the fast scroller as not visible for two pane layout.
+ mFastScroller.setVisibility(GONE);
+ }
+
+ @Override
+ protected void hideRecommendations() {
+ super.hideRecommendations();
+ mSuggestedWidgetsContainer.setVisibility(GONE);
+ }
+
+ private void setupSuggestedWidgets(LayoutInflater layoutInflater) {
+ // Add suggested widgets.
+ mSuggestedWidgetsContainer = mSearchScrollView.findViewById(R.id.suggestions_header);
+
+ // Inflate the suggestions header.
+ mSuggestedWidgetsHeader = (WidgetsListHeader) layoutInflater.inflate(
+ R.layout.widgets_list_row_header_two_pane,
+ mSuggestedWidgetsContainer,
+ false);
+ mSuggestedWidgetsHeader.setExpanded(true);
+
+ PackageItemInfo packageItemInfo = new PackageItemInfo(
+ /* packageName= */ SUGGESTIONS_PACKAGE_NAME,
+ Process.myUserHandle()) {
+ @Override
+ public boolean usingLowResIcon() {
+ return false;
+ }
+ };
+ packageItemInfo.title = getContext().getString(R.string.suggested_widgets_header_title);
+ WidgetsListHeaderEntry widgetsListHeaderEntry = WidgetsListHeaderEntry.create(
+ packageItemInfo,
+ getContext().getString(R.string.suggested_widgets_header_title),
+ mActivityContext.getPopupDataProvider().getRecommendedWidgets())
+ .withWidgetListShown();
+
+ mSuggestedWidgetsHeader.applyFromItemInfoWithIcon(widgetsListHeaderEntry);
+ mSuggestedWidgetsHeader.setIcon(
+ getContext().getDrawable(R.drawable.widget_suggestions_icon));
+ mSuggestedWidgetsHeader.setOnClickListener(view -> {
+ mSuggestedWidgetsHeader.setExpanded(true);
+ resetExpandedHeaders();
+ mRightPane.removeAllViews();
+ mRightPane.addView(mRecommendedWidgetsTable);
+ });
+ mSuggestedWidgetsContainer.addView(mSuggestedWidgetsHeader);
+ }
+
+ @Override
+ protected float getMaxTableHeight(float noWidgetsViewHeight) {
+ return Float.MAX_VALUE;
+ }
+
+ @Override
+ public void onActivePageChanged(int currentActivePage) {
+ // if the current active page changes to personal or work we set suggestions
+ // to be the selected widget
+ if (currentActivePage == PERSONAL_TAB || currentActivePage == WORK_TAB) {
+ mSuggestedWidgetsHeader.callOnClick();
+ }
+
+ super.onActivePageChanged(currentActivePage);
+ }
+
+ @Override
+ protected void updateRecyclerViewVisibility(AdapterHolder adapterHolder) {
+ // The first item is always an empty space entry. Look for any more items.
+ boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.hasVisibleEntries();
+
+ mRightPane.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
+
+ super.updateRecyclerViewVisibility(adapterHolder);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mSuggestedWidgetsContainer.setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onSearchResults(List entries) {
+ super.onSearchResults(entries);
+ mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.selectFirstHeaderEntry();
+ }
+
+ @Override
+ protected boolean shouldScroll(MotionEvent ev) {
+ return getPopupContainer().isEventOverView(mRightPaneScrollView, ev)
+ ? mRightPaneScrollView.canScrollVertically(-1)
+ : super.shouldScroll(ev);
+ }
+
+ @Override
+ protected void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
+ if (isInSearchMode) {
+ mSuggestedWidgetsContainer.setVisibility(GONE);
+ } else {
+ mSuggestedWidgetsContainer.setVisibility(VISIBLE);
+ mSuggestedWidgetsHeader.callOnClick();
+ }
+ super.setViewVisibilityBasedOnSearch(isInSearchMode);
+ }
+
+ @Override
+ protected View getContentView() {
+ return mRightPane;
+ }
+
+ private HeaderChangeListener getHeaderChangeListener() {
+ return new HeaderChangeListener() {
+ @Override
+ public void onHeaderChanged(@NonNull PackageUserKey selectedHeader) {
+ WidgetsListContentEntry contentEntry = mActivityContext.getPopupDataProvider()
+ .getSelectedAppWidgets(selectedHeader);
+
+ if (contentEntry == null || mRightPane == null) {
+ return;
+ }
+
+ if (mSuggestedWidgetsHeader != null) {
+ mSuggestedWidgetsHeader.setExpanded(false);
+ }
+ WidgetsRowViewHolder widgetsRowViewHolder =
+ mWidgetsListTableViewHolderBinder.newViewHolder(mRightPane);
+ mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
+ contentEntry,
+ ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
+ Collections.EMPTY_LIST);
+ widgetsRowViewHolder.mDataCallback = data -> {
+ mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
+ contentEntry,
+ ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
+ Collections.singletonList(data));
+ };
+ mRightPane.removeAllViews();
+ mRightPane.addView(widgetsRowViewHolder.itemView);
+ }
+ };
+ }
+
+ @Override
+ protected boolean isTwoPane() {
+ return true;
+ }
+
+ /**
+ * This is a listener for when the selected header gets changed in the left pane.
+ */
+ public interface HeaderChangeListener {
+ /**
+ * Sets the right pane to have the widgets for the currently selected header from
+ * the left pane.
+ */
+ void onHeaderChanged(@NonNull PackageUserKey selectedHeader);
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 9eeea2425a..c22f93dca3 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -72,7 +72,8 @@ public class WorkProfileTest extends AbstractLauncherUiTest {
}
updateWorkProfileSetupSuccessful("am start-user", output);
- Log.d(WORK_TAB_MISSING, "workProfileSuccessful? " + mWorkProfileSetupSuccessful);
+ Log.d(WORK_TAB_MISSING, "workProfileSuccessful? " + mWorkProfileSetupSuccessful +
+ " shellCmd: " + logStr);
if (!mWorkProfileSetupSuccessful) {
return; // no need to setup launcher since all tests will skip.
}