Merge "Fix issue with wrong token used for input consumer unregistration" into main
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 05e1535..851f2b3 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -157,8 +157,8 @@
import com.android.quickstep.util.WorkspaceRevealAnim;
import com.android.quickstep.views.FloatingWidgetView;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.DelegateLaunchAnimatorController;
+import com.android.systemui.animation.ActivityTransitionAnimator;
+import com.android.systemui.animation.DelegateTransitionAnimatorController;
import com.android.systemui.animation.LaunchableView;
import com.android.systemui.animation.RemoteAnimationDelegate;
import com.android.systemui.shared.system.BlurUtils;
@@ -1838,8 +1838,8 @@
// The CUJ is logged by the click handler, so we don't log it inside the animation
// library.
- ActivityLaunchAnimator.Controller controllerDelegate =
- ActivityLaunchAnimator.Controller.fromView(viewToUse, null /* cujType */);
+ ActivityTransitionAnimator.Controller controllerDelegate =
+ ActivityTransitionAnimator.Controller.fromView(viewToUse, null /* cujType */);
if (controllerDelegate == null) {
return null;
@@ -1847,15 +1847,15 @@
// This wrapper allows us to override the default value, telling the controller that the
// current window is below the animating window.
- ActivityLaunchAnimator.Controller controller =
- new DelegateLaunchAnimatorController(controllerDelegate) {
+ ActivityTransitionAnimator.Controller controller =
+ new DelegateTransitionAnimatorController(controllerDelegate) {
@Override
public boolean isBelowAnimatingWindow() {
return true;
}
};
- ActivityLaunchAnimator.Callback callback = task -> {
+ ActivityTransitionAnimator.Callback callback = task -> {
final int backgroundColor =
startingWindowListener.mBackgroundColor == Color.TRANSPARENT
? launcher.getScrimView().getBackgroundColor()
@@ -1863,15 +1863,17 @@
return ColorUtils.setAlphaComponent(backgroundColor, 255);
};
- ActivityLaunchAnimator.Listener listener = new ActivityLaunchAnimator.Listener() {
- @Override
- public void onLaunchAnimationEnd() {
- onEndCallback.executeAllAndDestroy();
- }
- };
+ ActivityTransitionAnimator.Listener listener =
+ new ActivityTransitionAnimator.Listener() {
+ @Override
+ public void onTransitionAnimationEnd() {
+ onEndCallback.executeAllAndDestroy();
+ }
+ };
return new ContainerAnimationRunner(
- new ActivityLaunchAnimator.AnimationDelegate(controller, callback, listener));
+ new ActivityTransitionAnimator.AnimationDelegate(
+ controller, callback, listener));
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index e1b6494..37e5309 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -253,6 +253,11 @@
return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration);
}
+ public void updateTaskbarLauncherStateGoingHome() {
+ mTaskbarLauncherStateController.updateStateForFlag(FLAG_VISIBLE, true);
+ mTaskbarLauncherStateController.applyState();
+ }
+
public boolean isDraggingItem() {
return mControllers.taskbarDragController.isDragging();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 8e4a78f..9f6994a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -304,6 +304,10 @@
callbacks.addListener(mTaskBarRecentsAnimationListener);
((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(() ->
mTaskBarRecentsAnimationListener.endGestureStateOverride(true));
+
+ ((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchCancelledRunnable(() -> {
+ updateStateForUserFinishedToApp(false /* finishedToApp */);
+ });
return animatorSet;
}
@@ -770,21 +774,29 @@
mTaskBarRecentsAnimationListener = null;
((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(null);
- // Update the visible state immediately to ensure a seamless handoff
- boolean launcherVisible = !finishedToApp;
- updateStateForFlag(FLAG_TRANSITION_TO_VISIBLE, false);
- updateStateForFlag(FLAG_VISIBLE, launcherVisible);
- applyState();
-
- TaskbarStashController controller = mControllers.taskbarStashController;
- if (DEBUG) {
- Log.d(TAG, "endGestureStateOverride - FLAG_IN_APP: " + finishedToApp);
- }
- controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
- controller.applyState();
+ updateStateForUserFinishedToApp(finishedToApp);
}
}
+ /**
+ * Updates the visible state immediately to ensure a seamless handoff.
+ * @param finishedToApp True iff user is in an app.
+ */
+ private void updateStateForUserFinishedToApp(boolean finishedToApp) {
+ // Update the visible state immediately to ensure a seamless handoff
+ boolean launcherVisible = !finishedToApp;
+ updateStateForFlag(FLAG_TRANSITION_TO_VISIBLE, false);
+ updateStateForFlag(FLAG_VISIBLE, launcherVisible);
+ applyState();
+
+ TaskbarStashController controller = mControllers.taskbarStashController;
+ if (DEBUG) {
+ Log.d(TAG, "endGestureStateOverride - FLAG_IN_APP: " + finishedToApp);
+ }
+ controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
+ controller.applyState();
+ }
+
private static String getStateString(int flags) {
StringJoiner result = new StringJoiner("|");
appendFlag(result, flags, FLAG_VISIBLE, "flag_visible");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index c883759..db7d0eb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -60,8 +60,10 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
+import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
@@ -951,6 +953,15 @@
if (mActivity.isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)) {
return false;
}
+
+ // Do not stash if hardware keyboard is attached, in 3 button nav and desktop windowing mode
+ DesktopVisibilityController visibilityController =
+ LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
+ if (visibilityController != null && mActivity.isHardwareKeyboard()
+ && mActivity.isThreeButtonNav() && visibilityController.areFreeformTasksVisible()) {
+ return false;
+ }
+
return mIsImeShowing || mIsImeSwitcherShowing;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index 74ba006..d98e608 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -31,8 +31,6 @@
import android.view.Window;
import android.view.WindowManager;
-import androidx.annotation.VisibleForTesting;
-
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -53,12 +51,12 @@
private final Launcher mLauncher;
private final SystemUiProxy mSystemUiProxy;
- @VisibleForTesting final float mTouchSlop;
+ private final float mTouchSlop;
private int mLastAction;
private final SparseArray<PointF> mDownEvents;
/* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
- @VisibleForTesting boolean mCanIntercept;
+ private boolean mCanIntercept;
private boolean mIsTrackpadReverseScroll;
@@ -87,9 +85,9 @@
@Override
public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- final int action = ev.getActionMasked();
- final int idx = ev.getActionIndex();
- final int pid = ev.getPointerId(idx);
+ int action = ev.getActionMasked();
+ int idx = ev.getActionIndex();
+ int pid = ev.getPointerId(idx);
if (action == ACTION_DOWN) {
mCanIntercept = canInterceptTouch(ev);
if (!mCanIntercept) {
@@ -137,6 +135,7 @@
.log(LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN);
setWindowSlippery(false);
mIsTrackpadReverseScroll = false;
+ return true;
}
return true;
}
@@ -150,8 +149,7 @@
* Touches can slide out of the window but they cannot necessarily slide
* back in (unless the other window with touch focus permits it).
*/
- @VisibleForTesting
- void setWindowSlippery(boolean enable) {
+ private void setWindowSlippery(boolean enable) {
Window w = mLauncher.getWindow();
WindowManager.LayoutParams wlp = w.getAttributes();
if (enable) {
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 8ff43f0..c2cd11c 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -646,6 +646,18 @@
});
});
}
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ recentsView.onTaskLaunchedInLiveTileModeCancelled();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ recentsView.setTaskLaunchCancelledRunnable(null);
+ }
};
} else {
AnimatorPlaybackController controller =
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index caffab1..555bf21 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -35,6 +35,7 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.RecentsAnimationCallbacks;
@@ -150,7 +151,16 @@
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- controller.finish(true /* toRecents */, null /* onFinishComplete */,
+ controller.finish(
+ true /* toRecents */,
+ () -> {
+ LauncherTaskbarUIController controller =
+ mLauncher.getTaskbarUIController();
+ if (controller != null) {
+ controller.updateTaskbarLauncherStateGoingHome();
+ }
+
+ },
false /* sendUserLeaveHint */);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index fecbf08..9884d8d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -759,6 +759,9 @@
private RunnableList mSideTaskLaunchCallback;
@Nullable
private TaskLaunchListener mTaskLaunchListener;
+ @Nullable
+ private Runnable mOnTaskLaunchCancelledRunnable;
+
// keeps track of the state of the filter for tasks in recents view
private final RecentsFilterState mFilterState = new RecentsFilterState();
@@ -1195,6 +1198,21 @@
}
}
+ /**
+ * This is a one-time callback when touching in live tile mode. It's reset to null right
+ * after it's called.
+ */
+ public void setTaskLaunchCancelledRunnable(Runnable onTaskLaunchCancelledRunnable) {
+ mOnTaskLaunchCancelledRunnable = onTaskLaunchCancelledRunnable;
+ }
+
+ public void onTaskLaunchedInLiveTileModeCancelled() {
+ if (mOnTaskLaunchCancelledRunnable != null) {
+ mOnTaskLaunchCancelledRunnable.run();
+ mOnTaskLaunchCancelledRunnable = null;
+ }
+ }
+
private void executeSideTaskLaunchCallback() {
if (mSideTaskLaunchCallback != null) {
mSideTaskLaunchCallback.executeAllAndDestroy();
diff --git a/quickstep/tests/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchControllerTest.kt b/quickstep/tests/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchControllerTest.kt
deleted file mode 100644
index 119b862..0000000
--- a/quickstep/tests/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchControllerTest.kt
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2024 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.uioverrides.touchcontrollers
-
-import android.view.MotionEvent
-import android.view.WindowManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.launcher3.Launcher
-import com.android.launcher3.ui.AbstractLauncherUiTest
-import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertFalse
-import junit.framework.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class StatusBarTouchControllerTest : AbstractLauncherUiTest() {
- @Before
- @Throws(Exception::class)
- fun setup() {
- super.setUp()
- initialize(this)
- }
-
- @Test
- fun interceptActionDown_canIntercept() {
- executeOnLauncher { launcher ->
- val underTest = StatusBarTouchController(launcher)
- assertFalse(underTest.mCanIntercept)
- val downEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-
- underTest.onControllerInterceptTouchEvent(downEvent)
-
- assertTrue(underTest.mCanIntercept)
- }
- }
-
- @Test
- fun interceptVerticalActionMove_handledAndSetSlippery() {
- executeOnLauncher { launcher ->
- val underTest = StatusBarTouchController(launcher)
- val downEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- underTest.onControllerInterceptTouchEvent(downEvent)
- val w = launcher.window
- assertEquals(0, w.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY)
- val moveEvent =
- MotionEvent.obtain(
- 2,
- 2,
- MotionEvent.ACTION_MOVE,
- underTest.mTouchSlop,
- underTest.mTouchSlop + 10,
- 0
- )
-
- val handled = underTest.onControllerInterceptTouchEvent(moveEvent)
-
- assertTrue(handled)
- assertEquals(
- WindowManager.LayoutParams.FLAG_SLIPPERY,
- w.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY
- )
- }
- }
-
- @Test
- fun interceptHorizontalActionMove_not_handled() {
- executeOnLauncher { launcher ->
- val underTest = StatusBarTouchController(launcher)
- val downEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- underTest.onControllerInterceptTouchEvent(downEvent)
- val moveEvent =
- MotionEvent.obtain(
- 2,
- 2,
- MotionEvent.ACTION_MOVE,
- underTest.mTouchSlop + 10,
- underTest.mTouchSlop,
- 0
- )
-
- val handled = underTest.onControllerInterceptTouchEvent(moveEvent)
-
- assertFalse(handled)
- }
- }
-
- @Test
- fun interceptActionMoveAsFirstGestureEvent_notCrashedNorHandled() {
- executeOnLauncher { launcher ->
- val underTest = StatusBarTouchController(launcher)
- underTest.mCanIntercept = true
- val moveEvent = MotionEvent.obtain(2, 2, MotionEvent.ACTION_MOVE, 10f, 10f, 0)
-
- val handled = underTest.onControllerInterceptTouchEvent(moveEvent)
-
- assertFalse(handled)
- }
- }
-
- @Test
- fun handleActionUp_setNotSlippery() {
- executeOnLauncher { launcher: Launcher ->
- val underTest = StatusBarTouchController(launcher)
- underTest.mCanIntercept = true
- underTest.setWindowSlippery(true)
- val moveEvent = MotionEvent.obtain(2, 2, MotionEvent.ACTION_UP, 10f, 10f, 0)
-
- val handled = underTest.onControllerTouchEvent(moveEvent)
-
- assertTrue(handled)
- assertEquals(
- 0,
- launcher.window.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY
- )
- }
- }
-
- @Test
- fun handleActionCancel_setNotSlippery() {
- executeOnLauncher { launcher ->
- val underTest = StatusBarTouchController(launcher)
- underTest.mCanIntercept = true
- underTest.setWindowSlippery(true)
- val moveEvent = MotionEvent.obtain(2, 2, MotionEvent.ACTION_CANCEL, 10f, 10f, 0)
-
- val handled = underTest.onControllerTouchEvent(moveEvent)
-
- assertTrue(handled)
- assertEquals(
- 0,
- launcher.window.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY
- )
- }
- }
-}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index c472ef2..b7546c7 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -109,7 +109,7 @@
// We're staying in all apps, use same instance
mLauncher.getAllApps()
.getAppIcon(CALCULATOR_APP_NAME)
- .launch(CALCULATOR_APP_PACKAGE);
+ .launchIntoSplitScreen();
} else {
// We're in overview, use taskbar instance
mLauncher.getLaunchedAppState()
diff --git a/res/drawable/ic_encrypted_with_background.xml b/res/drawable/ic_private_space_with_background.xml
similarity index 65%
rename from res/drawable/ic_encrypted_with_background.xml
rename to res/drawable/ic_private_space_with_background.xml
index c439b55..59a33dd 100644
--- a/res/drawable/ic_encrypted_with_background.xml
+++ b/res/drawable/ic_private_space_with_background.xml
@@ -21,6 +21,9 @@
android:pathData="M48 24A24 24 0 0 1 0 24A24 24 0 0 1 48 24Z"
android:fillColor="?attr/materialColorOutlineVariant" />
<path
- android:pathData="M24.0002 10.667L13.3335 14.667V22.787C13.3335 29.5203 17.8802 35.8003 24.0002 37.3337C30.1202 35.8003 34.6668 29.5203 34.6668 22.787V14.667L24.0002 10.667ZM32.0002 22.787C32.0002 28.1203 28.6002 33.0537 24.0002 34.5603C19.4002 33.0537 16.0002 28.1337 16.0002 22.787V16.5203L24.0002 13.5203L32.0002 16.5203V22.787ZM25.2402 23.6937L26.0002 28.0003H22.0002L22.7602 23.6937C21.9068 23.2537 21.3335 22.3603 21.3335 21.3337C21.3335 19.867 22.5335 18.667 24.0002 18.667C25.4668 18.667 26.6668 19.867 26.6668 21.3337C26.6668 22.3603 26.0935 23.2537 25.2402 23.6937Z"
+ android:pathData="M33.3333 14.6667V33.3333H14.6667V14.6667H33.3333ZM33.3333 12H14.6667C13.2 12 12 13.2 12 14.6667V33.3333C12 34.8 13.2 36 14.6667 36H33.3333C34.8 36 36 34.8 36 33.3333V14.6667C36 13.2 34.8 12 33.3333 12Z"
+ android:fillColor="?attr/materialColorOnSurface" />
+ <path
+ android:pathData="M25.2397 24.3597L25.9997 28.6663H21.9997L22.7597 24.3597C21.9063 23.9197 21.333 23.0263 21.333 21.9997C21.333 20.533 22.533 19.333 23.9997 19.333C25.4663 19.333 26.6663 20.533 26.6663 21.9997C26.6663 23.0263 26.093 23.9197 25.2397 24.3597Z"
android:fillColor="?attr/materialColorOnSurface" />
</vector>
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index 47bf9e7..009359c 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -45,7 +45,7 @@
android:visibility="gone"
android:textSize="18sp"
android:layout_below="@id/search_and_recommendations_container"
- tools:text="No widgets available" />
+ tools:text="@string/no_widgets_available" />
<!-- Fast scroller popup -->
<TextView
diff --git a/res/layout/widgets_list_row_header_two_pane.xml b/res/layout/widgets_list_row_header_two_pane.xml
index c0a6ea8..bdb2aed 100644
--- a/res/layout/widgets_list_row_header_two_pane.xml
+++ b/res/layout/widgets_list_row_header_two_pane.xml
@@ -23,6 +23,7 @@
android:importantForAccessibility="yes"
android:focusable="true"
launcher:appIconSize="48dp"
+ launcher:collapsable="false"
android:descendantFocusability="afterDescendants"
android:background="@drawable/bg_widgets_header_two_pane" >
diff --git a/res/layout/widgets_two_pane_sheet.xml b/res/layout/widgets_two_pane_sheet.xml
index 01c1b10..cd7f2e1 100644
--- a/res/layout/widgets_two_pane_sheet.xml
+++ b/res/layout/widgets_two_pane_sheet.xml
@@ -47,6 +47,16 @@
android:textColor="?attr/widgetPickerTitleColor"
android:textSize="24sp" />
+ <TextView
+ android:id="@+id/no_widgets_text"
+ style="@style/PrimaryHeadline"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textSize="18sp"
+ android:visibility="gone"
+ tools:text="@string/no_widgets_available" />
+
<LinearLayout
android:id="@+id/linear_layout_container"
android:layout_width="match_parent"
@@ -57,6 +67,9 @@
android:id="@+id/recycler_view_container"
android:layout_width="0dp"
android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingBottom="24dp"
android:layout_gravity="start"
android:layout_weight="0.33">
<TextView
@@ -90,15 +103,6 @@
android:gravity="end"
android:layout_gravity="end"
android:orientation="horizontal">
- <TextView
- android:id="@+id/no_widgets_text"
- style="@style/PrimaryHeadline"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:textSize="18sp"
- android:visibility="gone"
- tools:text="No widgets available" />
<ScrollView
android:id="@+id/right_pane_scroll_view"
android:layout_width="match_parent"
diff --git a/res/layout/widgets_two_pane_sheet_paged_view.xml b/res/layout/widgets_two_pane_sheet_paged_view.xml
index 4a7749b..887efb8 100644
--- a/res/layout/widgets_two_pane_sheet_paged_view.xml
+++ b/res/layout/widgets_two_pane_sheet_paged_view.xml
@@ -22,6 +22,8 @@
android:gravity="start"
android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
android:layout_gravity="start"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_alignParentStart="true">
<com.android.launcher3.widget.picker.WidgetPagedView
android:id="@+id/widgets_view_pager"
diff --git a/res/layout/widgets_two_pane_sheet_recyclerview.xml b/res/layout/widgets_two_pane_sheet_recyclerview.xml
index 8b48abb..f3d3b16 100644
--- a/res/layout/widgets_two_pane_sheet_recyclerview.xml
+++ b/res/layout/widgets_two_pane_sheet_recyclerview.xml
@@ -21,6 +21,7 @@
android:layout_height="match_parent"
android:gravity="start"
android:layout_gravity="start"
+ android:clipChildren="false"
android:layout_alignParentStart="true">
<com.android.launcher3.widget.picker.WidgetsRecyclerView
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 754f0cb..4a0b5e8 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -582,6 +582,7 @@
<declare-styleable name="WidgetsListRowHeader">
<attr name="appIconSize" format="dimension" />
+ <attr name="collapsable" format="boolean" />
</declare-styleable>
<attr name="materialColorOnSecondaryFixedVariant" format="color" />
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index baa1ee3..2f0c096 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -213,6 +213,7 @@
mIsRtl = (getResources().getConfiguration().getLayoutDirection()
== View.LAYOUT_DIRECTION_RTL);
mDeviceProfile = mActivity.getDeviceProfile();
+ mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
final int defaultIconSize;
@@ -243,7 +244,6 @@
defaultIconSize = mDeviceProfile.iconSizePx;
}
- mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride,
defaultIconSize);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index e9545c8..bf4f6c3 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -1010,7 +1010,7 @@
* Returns the amount of extra (or unused) vertical space.
*/
private int updateAvailableDimensions(Resources res) {
- iconCenterVertically = mIsScalableGrid || mIsResponsiveGrid;
+ iconCenterVertically = (mIsScalableGrid || mIsResponsiveGrid) && isVerticalBarLayout();
if (mIsResponsiveGrid) {
iconSizePx = mResponsiveWorkspaceCellSpec.getIconSize();
@@ -1738,15 +1738,8 @@
// The hotseat icons will be placed in the middle of the hotseat cells.
// Changing the hotseatCellHeightPx is not affecting hotseat icon positions
// in vertical bar layout.
- // Workspace icons are moved up by a small factor. The variable diffOverlapFactor
- // is set to account for that difference.
- float diffOverlapFactor = mIsResponsiveGrid ? 0
- : iconSizePx * (ICON_OVERLAP_FACTOR - 1) / 2;
-
- int paddingTop = Math.max((int) (mInsets.top + cellLayoutPaddingPx.top
- - diffOverlapFactor), 0);
- int paddingBottom = Math.max((int) (mInsets.bottom + cellLayoutPaddingPx.bottom
- + diffOverlapFactor), 0);
+ int paddingTop = Math.max((int) (mInsets.top + cellLayoutPaddingPx.top), 0);
+ int paddingBottom = Math.max((int) (mInsets.bottom + cellLayoutPaddingPx.bottom), 0);
if (isSeascape()) {
hotseatBarPadding.set(mInsets.left + mHotseatBarEdgePaddingPx, paddingTop,
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index ae2849e..6acfcd0 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -80,6 +80,7 @@
import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusedItemDecorator;
+import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.UserCache;
@@ -1535,7 +1536,11 @@
// No animations will occur when changes occur to the items in this RecyclerView.
mRecyclerView.setItemAnimator(null);
onInitializeRecyclerView(mRecyclerView);
- FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mRecyclerView);
+ // Use ViewGroupFocusHelper for SearchRecyclerView to draw focus outline for the
+ // buttons in the view (e.g. query builder button and setting button)
+ FocusedItemDecorator focusedItemDecorator = isSearch() ? new FocusedItemDecorator(
+ new ViewGroupFocusHelper(mRecyclerView)) : new FocusedItemDecorator(
+ mRecyclerView);
mRecyclerView.addItemDecoration(focusedItemDecorator);
mOnFocusChangeListener = focusedItemDecorator.getFocusListener();
mAdapter.setIconFocusListener(mOnFocusChangeListener);
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index f058ae4..284b31e 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -29,6 +29,7 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
@@ -633,6 +634,20 @@
}
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ boolean shouldCenterIcon = mActivity.getDeviceProfile().iconCenterVertically;
+ if (shouldCenterIcon) {
+ int iconSize = mActivity.getDeviceProfile().iconSizePx;
+ Paint.FontMetrics fm = mFolderName.getPaint().getFontMetrics();
+ int cellHeightPx = iconSize + mFolderName.getCompoundDrawablePadding()
+ + (int) Math.ceil(fm.bottom - fm.top);
+ setPadding(getPaddingLeft(), (MeasureSpec.getSize(heightMeasureSpec)
+ - cellHeightPx) / 2, getPaddingRight(), getPaddingBottom());
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
/** Sets the visibility of the icon's title text */
public void setTextVisible(boolean visible) {
if (visible) {
diff --git a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
index 2476a6f..5e2832f 100644
--- a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
+++ b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
@@ -20,12 +20,12 @@
import android.view.View;
import android.view.View.OnFocusChangeListener;
-import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
-
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
import androidx.recyclerview.widget.RecyclerView.State;
+import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
+
/**
* {@link ItemDecoration} for drawing and animating focused view background.
*/
@@ -37,12 +37,17 @@
mHelper = new SimpleFocusIndicatorHelper(container);
}
+ public FocusedItemDecorator(FocusIndicatorHelper focusIndicatorHelper) {
+ mHelper = focusIndicatorHelper;
+ }
+
public OnFocusChangeListener getFocusListener() {
return mHelper;
}
@Override
- public void onDraw(Canvas c, RecyclerView parent, State state) {
+ public void onDrawOver(Canvas c, RecyclerView parent, State state) {
+ // Use onDrawOver so focus outline is always visible
mHelper.draw(c);
}
}
diff --git a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
index fde220c..f9bd343 100644
--- a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
+++ b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
@@ -50,10 +50,18 @@
}
private void computeLocationRelativeToContainer(View child, Rect outRect) {
- View parent = (View) child.getParent();
+ if (child == null) {
+ return;
+ }
+
outRect.left += child.getX();
outRect.top += child.getY();
+ if (child.getParent() == null || !(child.getParent() instanceof View)) {
+ return;
+ }
+
+ View parent = (View) child.getParent();
if (parent != mContainer) {
if (parent instanceof PagedView) {
PagedView page = (PagedView) parent;
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index fbbfea9..0af7e67 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -22,9 +22,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Flags;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.SecondaryDropTarget;
import com.android.launcher3.Utilities;
@@ -49,9 +47,10 @@
* onClickListener that depends on the item that the shortcut services.
*
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
- * @param <T>
+ *
+ * @param <T> extends {@link ActivityContext}
*/
-public abstract class SystemShortcut<T extends Context & ActivityContext> extends ItemInfo
+public abstract class SystemShortcut<T extends ActivityContext> extends ItemInfo
implements View.OnClickListener {
private final int mIconResId;
@@ -100,24 +99,25 @@
return mAccessibilityActionId == action;
}
- public interface Factory<T extends Context & ActivityContext> {
+ public interface Factory<T extends ActivityContext> {
- @Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo, View originalView);
+ @Nullable
+ SystemShortcut<T> getShortcut(T context, ItemInfo itemInfo, @NonNull View originalView);
}
- public static final Factory<Launcher> WIDGETS = (launcher, itemInfo, originalView) -> {
+ public static final Factory<ActivityContext> WIDGETS = (context, itemInfo, originalView) -> {
if (itemInfo.getTargetComponent() == null) return null;
final List<WidgetItem> widgets =
- launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
+ context.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
if (widgets.isEmpty()) {
return null;
}
- return new Widgets(launcher, itemInfo, originalView);
+ return new Widgets(context, itemInfo, originalView);
};
- public static class Widgets extends SystemShortcut<Launcher> {
- public Widgets(Launcher target, ItemInfo itemInfo, View originalView) {
+ public static class Widgets<T extends ActivityContext> extends SystemShortcut<T> {
+ public Widgets(T target, ItemInfo itemInfo, @NonNull View originalView) {
super(R.drawable.ic_widget, R.string.widget_button_text, target, itemInfo,
originalView);
}
@@ -134,14 +134,14 @@
}
}
- public static final Factory<BaseDraggingActivity> APP_INFO = AppInfo::new;
+ public static final Factory<ActivityContext> APP_INFO = AppInfo::new;
- public static class AppInfo<T extends Context & ActivityContext> extends SystemShortcut<T> {
+ public static class AppInfo<T extends ActivityContext> extends SystemShortcut<T> {
@Nullable
private SplitAccessibilityInfo mSplitA11yInfo;
- public AppInfo(T target, ItemInfo itemInfo, View originalView) {
+ public AppInfo(T target, ItemInfo itemInfo, @NonNull View originalView) {
super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
itemInfo, originalView);
}
@@ -180,7 +180,7 @@
public void onClick(View view) {
dismissTaskMenuView(mTarget);
Rect sourceBounds = Utilities.getViewBounds(view);
- new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
+ new PackageManagerHelper(view.getContext()).startDetailsActivityForInfo(
mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
.log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP);
@@ -200,8 +200,11 @@
}
}
- public static final Factory<Launcher> PRIVATE_PROFILE_INSTALL =
- (launcher, itemInfo, originalView) -> {
+ public static final Factory<ActivityContext> PRIVATE_PROFILE_INSTALL =
+ (context, itemInfo, originalView) -> {
+ if (originalView == null) {
+ return null;
+ }
if (itemInfo.getTargetComponent() == null
|| !(itemInfo instanceof com.android.launcher3.model.data.AppInfo)
|| !itemInfo.getContainerInfo().hasAllAppsContainer()
@@ -210,7 +213,7 @@
}
PrivateProfileManager privateProfileManager =
- launcher.getAppsView().getPrivateProfileManager();
+ context.getAppsView().getPrivateProfileManager();
if (privateProfileManager == null || !privateProfileManager.isEnabled()) {
return null;
}
@@ -221,30 +224,28 @@
}
// Do not show shortcut if an app is already installed to the space
ComponentName targetComponent = itemInfo.getTargetComponent();
- if (launcher.getAppsView()
- .getAppsStore()
- .getApp(new ComponentKey(targetComponent, privateProfileUser))
- != null) {
+ if (context.getAppsView().getAppsStore().getApp(
+ new ComponentKey(targetComponent, privateProfileUser)) != null) {
return null;
}
// Do not show shortcut for settings
String[] packagesToSkip =
- launcher.getResources()
+ originalView.getContext().getResources()
.getStringArray(R.array.skip_private_profile_shortcut_packages);
if (Arrays.asList(packagesToSkip).contains(targetComponent.getPackageName())) {
return null;
}
- return new InstallToPrivateProfile(
- launcher, itemInfo, originalView, privateProfileUser);
+ return new InstallToPrivateProfile<>(
+ context, itemInfo, originalView, privateProfileUser);
};
- static class InstallToPrivateProfile extends SystemShortcut<Launcher> {
+ static class InstallToPrivateProfile<T extends ActivityContext> extends SystemShortcut<T> {
UserHandle mSpaceUser;
- InstallToPrivateProfile(
- Launcher target, ItemInfo itemInfo, View originalView, UserHandle spaceUser) {
+ InstallToPrivateProfile(T target, ItemInfo itemInfo, @NonNull View originalView,
+ UserHandle spaceUser) {
// TODO(b/302666597): update icon once available
super(
R.drawable.ic_install_to_private,
@@ -271,8 +272,11 @@
}
}
- public static final Factory<BaseDraggingActivity> INSTALL =
+ public static final Factory<ActivityContext> INSTALL =
(activity, itemInfo, originalView) -> {
+ if (originalView == null) {
+ return null;
+ }
boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo)
&& ((WorkspaceItemInfo) itemInfo).hasStatusFlag(
WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
@@ -280,18 +284,19 @@
if (itemInfo instanceof com.android.launcher3.model.data.AppInfo) {
com.android.launcher3.model.data.AppInfo
appInfo = (com.android.launcher3.model.data.AppInfo) itemInfo;
- isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
+ isInstantApp = InstantAppResolver.newInstance(
+ originalView.getContext()).isInstantApp(appInfo);
}
boolean enabled = supportsWebUI || isInstantApp;
if (!enabled) {
return null;
}
return new Install(activity, itemInfo, originalView);
- };
+ };
- public static class Install extends SystemShortcut<BaseDraggingActivity> {
+ public static class Install<T extends ActivityContext> extends SystemShortcut<T> {
- public Install(BaseDraggingActivity target, ItemInfo itemInfo, View originalView) {
+ public Install(T target, ItemInfo itemInfo, @NonNull View originalView) {
super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label,
target, itemInfo, originalView);
}
@@ -306,16 +311,16 @@
}
}
- public static final Factory<Launcher> DONT_SUGGEST_APP = (activity, itemInfo, originalView) -> {
- if (!itemInfo.isPredictedItem()) {
- return null;
- }
- return new DontSuggestApp(activity, itemInfo, originalView);
- };
+ public static final Factory<ActivityContext> DONT_SUGGEST_APP =
+ (activity, itemInfo, originalView) -> {
+ if (!itemInfo.isPredictedItem()) {
+ return null;
+ }
+ return new DontSuggestApp<>(activity, itemInfo, originalView);
+ };
- private static class DontSuggestApp extends SystemShortcut<Launcher> {
- DontSuggestApp(Launcher target, ItemInfo itemInfo,
- View originalView) {
+ private static class DontSuggestApp<T extends ActivityContext> extends SystemShortcut<T> {
+ DontSuggestApp(T target, ItemInfo itemInfo, View originalView) {
super(R.drawable.ic_block_no_shadow, R.string.dismiss_prediction_label, target,
itemInfo, originalView);
}
@@ -329,36 +334,36 @@
}
}
- public static final Factory<Launcher> UNINSTALL_APP = (activity, itemInfo, originalView) -> {
- if (!Flags.enablePrivateSpace()) {
- return null;
- }
- if (!UserCache.getInstance(activity.getApplicationContext()).getUserInfo(
- itemInfo.user).isPrivate()) {
- // If app is not Private Space app.
- return null;
- }
- ComponentName cn = SecondaryDropTarget.getUninstallTarget(activity.getApplicationContext(),
- itemInfo);
- if (cn == null) {
- // If component name is null, don't show uninstall shortcut.
- // System apps will have component name as null.
- return null;
- }
- return new UninstallApp(activity, itemInfo, originalView, cn);
- };
+ public static final Factory<ActivityContext> UNINSTALL_APP =
+ (activityContext, itemInfo, originalView) -> {
+ if (originalView == null) {
+ return null;
+ }
+ if (!Flags.enablePrivateSpace()) {
+ return null;
+ }
+ if (!UserCache.INSTANCE.get(originalView.getContext()).getUserInfo(
+ itemInfo.user).isPrivate()) {
+ // If app is not Private Space app.
+ return null;
+ }
+ ComponentName cn = SecondaryDropTarget.getUninstallTarget(originalView.getContext(),
+ itemInfo);
+ if (cn == null) {
+ // If component name is null, don't show uninstall shortcut.
+ // System apps will have component name as null.
+ return null;
+ }
+ return new UninstallApp(activityContext, itemInfo, originalView, cn);
+ };
- private static class UninstallApp extends SystemShortcut<Launcher> {
- private static final String TAG = "UninstallApp";
- Context mContext;
- @NonNull
- ComponentName mComponentName;
+ private static class UninstallApp<T extends ActivityContext> extends SystemShortcut<T> {
+ @NonNull ComponentName mComponentName;
- UninstallApp(Launcher target, ItemInfo itemInfo, View originalView,
+ UninstallApp(T target, ItemInfo itemInfo, @NonNull View originalView,
@NonNull ComponentName cn) {
super(R.drawable.ic_uninstall_no_shadow, R.string.uninstall_drop_target_label, target,
itemInfo, originalView);
- mContext = target.getApplicationContext();
mComponentName = cn;
}
@@ -366,7 +371,7 @@
@Override
public void onClick(View view) {
dismissTaskMenuView(mTarget);
- SecondaryDropTarget.performUninstall(mContext, mComponentName, mItemInfo);
+ SecondaryDropTarget.performUninstall(view.getContext(), mComponentName, mItemInfo);
mTarget.getStatsLogManager()
.logger()
.withItemInfo(mItemInfo)
@@ -374,8 +379,8 @@
}
}
- public static <T extends Context & ActivityContext> void dismissTaskMenuView(T activity) {
+ public static <T extends ActivityContext> void dismissTaskMenuView(T activity) {
AbstractFloatingView.closeOpenViews(activity, true,
- AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
+ AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
}
}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 5636405..1623ad8 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -18,6 +18,8 @@
import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
+import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.app.Activity;
@@ -208,6 +210,11 @@
return response;
}
+ case TestProtocol.REQUEST_GET_SPLIT_SELECTION_ACTIVE:
+ response.putBoolean(TEST_INFO_RESPONSE_FIELD, enableSplitContextually()
+ && Launcher.ACTIVITY_TRACKER.getCreatedActivity().isSplitSelectionActive());
+ return response;
+
case TestProtocol.REQUEST_ENABLE_ROTATION:
MAIN_EXECUTOR.submit(() ->
Launcher.ACTIVITY_TRACKER.getCreatedActivity().getRotationHelper()
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index ded4da6..4abefc7 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -40,6 +40,7 @@
import android.widget.Toast;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.BuildConfig;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
@@ -352,18 +353,18 @@
private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
- Intent intent;
- if (item instanceof ItemInfoWithIcon
- && (((ItemInfoWithIcon) item).runtimeStatusFlags
- & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
- ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
- intent = ApiWrapper.getAppMarketActivityIntent(launcher,
- appInfo.getTargetComponent().getPackageName(), Process.myUserHandle());
- } else {
- intent = item.getIntent();
- if (item instanceof AppInfo
- && (((ItemInfoWithIcon) item).runtimeStatusFlags
+ Intent intent = item.getIntent();
+ if (item instanceof ItemInfoWithIcon itemInfoWithIcon) {
+ if ((itemInfoWithIcon.runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ intent = ApiWrapper.getAppMarketActivityIntent(launcher,
+ itemInfoWithIcon.getTargetComponent().getPackageName(),
+ Process.myUserHandle());
+ } else if ((itemInfoWithIcon.runtimeStatusFlags
& ItemInfoWithIcon.FLAG_PRIVATE_SPACE_INSTALL_APP) != 0) {
+ intent = ApiWrapper.getAppMarketActivityIntent(launcher,
+ BuildConfig.APPLICATION_ID,
+ launcher.getAppsView().getPrivateProfileManager().getProfileUser());
launcher.getStatsLogManager().logger().log(
LAUNCHER_PRIVATE_SPACE_INSTALL_APP_BUTTON_TAP);
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 17d9276..28bae59 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -173,6 +173,7 @@
protected WidgetsSearchBar mSearchBar;
protected TextView mHeaderTitle;
protected RecyclerViewFastScroller mFastScroller;
+ protected int mBottomPadding;
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -370,15 +371,16 @@
@Override
public void setInsets(Rect insets) {
super.setInsets(insets);
- int bottomPadding = Math.max(insets.bottom, mNavBarScrimHeight);
- setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, bottomPadding);
- setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, bottomPadding);
+ mBottomPadding = Math.max(insets.bottom, mNavBarScrimHeight);
+ setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, mBottomPadding);
+ setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, mBottomPadding);
if (mHasWorkProfile) {
- setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, bottomPadding);
+ setBottomPadding(mAdapters.get(AdapterHolder.WORK)
+ .mWidgetsRecyclerView, mBottomPadding);
}
- ((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = bottomPadding;
+ ((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = mBottomPadding;
- if (bottomPadding > 0) {
+ if (mBottomPadding > 0) {
setupNavBarColor();
} else {
clearNavBarColor();
@@ -387,6 +389,15 @@
requestLayout();
}
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ WindowInsets w = super.onApplyWindowInsets(insets);
+ if (mInsets.bottom != mNavBarScrimHeight) {
+ setInsets(mInsets);
+ }
+ return w;
+ }
+
private void setBottomPadding(RecyclerView recyclerView, int bottomPadding) {
recyclerView.setPadding(
recyclerView.getPaddingLeft(),
@@ -791,8 +802,9 @@
if (mDeviceProfile.isLandscape != dp.isLandscape && dp.isTablet && !dp.isTwoPanels) {
handleClose(false);
show(BaseActivity.fromContext(getContext()), false);
- } else {
+ } else if (!isTwoPane()) {
reset();
+ resetExpandedHeaders();
}
// When folding/unfolding the foldables, we need to switch between the regular widget picker
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index b5e7401..3383299 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -52,7 +52,11 @@
private static final int[] EXPANDED_DRAWABLE_STATE = new int[] {android.R.attr.state_expanded};
private final int mIconSize;
-
+ /**
+ * Indicates if the header is collapsable. For example, when displayed in a two pane layout,
+ * widget apps aren't collapsable.
+ */
+ private final boolean mIsCollapsable;
@Nullable private HandlerRunnable mIconLoadRequest;
@Nullable private Drawable mIconDrawable;
@Nullable private WidgetsListDrawableState mListDrawableState;
@@ -79,6 +83,7 @@
R.styleable.WidgetsListRowHeader, defStyleAttr, /* defStyleRes= */ 0);
mIconSize = a.getDimensionPixelSize(R.styleable.WidgetsListRowHeader_appIconSize,
grid.iconSizePx);
+ mIsCollapsable = a.getBoolean(R.styleable.WidgetsListRowHeader_collapsable, true);
}
@Override
@@ -87,32 +92,36 @@
mAppIcon = findViewById(R.id.app_icon);
mTitle = findViewById(R.id.app_title);
mSubtitle = findViewById(R.id.app_subtitle);
- setAccessibilityDelegate(new AccessibilityDelegate() {
+ // Lists that cannot collapse, don't need EXPAND or COLLAPSE accessibility actions.
+ if (mIsCollapsable) {
+ setAccessibilityDelegate(new AccessibilityDelegate() {
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- if (mIsExpanded) {
- info.removeAction(AccessibilityNodeInfo.ACTION_EXPAND);
- info.addAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
- } else {
- info.removeAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
- info.addAction(AccessibilityNodeInfo.ACTION_EXPAND);
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ if (mIsExpanded) {
+ info.removeAction(AccessibilityNodeInfo.ACTION_EXPAND);
+ info.addAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
+ } else {
+ info.removeAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
+ info.addAction(AccessibilityNodeInfo.ACTION_EXPAND);
+ }
+ super.onInitializeAccessibilityNodeInfo(host, info);
}
- super.onInitializeAccessibilityNodeInfo(host, info);
- }
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- switch (action) {
- case AccessibilityNodeInfo.ACTION_EXPAND:
- case AccessibilityNodeInfo.ACTION_COLLAPSE:
- callOnClick();
- return true;
- default:
- return super.performAccessibilityAction(host, action, args);
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_EXPAND:
+ case AccessibilityNodeInfo.ACTION_COLLAPSE:
+ callOnClick();
+ return true;
+ default:
+ return super.performAccessibilityAction(host, action, args);
+ }
}
- }
- });
+ });
+ }
}
/** Sets the expand toggle to expand / collapse. */
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index 26c04f5..5a1ec87 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Outline;
+import android.graphics.Rect;
import android.os.Process;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -58,11 +59,13 @@
private FrameLayout mSuggestedWidgetsContainer;
private WidgetsListHeader mSuggestedWidgetsHeader;
+ private PackageUserKey mSuggestedWidgetsPackageUserKey;
private LinearLayout mRightPane;
private ScrollView mRightPaneScrollView;
private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
private int mActivePage = -1;
+ private PackageUserKey mSelectedHeader;
private final ViewOutlineProvider mViewOutlineProviderRightPane = new ViewOutlineProvider() {
@Override
@@ -124,6 +127,7 @@
mFastScroller.setVisibility(GONE);
}
+ /** Overrides onConfigurationChanged method from WidgetsFullSheet. Needed for b/319150904 */
@Override
protected void onConfigurationChanged(Configuration newConfig) {}
@@ -147,6 +151,23 @@
}
layoutParams.weight = layoutParams.width == 0 ? 0.33F : 0;
leftPane.setLayoutParams(layoutParams);
+ requestApplyInsets();
+ if (mSelectedHeader != null) {
+ if (mSelectedHeader.equals(mSuggestedWidgetsPackageUserKey)) {
+ mSuggestedWidgetsHeader.callOnClick();
+ } else {
+ getHeaderChangeListener().onHeaderChanged(mSelectedHeader);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onWidgetsBound() {
+ super.onWidgetsBound();
+ if (!mHasRecommendedWidgets && mSelectedHeader == null) {
+ mAdapters.get(mActivePage).mWidgetsListAdapter.selectFirstHeaderEntry();
+ mAdapters.get(mActivePage).mWidgetsRecyclerView.scrollToTop();
}
}
@@ -195,6 +216,8 @@
mRightPane.removeAllViews();
mRightPane.addView(mRecommendedWidgetsTable);
mRightPaneScrollView.setScrollY(0);
+ mSuggestedWidgetsPackageUserKey = PackageUserKey.fromPackageItemInfo(packageItemInfo);
+ mSelectedHeader = mSuggestedWidgetsPackageUserKey;
});
mSuggestedWidgetsContainer.addView(mSuggestedWidgetsHeader);
}
@@ -273,6 +296,7 @@
return new HeaderChangeListener() {
@Override
public void onHeaderChanged(@NonNull PackageUserKey selectedHeader) {
+ mSelectedHeader = selectedHeader;
WidgetsListContentEntry contentEntry = mActivityContext.getPopupDataProvider()
.getSelectedAppWidgets(selectedHeader);
@@ -303,6 +327,18 @@
}
@Override
+ public void setInsets(Rect insets) {
+ super.setInsets(insets);
+ FrameLayout rightPaneContainer = mContent.findViewById(R.id.right_pane_container);
+ rightPaneContainer.setPadding(
+ rightPaneContainer.getPaddingLeft(),
+ rightPaneContainer.getPaddingTop(),
+ rightPaneContainer.getPaddingRight(),
+ mBottomPadding);
+ requestLayout();
+ }
+
+ @Override
protected int getWidgetListHorizontalMargin() {
return getResources().getDimensionPixelSize(
R.dimen.widget_list_left_pane_horizontal_margin);
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
index 920ba6f..361247b 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
@@ -80,8 +80,8 @@
hotseatQsbSpace: 0.0px (0.0dp)
hotseatQsbHeight: 0.0px (0.0dp)
springLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)
- getHotseatLayoutPadding(context).top: 64.0px (24.380953dp)
- getHotseatLayoutPadding(context).bottom: 112.0px (42.666668dp)
+ getHotseatLayoutPadding(context).top: 74.0px (28.190475dp)
+ getHotseatLayoutPadding(context).bottom: 103.0px (39.238094dp)
getHotseatLayoutPadding(context).left: 42.0px (16.0dp)
getHotseatLayoutPadding(context).right: 63.0px (24.0dp)
numShownHotseatIcons: 5
@@ -123,8 +123,8 @@
dropTargetBarSizePx: 95.0px (36.190475dp)
dropTargetBarBottomMarginPx: 16.0px (6.095238dp)
getCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)
- getCellLayoutSpringLoadShrunkBottom(): 952.0px (362.66666dp)
+ getCellLayoutSpringLoadShrunkBottom(): 961.0px (366.09525dp)
workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
- getWorkspaceSpringLoadScale(): 0.79639447px (0.30338836dp)
+ getWorkspaceSpringLoadScale(): 0.8059385px (0.30702418dp)
getCellLayoutHeight(): 943.0px (359.2381dp)
getCellLayoutWidth(): 2073.0px (789.7143dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
index 65460ec..d93ec58 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
@@ -80,8 +80,8 @@
hotseatQsbSpace: 0.0px (0.0dp)
hotseatQsbHeight: 0.0px (0.0dp)
springLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)
- getHotseatLayoutPadding(context).top: 64.0px (24.380953dp)
- getHotseatLayoutPadding(context).bottom: 49.0px (18.666666dp)
+ getHotseatLayoutPadding(context).top: 74.0px (28.190475dp)
+ getHotseatLayoutPadding(context).bottom: 40.0px (15.238095dp)
getHotseatLayoutPadding(context).left: 42.0px (16.0dp)
getHotseatLayoutPadding(context).right: 189.0px (72.0dp)
numShownHotseatIcons: 5
@@ -123,8 +123,8 @@
dropTargetBarSizePx: 95.0px (36.190475dp)
dropTargetBarBottomMarginPx: 16.0px (6.095238dp)
getCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)
- getCellLayoutSpringLoadShrunkBottom(): 1008.0px (384.0dp)
+ getCellLayoutSpringLoadShrunkBottom(): 1017.0px (387.42856dp)
workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
- getWorkspaceSpringLoadScale(): 0.8021869px (0.305595dp)
+ getWorkspaceSpringLoadScale(): 0.8111332px (0.3090031dp)
getCellLayoutHeight(): 1006.0px (383.2381dp)
getCellLayoutWidth(): 1947.0px (741.7143dp)
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 2f9945d..8d40ff2 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -150,6 +150,7 @@
public static final String REQUEST_GET_OVERVIEW_PAGE_SPACING = "get-overview-page-spacing";
public static final String REQUEST_GET_OVERVIEW_CURRENT_PAGE_INDEX =
"get-overview-current-page-index";
+ public static final String REQUEST_GET_SPLIT_SELECTION_ACTIVE = "get-split-selection-active";
public static final String REQUEST_ENABLE_ROTATION = "enable_rotation";
public static final String REQUEST_ENABLE_SUGGESTION = "enable-suggestion";
public static final String REQUEST_MODEL_QUEUE_CLEARED = "model-queue-cleared";
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java b/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
new file mode 100644
index 0000000..3f37563
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
+
+import android.content.ContextWrapper;
+
+import androidx.annotation.Nullable;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.popup.PopupDataProvider;
+
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * {@link ContextWrapper} around {@link ActivityContextWrapper} with internal Launcher interface for
+ * testing.
+ *
+ * There are 2 constructors in this class. The base context can be {@link SandboxContext} or
+ * Instrumentation target context.
+ * Using {@link SandboxContext} as base context allows custom implementations for
+ * MainThreadInitializedObject providers.
+ */
+
+public class TestSandboxModelContextWrapper extends ActivityContextWrapper implements
+ BgDataModel.Callbacks {
+
+ protected AllAppsStore<ActivityContextWrapper> mAllAppsStore;
+ protected AlphabeticalAppsList<ActivityContextWrapper> mAppsList;
+
+ public final CountDownLatch mBindCompleted = new CountDownLatch(1);
+
+ protected ActivityAllAppsContainerView<ActivityContextWrapper> mAppsView;
+
+ private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {});
+ protected final UserCache mUserCache;
+
+ public TestSandboxModelContextWrapper(SandboxContext base) {
+ super(base);
+ mUserCache = base.getObject(UserCache.INSTANCE);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
+ mAppsView = new ActivityAllAppsContainerView<>(this));
+ mAppsList = mAppsView.getPersonalAppList();
+ mAllAppsStore = mAppsView.getAppsStore();
+ }
+
+ public TestSandboxModelContextWrapper() {
+ super(getInstrumentation().getTargetContext());
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
+ mAppsView = new ActivityAllAppsContainerView<>(this));
+ mUserCache = UserCache.getInstance(this);
+ mAppsList = mAppsView.getPersonalAppList();
+ mAllAppsStore = mAppsView.getAppsStore();
+ }
+ @Nullable
+ @Override
+ public PopupDataProvider getPopupDataProvider() {
+ return mPopupDataProvider;
+ }
+
+ @Override
+ public ActivityAllAppsContainerView<ActivityContextWrapper> getAppsView() {
+ return mAppsView;
+ }
+
+ @Override
+ public void bindAllApplications(AppInfo[] apps, int flags,
+ Map<PackageUserKey, Integer> packageUserKeytoUidMap) {
+ mAllAppsStore.setApps(apps, flags, packageUserKeytoUidMap);
+ mBindCompleted.countDown();
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/multivalentTests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 10b428a..7fba33e 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -125,8 +125,6 @@
Log.e(TAG, "Failed to save accessibility hierarchy", ex);
}
- dumpCommand("logcat -d -s TestRunner", diagFile(description, "FilteredLogcat", "txt"));
-
// Dump bugreport
if (!sSavedBugreport) {
dumpCommand("bugreportz -s", diagFile(description, "Bugreport", "zip"));
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 6c9f5ed..cdde605 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -28,7 +28,9 @@
import static com.android.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID;
import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_GET_SPLIT_SELECTION_ACTIVE;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_NUM_ALL_APPS_COLUMNS;
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
import android.app.ActivityManager;
import android.app.Instrumentation;
@@ -878,7 +880,6 @@
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
- waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
if (is3PLauncher() && isTablet() && !isTransientTaskbar()) {
@@ -887,6 +888,12 @@
waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
}
+ boolean splitSelectionActive = getTestInfo(REQUEST_GET_SPLIT_SELECTION_ACTIVE)
+ .getBoolean(TEST_INFO_RESPONSE_FIELD);
+ if (!splitSelectionActive) {
+ waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+ } // do nothing, we expect that view
+
return waitForLauncherObject(APPS_RES_ID);
}
case OVERVIEW:
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index dbafe79..2905d85 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -17,9 +17,12 @@
import android.content.Context
import android.content.res.Configuration
-import android.content.res.Resources
import android.graphics.Point
import android.graphics.Rect
+import android.platform.test.rule.AllowedDevices
+import android.platform.test.rule.DeviceProduct
+import android.platform.test.rule.IgnoreLimit
+import android.platform.test.rule.LimitDevicesRule
import android.util.DisplayMetrics
import android.view.Surface
import androidx.test.core.app.ApplicationProvider
@@ -32,7 +35,6 @@
import com.android.launcher3.util.rule.TestStabilityRule
import com.android.launcher3.util.window.CachedDisplayInfo
import com.android.launcher3.util.window.WindowManagerProxy
-import com.android.wm.shell.Flags
import com.google.common.truth.Truth
import java.io.BufferedReader
import java.io.File
@@ -52,6 +54,8 @@
*
* For an implementation that mocks InvariantDeviceProfile, use [FakeInvariantDeviceProfileTest]
*/
+@AllowedDevices(allowed = [DeviceProduct.CF_PHONE])
+@IgnoreLimit(ignoreLimit = BuildConfig.IS_STUDIO_BUILD)
abstract class AbstractDeviceProfileTest {
protected val testContext: Context = InstrumentationRegistry.getInstrumentation().context
protected lateinit var context: SandboxContext
@@ -59,15 +63,11 @@
private val displayController: DisplayController = mock()
private val windowManagerProxy: WindowManagerProxy = mock()
private val launcherPrefs: LauncherPrefs = mock()
- private val allowLeftRightSplitInPortrait: Boolean = initAllowLeftRightSplitInPortrait()
- fun initAllowLeftRightSplitInPortrait(): Boolean {
- val res = Resources.getSystem()
- val resId = res.getIdentifier("config_leftRightSplitInPortrait", "bool", "android")
- return Flags.enableLeftRightSplitInPortrait() && resId > 0 && res.getBoolean(resId)
- }
@Rule @JvmField val testStabilityRule = TestStabilityRule()
+ @Rule @JvmField val limitDevicesRule = LimitDevicesRule()
+
class DeviceSpec(
val naturalSize: Pair<Int, Int>,
var densityDpi: Int,
@@ -311,22 +311,6 @@
protected fun assertDump(dp: DeviceProfile, folderName: String, filename: String) {
val dump = dump(context!!, dp, "${folderName}_$filename.txt")
var expected = readDumpFromAssets(testContext, "$folderName/$filename.txt")
-
- // TODO(b/315230497): We don't currently have device-specific device profile dumps, so just
- // update the result before we do the comparison
- if (allowLeftRightSplitInPortrait) {
- val isLeftRightSplitInPortrait =
- when {
- allowLeftRightSplitInPortrait && dp.isTablet -> !dp.isLandscape
- else -> dp.isLandscape
- }
- expected =
- expected.replace(
- Regex("isLeftRightSplit:\\w+"),
- "isLeftRightSplit:$isLeftRightSplitInPortrait"
- )
- }
-
Truth.assertThat(dump).isEqualTo(expected)
}
diff --git a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
index 30b5663..251a401 100644
--- a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
@@ -18,6 +18,10 @@
import android.content.Context
import android.graphics.PointF
import android.graphics.Rect
+import android.platform.test.rule.AllowedDevices
+import android.platform.test.rule.DeviceProduct
+import android.platform.test.rule.IgnoreLimit
+import android.platform.test.rule.LimitDevicesRule
import android.util.SparseArray
import androidx.test.core.app.ApplicationProvider
import com.android.launcher3.DeviceProfile.DEFAULT_DIMENSION_PROVIDER
@@ -27,6 +31,7 @@
import java.io.PrintWriter
import java.io.StringWriter
import org.junit.Before
+import org.junit.Rule
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -37,6 +42,8 @@
*
* For an implementation that creates InvariantDeviceProfile, use [AbstractDeviceProfileTest]
*/
+@AllowedDevices(allowed = [DeviceProduct.CF_PHONE])
+@IgnoreLimit(ignoreLimit = BuildConfig.IS_STUDIO_BUILD)
abstract class FakeInvariantDeviceProfileTest {
protected var context: Context? = null
@@ -49,6 +56,8 @@
protected var isGestureMode: Boolean = true
protected var isTransientTaskbar: Boolean = true
+ @Rule @JvmField val limitDevicesRule = LimitDevicesRule()
+
@Before
fun setUp() {
context = ApplicationProvider.getApplicationContext()
diff --git a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
new file mode 100644
index 0000000..e459956
--- /dev/null
+++ b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2024 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.popup;
+
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.launcher3.Flags.FLAG_ENABLE_PRIVATE_SPACE;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.os.Process;
+import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.View;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.allapps.PrivateProfileManager;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext;
+import com.android.launcher3.util.TestSandboxModelContextWrapper;
+import com.android.launcher3.util.UserIconInfo;
+import com.android.launcher3.views.BaseDragLayer;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemShortcutTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+ private static final UserHandle PRIVATE_HANDLE = new UserHandle(11);
+ private static final UserHandle MAIN_HANDLE = Process.myUserHandle();
+ private View mView;
+ private ItemInfo mItemInfo;
+ private TestSandboxModelContextWrapper mTestContext;
+ private final SandboxModelContext mSandboxContext = new SandboxModelContext();
+ private PrivateProfileManager mPrivateProfileManager;
+ private PopupDataProvider mPopupDataProvider;
+ private AppInfo mAppInfo;
+ @Mock UserCache mUserCache;
+ @Mock BaseDragLayer mBaseDragLayer;
+ @Mock UserIconInfo mUserIconInfo;
+ @Mock LauncherActivityInfo mLauncherActivityInfo;
+ @Mock ApplicationInfo mApplicationInfo;
+ @Mock Intent mIntent;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSandboxContext.putObject(UserCache.INSTANCE, mUserCache);
+ mTestContext = new TestSandboxModelContextWrapper(mSandboxContext);
+ mView = new View(mSandboxContext);
+ spyOn(mTestContext);
+ spyOn(mSandboxContext);
+ doReturn(mBaseDragLayer).when(mTestContext).getDragLayer();
+
+ mItemInfo = new ItemInfo();
+
+ LauncherApps mLauncherApps = mSandboxContext.spyService(LauncherApps.class);
+ doReturn(mLauncherActivityInfo).when(mLauncherApps).resolveActivity(any(), any());
+ when(mLauncherActivityInfo.getApplicationInfo()).thenReturn(mApplicationInfo);
+
+ when(mUserCache.getUserInfo(any())).thenReturn(mUserIconInfo);
+ when(mBaseDragLayer.getChildCount()).thenReturn(0);
+ mPrivateProfileManager = mTestContext.getAppsView().getPrivateProfileManager();
+ spyOn(mPrivateProfileManager);
+ when(mPrivateProfileManager.getProfileUser()).thenReturn(PRIVATE_HANDLE);
+
+ mPopupDataProvider = mTestContext.getPopupDataProvider();
+ spyOn(mPopupDataProvider);
+ }
+
+ @After
+ public void tearDown() {
+ mSandboxContext.onDestroy();
+ }
+
+ @Test
+ public void testWidgetsForNullComponentName() {
+ assertNull(mItemInfo.getTargetComponent());
+ SystemShortcut systemShortcut = SystemShortcut.WIDGETS
+ .getShortcut(mTestContext, mItemInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testWidgetsForEmptyWidgetList() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ assertNotNull(mAppInfo.getTargetComponent());
+ doReturn(new ArrayList<>()).when(mPopupDataProvider).getWidgetsForPackageUser(any());
+ spyOn(mAppInfo);
+ SystemShortcut systemShortcut = SystemShortcut.WIDGETS
+ .getShortcut(mTestContext, mAppInfo, mView);
+ verify(mAppInfo, times(2)).getTargetComponent();
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testAppInfoShortcut() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ SystemShortcut systemShortcut = SystemShortcut.APP_INFO
+ .getShortcut(mTestContext, mAppInfo, mView);
+ assertNotNull(systemShortcut);
+ }
+
+
+ @Test
+ public void testDontSuggestAppForNonPredictedItem() {
+ assertFalse(mItemInfo.isPredictedItem());
+ SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP
+ .getShortcut(mTestContext, mItemInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testDontSuggestAppForPredictedItem() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_HOTSEAT_PREDICTION;
+ assertTrue(mAppInfo.isPredictedItem());
+ SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP
+ .getShortcut(mTestContext, mAppInfo, mView);
+ assertNotNull(systemShortcut);
+ systemShortcut.onClick(mView);
+ }
+
+ @Test
+ public void testPrivateProfileInstallwithTargetComponentNull() {
+ assertNull(mItemInfo.getTargetComponent());
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mItemInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testPrivateProfileInstallNotAllAppsContainer() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_HOTSEAT_PREDICTION;
+
+ assertNotNull(mAppInfo.getTargetComponent());
+ assertFalse(mAppInfo.getContainerInfo().hasAllAppsContainer());
+
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mAppInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testPrivateProfileInstallNullPrivateProfileManager() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_ALL_APPS;
+ mPrivateProfileManager = null;
+
+ assertNotNull(mAppInfo.getTargetComponent());
+ assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());
+ assertNull(mPrivateProfileManager);
+
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mAppInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testPrivateProfileInstallPrivateProfileManagerDisabled() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_ALL_APPS;
+
+ assertNotNull(mPrivateProfileManager);
+ assertNotNull(mAppInfo.getTargetComponent());
+ assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());
+
+ when(mPrivateProfileManager.isEnabled()).thenReturn(false);
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mAppInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testPrivateProfileInstallNullPrivateProfileUser() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_ALL_APPS;
+ when(mPrivateProfileManager.getProfileUser()).thenReturn(null);
+
+ assertNotNull(mPrivateProfileManager);
+ assertNotNull(mAppInfo.getTargetComponent());
+ assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());
+ assertNull(mPrivateProfileManager.getProfileUser());
+
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mAppInfo, mView);
+
+ verify(mPrivateProfileManager, times(2)).getProfileUser();
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testPrivateProfileInstallNonNullPrivateProfileUser() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_ALL_APPS;
+ when(mPrivateProfileManager.isEnabled()).thenReturn(true);
+ when(mPrivateProfileManager.getProfileUser()).thenReturn(PRIVATE_HANDLE);
+
+ assertNotNull(mAppInfo.getTargetComponent());
+ assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());
+ assertNotNull(mPrivateProfileManager);
+ assertNotNull(mPrivateProfileManager.getProfileUser());
+ assertNull(mTestContext.getAppsView().getAppsStore().getApp(
+ new ComponentKey(mAppInfo.getTargetComponent(), PRIVATE_HANDLE)));
+
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mAppInfo, mView);
+
+ verify(mPrivateProfileManager, times(3)).getProfileUser();
+ verify(mPrivateProfileManager).isEnabled();
+ assertNotNull(systemShortcut);
+ }
+
+ @Test
+ public void testInstallGetShortcutWithNonWorkSpaceItemInfo() {
+ SystemShortcut systemShortcut = SystemShortcut.INSTALL.getShortcut(
+ mTestContext, mItemInfo, mView);
+ Assert.assertNull(systemShortcut);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testInstallGetShortcutWithWorkSpaceItemInfo() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.intent = mIntent;
+ WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mAppInfo);
+ workspaceItemInfo.status = FLAG_SUPPORTS_WEB_UI;
+ SystemShortcut systemShortcut = SystemShortcut.INSTALL.getShortcut(
+ mTestContext, workspaceItemInfo, mView);
+ Assert.assertNotNull(systemShortcut);
+ }
+
+
+ @Test
+ @DisableFlags(FLAG_ENABLE_PRIVATE_SPACE)
+ public void testUninstallGetShortcutWithPrivateSpaceOff() {
+ SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
+ mTestContext, null, mView);
+ Assert.assertNull(systemShortcut);
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE)
+ public void testUninstallGetShortcutWithNonPrivateItemInfo() {
+ mAppInfo = new AppInfo();
+ mAppInfo.user = MAIN_HANDLE;
+ when(mUserIconInfo.isPrivate()).thenReturn(false);
+
+ SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
+ mTestContext, mAppInfo, mView);
+ verify(mUserIconInfo).isPrivate();
+ Assert.assertNull(systemShortcut);
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE)
+ public void testUninstallGetShortcutWithSystemItemInfo() {
+ mAppInfo = new AppInfo();
+ mAppInfo.user = PRIVATE_HANDLE;
+ mAppInfo.itemType = ITEM_TYPE_APPLICATION;
+ mAppInfo.intent = mIntent;
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ when(mLauncherActivityInfo.getComponentName()).thenReturn(mAppInfo.componentName);
+ when(mUserIconInfo.isPrivate()).thenReturn(true);
+ // System App
+ mApplicationInfo.flags = 1;
+
+ SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
+ mTestContext, mAppInfo, mView);
+ verify(mLauncherActivityInfo, times(0)).getComponentName();
+ Assert.assertNull(systemShortcut);
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE)
+ public void testUninstallGetShortcutWithPrivateItemInfo() {
+ mAppInfo = new AppInfo();
+ mAppInfo.user = PRIVATE_HANDLE;
+ mAppInfo.itemType = ITEM_TYPE_APPLICATION;
+ mAppInfo.intent = mIntent;
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ when(mUserIconInfo.isPrivate()).thenReturn(true);
+ when(mLauncherActivityInfo.getComponentName()).thenReturn(mAppInfo.componentName);
+ // 3rd party app, not system app.
+ mApplicationInfo.flags = 0;
+
+ SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
+ mTestContext, mAppInfo, mView);
+
+ verify(mLauncherActivityInfo).getComponentName();
+ Assert.assertNotNull(systemShortcut);
+
+ systemShortcut.onClick(mView);
+ verify(mSandboxContext).startActivity(any());
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
index cb30854..f8f5dde 100644
--- a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
@@ -28,10 +28,13 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import android.platform.test.rule.ScreenRecordRule;
import android.util.Log;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
@@ -47,11 +50,14 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.Objects;
import java.util.function.Predicate;
+@LargeTest
+@RunWith(AndroidJUnit4.class)
public class TaplWorkProfileTest extends AbstractLauncherUiTest {
private static final int WORK_PAGE = ActivityAllAppsContainerView.AdapterHolder.WORK;
@@ -64,6 +70,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
+ initialize(this);
String output =
mDevice.executeShellCommand(
"pm create-user --profileOf 0 --managed TestProfile");
@@ -139,7 +146,7 @@
// Staging; will be promoted to presubmit if stable
@TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
-
+ @ScreenRecordRule.ScreenRecord
@Test
public void toggleWorks() {
assumeTrue(mWorkProfileSetupSuccessful);
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java
new file mode 100644
index 0000000..b347f07
--- /dev/null
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 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 static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.ActivityContextWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WidgetsListHeaderAccessibilityTest {
+ private Context mContext;
+ private LayoutInflater mLayoutInflater;
+ @Mock
+ private View.OnClickListener mOnClickListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = new ActivityContextWrapper(getApplicationContext());
+ mLayoutInflater = LayoutInflater.from(
+ new ContextThemeWrapper(mContext, R.style.WidgetContainerTheme));
+ }
+
+ @Test
+ public void singlePaneCollapsable_hasCustomAccessibilityActions() {
+ WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
+ R.layout.widgets_list_row_header,
+ new FrameLayout(mContext), false);
+
+ assertThat(header.getAccessibilityDelegate()).isNotNull();
+
+ header.setOnClickListener(mOnClickListener);
+ header.getAccessibilityDelegate().performAccessibilityAction(header,
+ AccessibilityNodeInfo.ACTION_EXPAND, null);
+ header.getAccessibilityDelegate().performAccessibilityAction(header,
+ AccessibilityNodeInfo.ACTION_COLLAPSE, null);
+
+ verify(mOnClickListener, times(2)).onClick(header);
+ }
+
+ @Test
+ public void twoPaneNonCollapsable_noCustomAccessibilityDelegate() {
+ WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
+ R.layout.widgets_list_row_header_two_pane,
+ new FrameLayout(mContext), false);
+
+ assertThat(header.getAccessibilityDelegate()).isNull();
+ }
+}