Merge "Use script to auto-enable all tests that are currently working" into main
diff --git a/quickstep/res/layout/task_menu_with_arrow.xml b/quickstep/res/layout/task_menu_with_arrow.xml
index 38573fd..c9108a5 100644
--- a/quickstep/res/layout/task_menu_with_arrow.xml
+++ b/quickstep/res/layout/task_menu_with_arrow.xml
@@ -23,11 +23,17 @@
android:orientation="vertical"
android:visibility="invisible">
- <LinearLayout
- android:id="@+id/menu_option_layout"
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:showDividers="middle" />
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:id="@+id/menu_option_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:showDividers="middle" />
+
+ </ScrollView>
</com.android.quickstep.views.TaskMenuViewWithArrow>
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
index 0476fe8..1be908b 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
@@ -213,7 +213,8 @@
@Override
public int getTaskMenuHeight(float taskInsetMargin, DeviceProfile deviceProfile,
float taskMenuX, float taskMenuY) {
- return (int) (deviceProfile.availableHeightPx - taskInsetMargin - taskMenuY);
+ return (int) (deviceProfile.heightPx - deviceProfile.getInsets().top - taskMenuY
+ - deviceProfile.getOverviewActionsClaimedSpaceBelow());
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index 6431bdf..486fc2c 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -305,7 +305,8 @@
*/
public static int getDefaultBackgroundColor(
Context context, RemoteAnimationTarget target) {
- return (target != null && target.taskInfo.taskDescription != null)
+ return (target != null && target.taskInfo != null
+ && target.taskInfo.taskDescription != null)
? target.taskInfo.taskDescription.getBackgroundColor()
: Themes.getColorBackground(context);
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index e86b7d8..956dd26 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -140,9 +140,14 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int maxMenuHeight = calculateMaxHeight();
- if (MeasureSpec.getSize(heightMeasureSpec) > maxMenuHeight) {
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST);
+ if (!(enableOverviewIconMenu()
+ && ((RecentsView) mActivity.getOverviewPanel()).isOnGridBottomRow(mTaskView))) {
+ // TODO(b/326952853): Cap menu height for grid bottom row in a way that doesn't break
+ // additionalTranslationY.
+ int maxMenuHeight = calculateMaxHeight();
+ if (MeasureSpec.getSize(heightMeasureSpec) > maxMenuHeight) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST);
+ }
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index 12b8b6f..dcf681c 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -124,6 +124,25 @@
optionLayout = requireViewById(R.id.menu_option_layout)
}
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ val maxMenuHeight: Int = calculateMaxHeight()
+ val newHeightMeasureSpec =
+ if (MeasureSpec.getSize(heightMeasureSpec) > maxMenuHeight) {
+ MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST)
+ } else heightMeasureSpec
+ super.onMeasure(widthMeasureSpec, newHeightMeasureSpec)
+ }
+
+ private fun calculateMaxHeight(): Int {
+ val taskInsetMargin = resources.getDimension(R.dimen.task_card_margin)
+ return taskView.pagedOrientationHandler.getTaskMenuHeight(
+ taskInsetMargin,
+ mActivityContext.deviceProfile,
+ translationX,
+ translationY
+ )
+ }
+
private fun populateAndShowForTask(
taskContainer: TaskIdAttributeContainer,
alignedOptionIndex: Int
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index ec8e00d..2a54057 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -117,18 +117,21 @@
}
@Test
- public void testSaveAppPairMenuItemExistsOnSplitPair() throws Exception {
+ public void testSaveAppPairMenuItemOrActionExistsOnSplitPair() {
assumeTrue("App pairs feature is currently not enabled, no test needed",
Flags.enableAppPairs());
createAndLaunchASplitPair();
- assertTrue("Save app pair menu item is missing",
- mLauncher.goHome()
- .switchToOverview()
- .getCurrentTask()
- .tapMenu()
- .hasMenuItem("Save app pair"));
+ Overview overview = mLauncher.goHome().switchToOverview();
+ if (mLauncher.isGridOnlyOverviewEnabled() || !mLauncher.isTablet()) {
+ assertTrue("Save app pair menu item is missing",
+ overview.getCurrentTask()
+ .tapMenu()
+ .hasMenuItem("Save app pair"));
+ } else {
+ overview.getOverviewActions().assertHasAction("Save app pair");
+ }
}
@Test
diff --git a/res/layout/widget_recommendations.xml b/res/layout/widget_recommendations.xml
index 531db2e..5879b0f 100644
--- a/res/layout/widget_recommendations.xml
+++ b/res/layout/widget_recommendations.xml
@@ -33,6 +33,9 @@
android:textColor="?attr/widgetPickerTitleColor"
android:textFontWeight="500"
android:textSize="16sp"
+ android:maxLines="1"
+ android:paddingHorizontal="8dp"
+ android:ellipsize="end"
android:visibility="gone" />
<!-- Shown when there are more than one pages -->
<com.android.launcher3.pageindicators.PageIndicatorDots
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
index 255a6d2..4f73e66 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -18,7 +18,9 @@
import static com.android.launcher3.widget.util.WidgetsTableUtils.groupWidgetItemsUsingRowPxWithoutReordering;
+import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -31,14 +33,19 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeMap;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
/**
* A {@link PagedView} that displays widget recommendations in categories with dots as paged
@@ -46,6 +53,8 @@
*/
public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
private @Px float mAvailableHeight = Float.MAX_VALUE;
+ private static final String INITIALLY_DISPLAYED_WIDGETS_STATE_KEY =
+ "widgetRecommendationsView:mDisplayedWidgets";
private static final int MAX_CATEGORIES = 3;
private TextView mRecommendationPageTitle;
private final List<String> mCategoryTitles = new ArrayList<>();
@@ -57,6 +66,7 @@
private OnLongClickListener mWidgetCellOnLongClickListener;
@Nullable
private OnClickListener mWidgetCellOnClickListener;
+ private Set<ComponentName> mDisplayedWidgets = Collections.emptySet();
public WidgetRecommendationsView(Context context) {
this(context, /* attrs= */ null);
@@ -76,6 +86,38 @@
mRecommendationPageTitle = parent.findViewById(R.id.recommendations_page_title);
}
+ /**
+ * Saves the necessary state in the provided bundle. To be called in case of orientation /
+ * other config changes.
+ */
+ public void saveState(Bundle bundle) {
+ // Save the widgets that were displayed, so that, on rotation / fold / unfold, we can
+ // maintain the "initial" set of widgets that user first saw (if they fit).
+ bundle.putParcelableArrayList(INITIALLY_DISPLAYED_WIDGETS_STATE_KEY,
+ new ArrayList<>(mDisplayedWidgets));
+ }
+
+ /**
+ * Restores the state that was saved by the saveState method during orientation / other config
+ * changes.
+ */
+ public void restoreState(Bundle bundle) {
+ ArrayList<ComponentName> componentList;
+ if (Utilities.ATLEAST_T) {
+ componentList = bundle.getParcelableArrayList(
+ INITIALLY_DISPLAYED_WIDGETS_STATE_KEY, ComponentName.class);
+ } else {
+ componentList = bundle.getParcelableArrayList(
+ INITIALLY_DISPLAYED_WIDGETS_STATE_KEY);
+ }
+
+ // Restore the "initial" set of widgets that were displayed, so that, on rotation / fold /
+ // unfold, we can maintain the set of widgets that user first saw (if they fit).
+ if (componentList != null) {
+ mDisplayedWidgets = new HashSet<>(componentList);
+ }
+ }
+
/** Sets a {@link android.view.View.OnLongClickListener} for all widget cells in this table. */
public void setWidgetCellLongClickListener(OnLongClickListener onLongClickListener) {
mWidgetCellOnLongClickListener = onLongClickListener;
@@ -112,10 +154,18 @@
this.mAvailableHeight = availableHeight;
clear();
- int displayedWidgets = maybeDisplayInTable(recommendedWidgets, deviceProfile,
+ Set<ComponentName> displayedWidgets = maybeDisplayInTable(recommendedWidgets,
+ deviceProfile,
availableWidth, cellPadding);
+
+ if (mDisplayedWidgets.isEmpty()) {
+ // Save the widgets shown for the first time user opened the picker; so that, they can
+ // be maintained across orientation changes.
+ mDisplayedWidgets = displayedWidgets;
+ }
+
updateTitleAndIndicator(/* requestedPage= */ 0);
- return displayedWidgets;
+ return displayedWidgets.size();
}
/**
@@ -144,20 +194,21 @@
clear();
int displayedCategories = 0;
- int totalDisplayedWidgets = 0;
+ Set<ComponentName> allDisplayedWidgets = new HashSet<>();
// Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
new TreeMap<>(recommendations).entrySet()) {
// If none of the recommendations for the category could fit in the mAvailableHeight, we
// don't want to add that category; and we look for the next one.
- int displayedCount = maybeDisplayInTable(entry.getValue(), deviceProfile,
+ Set<ComponentName> displayedWidgetsForCategory = maybeDisplayInTable(entry.getValue(),
+ deviceProfile,
availableWidth, cellPadding);
- if (displayedCount > 0) {
+ if (!displayedWidgetsForCategory.isEmpty()) {
mCategoryTitles.add(
context.getResources().getString(entry.getKey().categoryTitleRes));
displayedCategories++;
- totalDisplayedWidgets += displayedCount;
+ allDisplayedWidgets.addAll(displayedWidgetsForCategory);
}
if (displayedCategories == MAX_CATEGORIES) {
@@ -165,11 +216,17 @@
}
}
+ if (mDisplayedWidgets.isEmpty()) {
+ // Save the widgets shown for the first time user opened the picker; so that, they can
+ // be maintained across orientation changes.
+ mDisplayedWidgets = allDisplayedWidgets;
+ }
+
updateTitleAndIndicator(requestedPage);
// For purpose of recommendations section, we don't want paging dots to be halved in two
// pane display, so, we always provide isTwoPanels = "false".
mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
- return totalDisplayedWidgets;
+ return allDisplayedWidgets.size();
}
private void clear() {
@@ -241,20 +298,25 @@
}
/**
- * Groups the provided recommendations into rows and displays them in a table if at least one
- * fits.
- * <p>Returns false if none of the recommendations could fit.</p>
+ * Groups the provided recommendations into rows and displays ones that fit in a table.
+ * <p>Returns the set of widgets that could fit.</p>
*/
- private int maybeDisplayInTable(List<WidgetItem> recommendedWidgets,
+ private Set<ComponentName> maybeDisplayInTable(List<WidgetItem> recommendedWidgets,
DeviceProfile deviceProfile,
final @Px int availableWidth, final @Px int cellPadding) {
+ List<WidgetItem> filteredRecommendedWidgets = recommendedWidgets;
+ // Show only those widgets that were displayed when user first opened the picker.
+ if (!mDisplayedWidgets.isEmpty()) {
+ filteredRecommendedWidgets = recommendedWidgets.stream().filter(
+ w -> mDisplayedWidgets.contains(w.componentName)).toList();
+ }
Context context = getContext();
LayoutInflater inflater = LayoutInflater.from(context);
// Since we are limited by space, we don't sort recommendations - to show most relevant
// (if possible).
List<ArrayList<WidgetItem>> rows = groupWidgetItemsUsingRowPxWithoutReordering(
- recommendedWidgets,
+ filteredRecommendedWidgets,
context,
deviceProfile,
availableWidth,
@@ -268,13 +330,17 @@
recommendationsTable.setWidgetCellOnClickListener(mWidgetCellOnClickListener);
recommendationsTable.setWidgetCellLongClickListener(mWidgetCellOnLongClickListener);
- int displayedCount = recommendationsTable.setRecommendedWidgets(rows,
+ List<ArrayList<WidgetItem>> displayedItems = recommendationsTable.setRecommendedWidgets(
+ rows,
deviceProfile, mAvailableHeight);
- if (displayedCount > 0) {
+
+ if (!displayedItems.isEmpty()) {
addView(recommendationsTable);
}
- return displayedCount;
+ return displayedItems.stream().flatMap(
+ items -> items.stream().map(w -> w.componentName))
+ .collect(Collectors.toSet());
}
/** Returns location of a widget cell for displaying the "touch and hold" education tip. */
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 85375ee..c3067a5 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -633,15 +633,15 @@
}
@Px
- private float getMaxAvailableHeightForRecommendations() {
+ protected float getMaxAvailableHeightForRecommendations() {
// There isn't enough space to show recommendations in landscape orientation on phones with
// a full sheet design. Tablets use a two pane picker.
- if (!isTwoPane() && mDeviceProfile.isLandscape) {
+ if (mDeviceProfile.isLandscape) {
return 0f;
}
return (mDeviceProfile.heightPx - mDeviceProfile.bottomSheetTopPadding)
- * getRecommendationSectionHeightRatio();
+ * RECOMMENDATION_TABLE_HEIGHT_RATIO;
}
/** b/209579563: "Widgets" header should be focused first. */
@@ -650,14 +650,6 @@
return mHeaderTitle;
}
- /**
- * Ratio of recommendations section with respect to bottom sheet's height on scale of 0 to 1.
- */
- @Px
- protected float getRecommendationSectionHeightRatio() {
- return RECOMMENDATION_TABLE_HEIGHT_RATIO;
- }
-
private void open(boolean animate) {
if (animate) {
if (getPopupContainer().getInsets().bottom > 0) {
@@ -733,6 +725,7 @@
// picker and calls save/restore hierarchy state. We save the state of recommendations
// across those updates.
bundle.putInt(RECOMMENDATIONS_SAVED_STATE_KEY, mRecommendationsCurrentPage);
+ mWidgetRecommendationsView.saveState(bundle);
SparseArray<Parcelable> superState = new SparseArray<>();
super.saveHierarchyState(superState);
bundle.putSparseParcelableArray(SUPER_SAVED_STATE_KEY, superState);
@@ -744,6 +737,7 @@
Bundle state = (Bundle) sparseArray.get(0);
mRecommendationsCurrentPage = state.getInt(
RECOMMENDATIONS_SAVED_STATE_KEY, /*defaultValue=*/0);
+ mWidgetRecommendationsView.restoreState(state);
super.restoreHierarchyState(state.getSparseParcelableArray(SUPER_SAVED_STATE_KEY));
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 563894d..a565780 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -83,14 +83,15 @@
* <p>If the content can't fit {@code recommendationTableMaxHeight}, this view will remove a
* last row from the {@code recommendedWidgets} until it fits or only one row left.
*
- * <p>Returns {@code false} if none of the widgets could fit</p>
+ * <p>Returns the list of widgets that could fit</p>
*/
- public int setRecommendedWidgets(List<ArrayList<WidgetItem>> recommendedWidgets,
+ public List<ArrayList<WidgetItem>> setRecommendedWidgets(
+ List<ArrayList<WidgetItem>> recommendedWidgets,
DeviceProfile deviceProfile, float recommendationTableMaxHeight) {
List<ArrayList<WidgetItem>> rows = selectRowsThatFitInAvailableHeight(recommendedWidgets,
recommendationTableMaxHeight, deviceProfile);
bindData(rows);
- return rows.stream().mapToInt(ArrayList::size).sum();
+ return rows;
}
private void bindData(List<ArrayList<WidgetItem>> recommendationTable) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index 1bf813c..14985bf 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -276,8 +276,15 @@
@Override
@Px
- protected float getRecommendationSectionHeightRatio() {
- return RECOMMENDATION_SECTION_HEIGHT_RATIO_TWO_PANE;
+ protected float getMaxAvailableHeightForRecommendations() {
+ if (mRecommendedWidgetsCount > 0) {
+ // If widgets were already selected for display, we show them all on orientation change
+ // in a two pane picker
+ return Float.MAX_VALUE;
+ }
+
+ return (mDeviceProfile.heightPx - mDeviceProfile.bottomSheetTopPadding)
+ * RECOMMENDATION_SECTION_HEIGHT_RATIO_TWO_PANE;
}
@Override
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
index 95444ba..d70ecdd 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
@@ -51,7 +51,6 @@
import com.android.launcher3.config.FeatureFlags.IntFlag;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.Workspace;
-import com.android.launcher3.util.rule.TestStabilityRule;
import org.junit.Assert;
@@ -138,20 +137,13 @@
*/
public static Point[] getCornersAndCenterPositions(LauncherInstrumentation launcher) {
final Point dimensions = launcher.getWorkspace().getIconGridDimensions();
- if (TestStabilityRule.isPresubmit()) {
- // Return only center in presubmit to fit under the presubmit SLO.
- return new Point[]{
- new Point(dimensions.x / 2, dimensions.y / 2)
- };
- } else {
- return new Point[]{
- new Point(0, 1),
- new Point(0, dimensions.y - 2),
- new Point(dimensions.x - 1, 1),
- new Point(dimensions.x - 1, dimensions.y - 2),
- new Point(dimensions.x / 2, dimensions.y / 2)
- };
- }
+ return new Point[]{
+ new Point(0, 1),
+ new Point(0, dimensions.y - 2),
+ new Point(dimensions.x - 1, 1),
+ new Point(dimensions.x - 1, dimensions.y - 2),
+ new Point(dimensions.x / 2, dimensions.y / 2)
+ };
}
/**
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
index d7c40a0..486a63b 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -17,6 +17,7 @@
package com.android.launcher3.tapl;
import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
/**
@@ -110,4 +111,12 @@
}
}
}
+
+ /** Asserts that an item matching the given string is present in the overview actions. */
+ public void assertHasAction(String text) {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to check if the action [" + text + "] is present")) {
+ mLauncher.waitForObjectInContainer(mOverviewActions, By.text(text));
+ }
+ }
}