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

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

Merge "Maintain the recommendations that were initially displayed" into main

parents 4c5472e6 168d4204
Loading
Loading
Loading
Loading
+81 −15
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@ package com.android.launcher3.widget.picker;

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 androidx.annotation.Px;
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 @@ import java.util.function.Consumer;
 */
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 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
    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 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
        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 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
        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 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
        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 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
            }
        }

        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 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
    }

    /**
     * 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 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
        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. */
+5 −11
Original line number Diff line number Diff line
@@ -633,15 +633,15 @@ public class WidgetsFullSheet extends BaseWidgetSheet
    }

    @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 @@ 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 getRecommendationSectionHeightRatio() {
        return RECOMMENDATION_TABLE_HEIGHT_RATIO;
    }

    private void open(boolean animate) {
        if (animate) {
            if (getPopupContainer().getInsets().bottom > 0) {
@@ -733,6 +725,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
        // 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 @@ public class WidgetsFullSheet extends BaseWidgetSheet
        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));
    }

+4 −3
Original line number Diff line number Diff line
@@ -83,14 +83,15 @@ public final class WidgetsRecommendationTableLayout extends TableLayout {
     * <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) {
+9 −2
Original line number Diff line number Diff line
@@ -276,8 +276,15 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {

    @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