Merge changes from topic "sticky-h-filter" into main
* changes:
Update tapl test to consider the show all button
Maintain selected or last header on clicking "Show all" button
diff --git a/res/layout/widgets_list_expand_button.xml b/res/layout/widgets_list_expand_button.xml
index 17c19ac..ff2d777 100644
--- a/res/layout/widgets_list_expand_button.xml
+++ b/res/layout/widgets_list_expand_button.xml
@@ -15,6 +15,7 @@
-->
<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/widget_list_expand_button"
style="@style/Button.Rounded.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/src/com/android/launcher3/views/StickyHeaderLayout.java b/src/com/android/launcher3/views/StickyHeaderLayout.java
index 090251f..4142e1f 100644
--- a/src/com/android/launcher3/views/StickyHeaderLayout.java
+++ b/src/com/android/launcher3/views/StickyHeaderLayout.java
@@ -120,7 +120,19 @@
}
private float getCurrentScroll() {
- return mScrollOffset + (mCurrentEmptySpaceView == null ? 0 : mCurrentEmptySpaceView.getY());
+ float scroll;
+ if (mCurrentRecyclerView.getVisibility() != VISIBLE) {
+ // When no list is displayed, assume no scroll.
+ scroll = 0f;
+ } else if (mCurrentEmptySpaceView != null) {
+ // Otherwise use empty space view as reference to position.
+ scroll = mCurrentEmptySpaceView.getY();
+ } else {
+ // If there is no empty space view, but the list is visible, we are scrolled away
+ // completely, so assume all non-sticky children should also be scrolled away.
+ scroll = -mHeaderHeight;
+ }
+ return mScrollOffset + scroll;
}
@Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 2f64ab1..8bebfb2 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -605,9 +605,12 @@
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.setVisibility(GONE);
mAdapters.get(getCurrentAdapterHolderType()).mWidgetsRecyclerView.setVisibility(
VISIBLE);
- // Visibility of recommended widgets, recycler views and headers are handled in methods
- // below.
- post(this::onRecommendedWidgetsBound);
+ if (mRecommendedWidgetsCount > 0) {
+ // Display recommendations immediately, if present, so that other parts of sticky
+ // header (e.g. personal / work tabs) don't flash in interim.
+ mWidgetRecommendationsContainer.setVisibility(VISIBLE);
+ }
+ // Visibility of recycler views and headers are handled in methods below.
onWidgetsBound();
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 3c67538..74a9a5c 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -103,7 +103,7 @@
.equals(mWidgetsContentVisiblePackageUserKey);
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
@Nullable private RecyclerView mRecyclerView;
- @Nullable private PackageUserKey mPendingClickHeader;
+ @Nullable private PackageUserKey mHeaderPositionToMaintain;
@Px private int mMaxHorizontalSpan;
private boolean mShowOnlyDefaultList = true;
@@ -215,7 +215,7 @@
// Get the current top of the header with the matching key before adjusting the visible
// entries.
OptionalInt previousPositionForPackageUserKey =
- getPositionForPackageUserKey(mPendingClickHeader);
+ getPositionForPackageUserKey(mHeaderPositionToMaintain);
OptionalInt topForPackageUserKey =
getOffsetForPosition(previousPositionForPackageUserKey);
@@ -247,13 +247,15 @@
mVisibleEntries.addAll(newVisibleEntries);
diffResult.dispatchUpdatesTo(this);
- if (mPendingClickHeader != null) {
+ if (mHeaderPositionToMaintain != null && mRecyclerView != null) {
// Get the position for the clicked header after adjusting the visible entries. The
// position may have changed if another header had previously been expanded.
OptionalInt positionForPackageUserKey =
- getPositionForPackageUserKey(mPendingClickHeader);
- scrollToPositionAndMaintainOffset(positionForPackageUserKey, topForPackageUserKey);
- mPendingClickHeader = null;
+ getPositionForPackageUserKey(mHeaderPositionToMaintain);
+ // Post scroll updates to be applied after diff updates.
+ mRecyclerView.post(() -> scrollToPositionAndMaintainOffset(positionForPackageUserKey,
+ topForPackageUserKey));
+ mHeaderPositionToMaintain = null;
}
}
@@ -384,7 +386,7 @@
// Store the header that was clicked so that its position will be maintained the next time
// we update the entries.
- mPendingClickHeader = packageUserKey;
+ mHeaderPositionToMaintain = packageUserKey;
updateVisibleEntries();
@@ -470,6 +472,16 @@
*/
public void useExpandedList() {
mShowOnlyDefaultList = false;
+ if (mWidgetsContentVisiblePackageUserKey != null) {
+ // Maintain selected header for the next update that expands the list.
+ mHeaderPositionToMaintain = mWidgetsContentVisiblePackageUserKey;
+ } else if (mVisibleEntries.size() > 2) {
+ // Maintain last visible header shown above expand button since there was no selected
+ // header.
+ mHeaderPositionToMaintain = PackageUserKey.fromPackageItemInfo(
+ mVisibleEntries.get(mVisibleEntries.size() - 2).mPkgItem);
+ }
+
}
/** Comparator for sorting WidgetListRowEntry based on package title. */
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 08c5552..fac73d3 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1728,6 +1728,27 @@
scrollDownByDistance(container, distance, appsListBottomPadding);
}
+ /** Scrolls up by given distance within the container. */
+ void scrollUpByDistance(UiObject2 container, int distance) {
+ scrollUpByDistance(container, distance, 0);
+ }
+
+ /** Scrolls up by given distance within the container considering the given bottom padding. */
+ void scrollUpByDistance(UiObject2 container, int distance, int bottomPadding) {
+ final Rect containerRect = getVisibleBounds(container);
+ final int bottomGestureMarginInContainer = getBottomGestureMarginInContainer(container);
+ scroll(
+ container,
+ Direction.UP,
+ new Rect(
+ 0,
+ containerRect.height() - bottomGestureMarginInContainer - distance,
+ 0,
+ bottomGestureMarginInContainer + bottomPadding),
+ /* steps= */ 10,
+ /* slowDown= */ true);
+ }
+
void scrollDownByDistance(UiObject2 container, int distance) {
scrollDownByDistance(container, distance, 0);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 3097d9c..ac2748e 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
import static com.android.launcher3.tapl.LauncherInstrumentation.log;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
@@ -31,6 +32,7 @@
import com.android.launcher3.testing.shared.TestProtocol;
import java.util.Collection;
+import java.util.List;
/**
* All widgets container.
@@ -128,8 +130,10 @@
final UiObject2 searchBar = findSearchBar();
final int searchBarHeight = searchBar.getVisibleBounds().height();
final UiObject2 fullWidgetsPicker = verifyActiveContainer();
- mLauncher.assertTrue("Widgets container didn't become scrollable",
- fullWidgetsPicker.wait(Until.scrollable(true), WAIT_TIME_MS));
+
+ // Widget picker may not be scrollable if there are few items. Instead of waiting on
+ // picker being scrollable, we wait on widget headers to be available.
+ waitForWidgetListItems(fullWidgetsPicker);
final UiObject2 widgetsContainer =
findTestAppWidgetsTableContainer(testAppWidgetPackage);
@@ -176,6 +180,13 @@
}
}
+ private void waitForWidgetListItems(UiObject2 fullWidgetsPicker) {
+ List<UiObject2> headers = fullWidgetsPicker.wait(Until.findObjects(
+ By.res(mLauncher.getLauncherPackageName(), "widgets_list_header")), WAIT_TIME_MS);
+ mLauncher.assertTrue("Widgets list is not available",
+ headers != null && !headers.isEmpty());
+ }
+
private UiObject2 findSearchBar() {
final BySelector searchBarContainerSelector = By.res(mLauncher.getLauncherPackageName(),
"search_and_recommendations_container");
@@ -199,19 +210,38 @@
"container");
String packageName = mLauncher.getContext().getPackageName();
+ String packageNameToFind =
+ (testAppWidgetPackage == null || testAppWidgetPackage.isEmpty()) ? packageName
+ : testAppWidgetPackage;
+
final BySelector targetAppSelector = By
.clazz("android.widget.TextView")
- .text((testAppWidgetPackage == null || testAppWidgetPackage.isEmpty())
- ? packageName
- : testAppWidgetPackage);
+ .text(packageNameToFind);
+ final BySelector expandListButtonSelector =
+ By.res(mLauncher.getLauncherPackageName(), "widget_list_expand_button");
final BySelector widgetsContainerSelector = By.res(mLauncher.getLauncherPackageName(),
"widgets_table");
boolean hasHeaderExpanded = false;
+ // List was expanded by clicking "Show all" button.
+ boolean hasListExpanded = false;
+
int scrollDistance = 0;
for (int i = 0; i < SCROLL_ATTEMPTS; i++) {
UiObject2 widgetPicker = mLauncher.waitForLauncherObject(widgetPickerSelector);
UiObject2 widgetListView = verifyActiveContainer();
+
+ // Press "Show all" button if it exists. Otherwise, keep scrolling to
+ // find the header or show all button.
+ UiObject2 expandListButton =
+ mLauncher.findObjectInContainer(widgetListView, expandListButtonSelector);
+ if (expandListButton != null) {
+ expandListButton.click();
+ hasListExpanded = true;
+ i = -1;
+ continue;
+ }
+
UiObject2 header = mLauncher.waitForObjectInContainer(widgetListView,
headerSelector);
// If a header is barely visible in the bottom edge of the screen, its height could be
@@ -222,6 +252,17 @@
// Look for a header that has the test app name.
UiObject2 headerTitle = mLauncher.findObjectInContainer(widgetListView,
targetAppSelector);
+
+ final UiObject2 searchBar = findSearchBar();
+ // If header's title is under or above search bar, let's not process the header yet,
+ // scroll a bit more to bring the header into visible area.
+ if (headerTitle != null
+ && headerTitle.getVisibleCenter().y <= searchBar.getVisibleCenter().y) {
+ log("Test app's header is behind the searchbar, scrolling up");
+ mLauncher.scrollUpByDistance(widgetListView, scrollDistance);
+ continue;
+ }
+
if (headerTitle != null) {
// If we find the header and it has not been expanded, let's click it to see the
// widgets list. Note that we wait until the header is out of the gesture region at
@@ -258,11 +299,24 @@
widgetPicker,
widgetsContainerSelector);
- mLauncher.scrollDownByDistance(hasHeaderExpanded && rightPane != null
- ? rightPane
- : widgetListView, scrollDistance);
+ if (hasListExpanded && packageNameToFind.compareToIgnoreCase(
+ getFirstHeaderTitle(widgetListView)) < 0) {
+ mLauncher.scrollUpByDistance(hasHeaderExpanded && rightPane != null
+ ? rightPane
+ : widgetListView, scrollDistance);
+ } else {
+ mLauncher.scrollDownByDistance(hasHeaderExpanded && rightPane != null
+ ? rightPane
+ : widgetListView, scrollDistance);
+ }
}
return null;
}
+
+ @NonNull
+ private String getFirstHeaderTitle(UiObject2 widgetListView) {
+ UiObject2 firstHeader = mLauncher.getObjectsInContainer(widgetListView, "app_title").get(0);
+ return firstHeader != null ? firstHeader.getText() : "";
+ }
}