Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eef12ba5 authored by Shamali Patwa's avatar Shamali Patwa Committed by Android (Google) Code Review
Browse files

Merge "Retain recommendations (if possible) while picker is open" into main

parents 44d9d137 b0813143
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
        android:layout_marginTop="16dp"
        android:accessibilityLiveRegion="polite"
        android:gravity="center_horizontal"
        android:layout_gravity="top"
        android:lineHeight="20sp"
        android:textColor="?attr/widgetPickerTitleColor"
        android:textFontWeight="500"
@@ -38,7 +39,7 @@
        android:id="@+id/widget_recommendations_page_indicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_gravity="center_horizontal|top"
        android:elevation="1dp"
        android:visibility="gone" />
    <!--
@@ -50,8 +51,9 @@
    <com.android.launcher3.widget.picker.WidgetRecommendationsView
        android:id="@+id/widget_recommendations_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_height="0dp"
        android:layout_gravity="center"
        android:layout_weight="1"
        android:background="@drawable/widgets_surface_background"
        android:importantForAccessibility="yes"
        launcher:pageIndicator="@+id/widget_recommendations_page_indicator" />
+1 −1
Original line number Diff line number Diff line
@@ -122,7 +122,7 @@
                        <LinearLayout
                            android:id="@+id/widget_recommendations_container"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_height="match_parent"
                            android:background="@drawable/widgets_surface_background"
                            android:orientation="vertical"
                            android:visibility="gone">
+34 −13
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Consumer;

/**
 * A {@link PagedView} that displays widget recommendations in categories with dots as paged
@@ -45,11 +46,13 @@ import java.util.TreeMap;
 */
public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
    private @Px float mAvailableHeight = Float.MAX_VALUE;

    private static final int MAX_CATEGORIES = 3;
    private TextView mRecommendationPageTitle;
    private final List<String> mCategoryTitles = new ArrayList<>();

    /** Callbacks to run when page changes */
    private final List<Consumer<Integer>> mPageSwitchListeners = new ArrayList<>();

    @Nullable
    private OnLongClickListener mWidgetCellOnLongClickListener;
    @Nullable
@@ -83,6 +86,13 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
        mWidgetCellOnClickListener = widgetCellOnClickListener;
    }

    /**
     * Add a callback to run when the current displayed page changes.
     */
    public void addPageSwitchListener(Consumer<Integer> pageChangeListener) {
        mPageSwitchListeners.add(pageChangeListener);
    }

    /**
     * Displays all the provided recommendations in a single table if they fit.
     *
@@ -104,7 +114,7 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots

        int displayedWidgets = maybeDisplayInTable(recommendedWidgets, deviceProfile,
                availableWidth, cellPadding);
        updateTitleAndIndicator();
        updateTitleAndIndicator(/* requestedPage= */ 0);
        return displayedWidgets;
    }

@@ -119,16 +129,18 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
     * @param availableWidth  width in px that the recommendations should display in
     * @param cellPadding     padding in px that should be applied to each widget in the
     *                        recommendations
     * @param requestedPage   page number to display initially.
     * @return number of recommendations that could fit in the available space.
     */
    public int setRecommendations(
            Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations,
            DeviceProfile deviceProfile,
            final @Px float availableHeight, final @Px int availableWidth,
            final @Px int cellPadding) {
            DeviceProfile deviceProfile, final @Px float availableHeight,
            final @Px int availableWidth, final @Px int cellPadding, final int requestedPage) {
        this.mAvailableHeight = availableHeight;
        Context context = getContext();
        mPageIndicator.setPauseScroll(true, deviceProfile.isTwoPanels);
        // 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=*/true, /*isTwoPanels=*/ false);
        clear();

        int displayedCategories = 0;
@@ -153,26 +165,33 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
            }
        }

        updateTitleAndIndicator();
        mPageIndicator.setPauseScroll(false, deviceProfile.isTwoPanels);
        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;
    }

    private void clear() {
        mCategoryTitles.clear();
        removeAllViews();
        setCurrentPage(0);
        mPageIndicator.setActiveMarker(0);
    }

    /** Displays the page title and paging indicator if there are multiple pages. */
    private void updateTitleAndIndicator() {
    private void updateTitleAndIndicator(int requestedPage) {
        boolean showPaginatedView = getPageCount() > 1;
        int titleAndIndicatorVisibility = showPaginatedView ? View.VISIBLE : View.GONE;
        mRecommendationPageTitle.setVisibility(titleAndIndicatorVisibility);
        mPageIndicator.setVisibility(titleAndIndicatorVisibility);
        if (showPaginatedView) {
            mPageIndicator.setActiveMarker(0);
            setCurrentPage(0);
            mRecommendationPageTitle.setText(mCategoryTitles.get(0));
            if (requestedPage <= 0 || requestedPage >= getPageCount()) {
                requestedPage = 0;
            }
            setCurrentPage(requestedPage);
            mPageIndicator.setActiveMarker(requestedPage);
            mRecommendationPageTitle.setText(mCategoryTitles.get(requestedPage));
        }
    }

@@ -180,7 +199,9 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
    protected void notifyPageSwitchListener(int prevPage) {
        if (getPageCount() > 1) {
            // Since the title is outside the paging scroll, we update the title on page switch.
            mRecommendationPageTitle.setText(mCategoryTitles.get(getNextPage()));
            int nextPage = getNextPage();
            mRecommendationPageTitle.setText(mCategoryTitles.get(nextPage));
            mPageSwitchListeners.forEach(listener -> listener.accept(nextPage));
            super.notifyPageSwitchListener(prevPage);
        }
    }
+81 −40
Original line number Diff line number Diff line
@@ -15,8 +15,6 @@
 */
package com.android.launcher3.widget.picker;

import static android.view.View.MeasureSpec.makeMeasureSpec;

import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
@@ -30,6 +28,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.Process;
import android.os.UserHandle;
@@ -67,6 +66,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.model.UserManagerState;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -81,7 +81,9 @@ import com.android.launcher3.workprofile.PersonalWorkPagedView;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.IntStream;

@@ -98,8 +100,11 @@ public class WidgetsFullSheet extends BaseWidgetSheet

    // The widget recommendation table can easily take over the entire screen on devices with small
    // resolution or landscape on phone. This ratio defines the max percentage of content area that
    // the table can display.
    private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.75f;
    // the table can display with respect to bottom sheet's height.
    private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.45f;
    private static final String RECOMMENDATIONS_SAVED_STATE_KEY =
            "widgetsFullSheet:mRecommendationsCurrentPage";
    private static final String SUPER_SAVED_STATE_KEY = "widgetsFullSheet:superHierarchyState";
    private final UserCache mUserCache;
    private final UserManagerState mUserManagerState = new UserManagerState();
    private final UserHandle mCurrentUser = Process.myUserHandle();
@@ -109,8 +114,13 @@ public class WidgetsFullSheet extends BaseWidgetSheet
    protected final boolean mHasWorkProfile;
    // Number of recommendations displayed
    protected int mRecommendedWidgetsCount;
    private List<WidgetItem> mRecommendedWidgets = new ArrayList<>();
    private Map<WidgetRecommendationCategory, List<WidgetItem>> mRecommendedWidgetsMap =
            new HashMap<>();
    protected int mRecommendationsCurrentPage = 0;
    protected final SparseArray<AdapterHolder> mAdapters = new SparseArray();
    @Nullable private ArrowTipView mLatestEducationalTip;
    @Nullable
    private ArrowTipView mLatestEducationalTip;
    private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
            new OnLayoutChangeListener() {
                @Override
@@ -156,14 +166,19 @@ public class WidgetsFullSheet extends BaseWidgetSheet
                }
            };

    @Px private final int mTabsHeight;

    @Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
    @Nullable private WidgetsRecyclerView mCurrentTouchEventRecyclerView;
    @Nullable PersonalWorkPagedView mViewPager;
    @Px
    private final int mTabsHeight;

    @Nullable
    private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
    @Nullable
    private WidgetsRecyclerView mCurrentTouchEventRecyclerView;
    @Nullable
    PersonalWorkPagedView mViewPager;
    private boolean mIsInSearchMode;
    private boolean mIsNoWidgetsViewNeeded;
    @Px protected int mMaxSpanPerRow;
    @Px
    protected int mMaxSpanPerRow;
    protected DeviceProfile mDeviceProfile;

    protected TextView mNoWidgetsView;
@@ -227,13 +242,16 @@ public class WidgetsFullSheet extends BaseWidgetSheet
                R.id.widget_recommendations_container);
        mWidgetRecommendationsView = mSearchScrollView.findViewById(
                R.id.widget_recommendations_view);
        // To save the currently displayed page, so that, it can be requested when rebinding
        // recommendations with different size constraints.
        mWidgetRecommendationsView.addPageSwitchListener(
                newPage -> mRecommendationsCurrentPage = newPage);
        mWidgetRecommendationsView.initParentViews(mWidgetRecommendationsContainer);
        mWidgetRecommendationsView.setWidgetCellLongClickListener(this);
        mWidgetRecommendationsView.setWidgetCellOnClickListener(this);

        mHeaderTitle = mSearchScrollView.findViewById(R.id.title);

        onRecommendedWidgetsBound();
        onWidgetsBound();
        setUpEducationViewsIfNeeded();
    }
@@ -354,7 +372,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet
        super.onAttachedToWindow();
        LauncherAppState.getInstance(mActivityContext).getModel()
                .refreshAndBindWidgetsAndShortcuts(null);
        onRecommendedWidgetsBound();
    }

    @Override
@@ -582,16 +599,26 @@ public class WidgetsFullSheet extends BaseWidgetSheet
        }

        if (enableCategorizedWidgetSuggestions()) {
            // We avoid applying new recommendations when some are already displayed.
            if (mRecommendedWidgetsMap.isEmpty()) {
                mRecommendedWidgetsMap =
                        mActivityContext.getPopupDataProvider().getCategorizedRecommendedWidgets();
            }
            mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
                    mActivityContext.getPopupDataProvider().getCategorizedRecommendedWidgets(),
                    mRecommendedWidgetsMap,
                    mDeviceProfile,
                    /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
                    /* availableWidth= */ mMaxSpanPerRow,
                    /* cellPadding= */ mWidgetCellHorizontalPadding
                    /* cellPadding= */ mWidgetCellHorizontalPadding,
                    /* requestedPage= */ mRecommendationsCurrentPage
            );
        } else {
            if (mRecommendedWidgets.isEmpty()) {
                mRecommendedWidgets =
                        mActivityContext.getPopupDataProvider().getRecommendedWidgets();
            }
            mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
                    mActivityContext.getPopupDataProvider().getRecommendedWidgets(),
                    mRecommendedWidgets,
                    mDeviceProfile,
                    /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
                    /* availableWidth= */ mMaxSpanPerRow,
@@ -604,23 +631,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet

    @Px
    private float getMaxAvailableHeightForRecommendations() {
        float noWidgetsViewHeight = 0;
        if (mIsNoWidgetsViewNeeded) {
            // Make sure recommended section leaves enough space for noWidgetsView.
            Rect noWidgetsViewTextBounds = new Rect();
            mNoWidgetsView.getPaint()
                    .getTextBounds(mNoWidgetsView.getText().toString(), /* start= */ 0,
                            mNoWidgetsView.getText().length(), noWidgetsViewTextBounds);
            noWidgetsViewHeight = noWidgetsViewTextBounds.height();
        }
        if (!isTwoPane()) {
            doMeasure(
                    makeMeasureSpec(mActivityContext.getDeviceProfile().availableWidthPx,
                            MeasureSpec.EXACTLY),
                    makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
                            MeasureSpec.EXACTLY));
        }
        return getMaxTableHeight(noWidgetsViewHeight);
        return (mDeviceProfile.heightPx - mDeviceProfile.bottomSheetTopPadding)
                * getRecommendationSectionHeightRatio();
    }

    /** b/209579563: "Widgets" header should be focused first. */
@@ -629,12 +641,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
        return mHeaderTitle;
    }

    /**
     * Ratio of recommendations section with respect to bottom sheet's height on scale of 0 to 1.
     */
    @Px
    protected float getMaxTableHeight(@Px float noWidgetsViewHeight) {
        return (mContent.getMeasuredHeight()
                - mTabsHeight - getHeaderViewHeight()
                - noWidgetsViewHeight)
                * RECOMMENDATION_TABLE_HEIGHT_RATIO;
    protected float getRecommendationSectionHeightRatio() {
        return RECOMMENDATION_TABLE_HEIGHT_RATIO;
    }

    private void open(boolean animate) {
@@ -705,6 +717,27 @@ public class WidgetsFullSheet extends BaseWidgetSheet
        return sheet;
    }

    @Override
    public void saveHierarchyState(SparseArray<Parcelable> sparseArray) {
        Bundle bundle = new Bundle();
        // With widget picker open, when we open shade to switch theme, Launcher re-creates the
        // picker and calls save/restore hierarchy state. We save the state of recommendations
        // across those updates.
        bundle.putInt(RECOMMENDATIONS_SAVED_STATE_KEY, mRecommendationsCurrentPage);
        SparseArray<Parcelable> superState = new SparseArray<>();
        super.saveHierarchyState(superState);
        bundle.putSparseParcelableArray(SUPER_SAVED_STATE_KEY, superState);
        sparseArray.put(0, bundle);
    }

    @Override
    public void restoreHierarchyState(SparseArray<Parcelable> sparseArray) {
        Bundle state = (Bundle) sparseArray.get(0);
        mRecommendationsCurrentPage = state.getInt(
                RECOMMENDATIONS_SAVED_STATE_KEY, /*defaultValue=*/0);
        super.restoreHierarchyState(state.getSparseParcelableArray(SUPER_SAVED_STATE_KEY));
    }

    private static int getWidgetSheetId(BaseActivity activity) {
        boolean isTwoPane = (activity.getDeviceProfile().isTablet
                // Enables two pane picker for tablets in all orientations when the
@@ -793,7 +826,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet

    /** private the height, in pixel, + the vertical margins of a given view. */
    protected static int measureHeightWithVerticalMargins(View view) {
        if (view.getVisibility() != VISIBLE) {
        if (view == null || view.getVisibility() != VISIBLE) {
            return 0;
        }
        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
@@ -828,6 +861,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
            saveHierarchyState(widgetsState);
            handleClose(false);
            WidgetsFullSheet sheet = show(BaseActivity.fromContext(getContext()), false);
            sheet.restoreRecommendations(mRecommendedWidgets, mRecommendedWidgetsMap);
            sheet.restoreHierarchyState(widgetsState);
            sheet.restorePreviousAdapterHolderType(getCurrentAdapterHolderType());
        } else if (!isTwoPane()) {
@@ -838,6 +872,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
        mDeviceProfile = dp;
    }

    private void restoreRecommendations(List<WidgetItem> recommendedWidgets,
            Map<WidgetRecommendationCategory, List<WidgetItem>> recommendedWidgetsMap) {
        mRecommendedWidgets = recommendedWidgets;
        mRecommendedWidgetsMap = recommendedWidgetsMap;
    }

    /**
     * Indicates if layout should be re-created on device profile change - so that a different
     * layout can be displayed.
@@ -887,7 +927,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
        }
    }

    @Nullable private View getViewToShowEducationTip() {
    @Nullable
    private View getViewToShowEducationTip() {
        if (mWidgetRecommendationsContainer.getVisibility() == VISIBLE) {
            return mWidgetRecommendationsView.getViewForEducationTip();
        }
+12 −4
Original line number Diff line number Diff line
@@ -62,6 +62,9 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
    private static final int MAXIMUM_WIDTH_LEFT_PANE_FOLDABLE_DP = 395;
    private static final String SUGGESTIONS_PACKAGE_NAME = "widgets_list_suggestions_entry";

    // This ratio defines the max percentage of content area that the recommendations can display
    // with respect to the bottom sheet's height.
    private static final float RECOMMENDATION_SECTION_HEIGHT_RATIO_TWO_PANE = 0.75f;
    private FrameLayout mSuggestedWidgetsContainer;
    private WidgetsListHeader mSuggestedWidgetsHeader;
    private PackageUserKey mSuggestedWidgetsPackageUserKey;
@@ -126,6 +129,10 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
        mWidgetRecommendationsView.initParentViews(mWidgetRecommendationsContainer);
        mWidgetRecommendationsView.setWidgetCellLongClickListener(this);
        mWidgetRecommendationsView.setWidgetCellOnClickListener(this);
        // To save the currently displayed page, so that, it can be requested when rebinding
        // recommendations with different size constraints.
        mWidgetRecommendationsView.addPageSwitchListener(
                newPage -> mRecommendationsCurrentPage = newPage);

        mHeaderTitle = mContent.findViewById(R.id.title);
        mRightPane = mContent.findViewById(R.id.right_pane);
@@ -139,7 +146,6 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
        mPrimaryWidgetListView.setOutlineProvider(mViewOutlineProvider);
        mPrimaryWidgetListView.setClipToOutline(true);

        onRecommendedWidgetsBound();
        onWidgetsBound();
        setUpEducationViewsIfNeeded();

@@ -240,11 +246,13 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
        String suggestionsRightPaneTitle = getContext().getString(
                R.string.widget_picker_right_pane_accessibility_title, suggestionsHeaderTitle);
        packageItemInfo.title = suggestionsHeaderTitle;
        // Suggestions may update at run time. The widgets count on suggestions doesn't add any
        // value, so, we don't show the count.
        WidgetsListHeaderEntry widgetsListHeaderEntry = WidgetsListHeaderEntry.create(
                        packageItemInfo,
                        /*titleSectionName=*/ suggestionsHeaderTitle,
                        /*items=*/ mActivityContext.getPopupDataProvider().getRecommendedWidgets(),
                        /*visibleWidgetsCount=*/ mRecommendedWidgetsCount)
                        /*visibleWidgetsCount=*/ 0)
                .withWidgetListShown();

        mSuggestedWidgetsHeader.applyFromItemInfoWithIcon(widgetsListHeaderEntry);
@@ -266,8 +274,8 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {

    @Override
    @Px
    protected float getMaxTableHeight(@Px float noWidgetsViewHeight) {
        return mContent.getMeasuredHeight() - measureHeightWithVerticalMargins(mHeaderTitle);
    protected float getRecommendationSectionHeightRatio() {
        return RECOMMENDATION_SECTION_HEIGHT_RATIO_TWO_PANE;
    }

    @Override