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

Commit 034d74da authored by Tony Wickham's avatar Tony Wickham Committed by Android (Google) Code Review
Browse files

Merge "Show more shortcuts when last notification is dimissed" into ub-launcher3-dorval-polish

parents 13fc445b 887bd1fe
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -22,10 +22,18 @@
    android:elevation="@dimen/deep_shortcuts_elevation">

    <LinearLayout
        android:id="@+id/deep_shortcuts"
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <!-- The shortcuts header is added at runtime when necessary. -->

        <LinearLayout
            android:id="@+id/shortcuts"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical" />
    </LinearLayout>

</com.android.launcher3.shortcuts.ShortcutsItemView>
+4 −1
Original line number Diff line number Diff line
@@ -21,4 +21,7 @@
    android:layout_height="@dimen/system_shortcut_header_height"
    android:orientation="horizontal"
    android:gravity="end|center_vertical"
    android:background="?attr/popupColorSecondary" />
    android:background="?attr/popupColorSecondary"
    android:elevation="1dp"
    android:outlineProvider="none" />
    <!-- We have elevation so this is drawn on top, but no outline provider to remove shadow -->
+8 −3
Original line number Diff line number Diff line
@@ -86,11 +86,16 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
        return getHeight() - footerHeight;
    }

    public Animator animateHeightRemoval(int heightToRemove) {
    public Animator animateHeightRemoval(int heightToRemove, boolean shouldRemoveFromTop) {
        Rect startRect = new Rect(mPillRect);
        Rect endRect = new Rect(mPillRect);
        if (shouldRemoveFromTop) {
            endRect.top += heightToRemove;
        } else {
            endRect.bottom -= heightToRemove;
        }
        return new RoundedRectRevealOutlineProvider(getBackgroundRadius(), getBackgroundRadius(),
                mPillRect, endRect, mRoundedCorners).createRevealAnimator(this, false);
                startRect, endRect, mRoundedCorners).createRevealAnimator(this, false);
    }

    public void updateHeader(int notificationCount, @Nullable IconPalette palette) {
+49 −17
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -191,7 +192,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
        // Add dummy views first, and populate with real info when ready.
        PopupPopulator.Item[] itemsToPopulate = PopupPopulator
                .getItemsToPopulate(shortcutIds, notificationKeys, systemShortcuts);
        addDummyViews(itemsToPopulate, notificationKeys.size() > 1);
        addDummyViews(itemsToPopulate, notificationKeys.size());

        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
        orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
@@ -202,7 +203,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
            mNotificationItemView = null;
            mShortcutsItemView = null;
            itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate);
            addDummyViews(itemsToPopulate, notificationKeys.size() > 1);
            addDummyViews(itemsToPopulate, notificationKeys.size());

            measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
            orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
@@ -252,8 +253,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
                systemShortcuts, systemShortcutViews));
    }

    private void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate,
            boolean notificationFooterHasIcons) {
    private void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate, int numNotifications) {
        final Resources res = getResources();
        final LayoutInflater inflater = mLauncher.getLayoutInflater();

@@ -274,6 +274,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra

            if (itemTypeToPopulate == PopupPopulator.Item.NOTIFICATION) {
                mNotificationItemView = (NotificationItemView) item;
                boolean notificationFooterHasIcons = numNotifications > 1;
                int footerHeight = notificationFooterHasIcons ?
                        res.getDimensionPixelSize(R.dimen.notification_footer_height) : 0;
                item.findViewById(R.id.footer).getLayoutParams().height = footerHeight;
@@ -316,6 +317,9 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
        int backgroundColor = Themes.getAttrColor(mLauncher, mNotificationItemView == null
                ? R.attr.popupColorPrimary : R.attr.popupColorSecondary);
        mShortcutsItemView.setBackgroundWithCorners(backgroundColor, shortcutsItemRoundedCorners);
        if (numNotifications > 0) {
            mShortcutsItemView.hideShortcuts(mIsAboveIcon, MAX_SHORTCUTS_IF_NOTIFICATIONS);
        }
    }

    protected PopupItemView getItemViewAt(int index) {
@@ -639,11 +643,22 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
        ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag();
        BadgeInfo badgeInfo = updatedBadges.get(PackageUserKey.fromItemInfo(originalInfo));
        if (badgeInfo == null || badgeInfo.getNotificationKeys().size() == 0) {
            // There are no more notifications, so create an animation to remove
            // the notifications view and expand the shortcuts view (if possible).
            AnimatorSet removeNotification = LauncherAnimUtils.createAnimatorSet();
            int hiddenShortcutsHeight = 0;
            if (mShortcutsItemView != null) {
                hiddenShortcutsHeight = mShortcutsItemView.getHiddenShortcutsHeight();
                int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary);
                // With notifications gone, all corners of shortcuts item should be rounded.
                mShortcutsItemView.setBackgroundWithCorners(backgroundColor,
                        ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS);
                removeNotification.play(mShortcutsItemView.showAllShortcuts(mIsAboveIcon));
            }
            final int duration = getResources().getInteger(
                    R.integer.config_removeNotificationViewDuration);
            removeNotification.play(reduceNotificationViewHeight(
                    mNotificationItemView.getHeightMinusFooter(), duration));
            removeNotification.play(adjustItemHeights(mNotificationItemView.getHeightMinusFooter(),
                    hiddenShortcutsHeight, duration));
            Animator fade = ObjectAnimator.ofFloat(mNotificationItemView, ALPHA, 0)
                    .setDuration(duration);
            fade.addListener(new AnimatorListenerAdapter() {
@@ -665,12 +680,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
            showArrow.setStartDelay((long) (duration - arrowScaleDuration * 1.5));
            removeNotification.playSequentially(hideArrow, showArrow);
            removeNotification.start();
            if (mShortcutsItemView != null) {
                int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary);
                // With notifications gone, all corners of shortcuts item should be rounded.
                mShortcutsItemView.setBackgroundWithCorners(backgroundColor,
                        ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS);
            }
            return;
        }
        mNotificationItemView.trimNotifications(NotificationKeyData.extractKeysOnly(
@@ -689,28 +698,50 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
                mArrow, new PropertyListBuilder().scale(scale).build());
    }

    public Animator reduceNotificationViewHeight(int heightToRemove, int duration) {
        return adjustItemHeights(heightToRemove, 0, duration);
    }

    /**
     * Animates the height of the notification item and the translationY of other items accordingly.
     */
    public Animator reduceNotificationViewHeight(int heightToRemove, int duration) {
    public Animator adjustItemHeights(int notificationHeightToRemove, int shortcutHeightToAdd,
            int duration) {
        if (mReduceHeightAnimatorSet != null) {
            mReduceHeightAnimatorSet.cancel();
        }
        final int translateYBy = mIsAboveIcon ? heightToRemove : -heightToRemove;
        final int translateYBy = mIsAboveIcon ? notificationHeightToRemove - shortcutHeightToAdd
                : -notificationHeightToRemove;
        mReduceHeightAnimatorSet = LauncherAnimUtils.createAnimatorSet();
        mReduceHeightAnimatorSet.play(mNotificationItemView.animateHeightRemoval(heightToRemove));
        boolean removingNotification =
                notificationHeightToRemove == mNotificationItemView.getHeightMinusFooter();
        boolean shouldRemoveNotificationHeightFromTop = mIsAboveIcon && removingNotification;
        mReduceHeightAnimatorSet.play(mNotificationItemView.animateHeightRemoval(
                notificationHeightToRemove, shouldRemoveNotificationHeightFromTop));
        PropertyResetListener<View, Float> resetTranslationYListener
                = new PropertyResetListener<>(TRANSLATION_Y, 0f);
        boolean itemIsAfterShortcuts = false;
        for (int i = 0; i < getItemCount(); i++) {
            final PopupItemView itemView = getItemViewAt(i);
            if (!mIsAboveIcon && itemView == mNotificationItemView) {
                // The notification view is already in the right place when container is below icon.
            if (itemIsAfterShortcuts) {
                // Every item after the shortcuts item needs to adjust for the new height.
                itemView.setTranslationY(itemView.getTranslationY() - shortcutHeightToAdd);
            }
            if (itemView == mNotificationItemView && (!mIsAboveIcon || removingNotification)) {
                // The notification view is already in the right place.
                continue;
            }
            ValueAnimator translateItem = ObjectAnimator.ofFloat(itemView, TRANSLATION_Y,
                    itemView.getTranslationY() + translateYBy).setDuration(duration);
            translateItem.addListener(resetTranslationYListener);
            mReduceHeightAnimatorSet.play(translateItem);
            if (itemView == mShortcutsItemView) {
                itemIsAfterShortcuts = true;
            }
        }
        if (mIsAboveIcon) {
            // We also need to adjust the arrow position to account for the new shortcuts height.
            mArrow.setTranslationY(mArrow.getTranslationY() - shortcutHeightToAdd);
        }
        mReduceHeightAnimatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
@@ -720,6 +751,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
                    // container itself did not. This means the items would jump back to their
                    // original translation unless we update the container's translationY here.
                    setTranslationY(getTranslationY() + translateYBy);
                    mArrow.setTranslationY(0);
                }
                mReduceHeightAnimatorSet = null;
            }
+11 −14
Original line number Diff line number Diff line
@@ -52,9 +52,9 @@ import java.util.List;
 */
public class PopupPopulator {

    public static final int MAX_ITEMS = 4;
    public static final int MAX_SHORTCUTS = 4;
    @VisibleForTesting static final int NUM_DYNAMIC = 2;
    private static final int MAX_SHORTCUTS_IF_NOTIFICATIONS = 2;
    public static final int MAX_SHORTCUTS_IF_NOTIFICATIONS = 2;

    public enum Item {
        SHORTCUT(R.layout.deep_shortcut, true),
@@ -77,10 +77,7 @@ public class PopupPopulator {
        boolean hasNotifications = notificationKeys.size() > 0;
        int numNotificationItems = hasNotifications ? 1 : 0;
        int numShortcuts = shortcutIds.size();
        if (hasNotifications && numShortcuts > MAX_SHORTCUTS_IF_NOTIFICATIONS) {
            numShortcuts = MAX_SHORTCUTS_IF_NOTIFICATIONS;
        }
        int numItems = Math.min(MAX_ITEMS, numShortcuts + numNotificationItems)
        int numItems = Math.min(MAX_SHORTCUTS, numShortcuts) + numNotificationItems
                + systemShortcuts.size();
        Item[] items = new Item[numItems];
        for (int i = 0; i < numItems; i++) {
@@ -126,12 +123,12 @@ public class PopupPopulator {
    };

    /**
     * Filters the shortcuts so that only MAX_ITEMS or fewer shortcuts are retained.
     * Filters the shortcuts so that only MAX_SHORTCUTS or fewer shortcuts are retained.
     * We want the filter to include both static and dynamic shortcuts, so we always
     * include NUM_DYNAMIC dynamic shortcuts, if at least that many are present.
     *
     * @param shortcutIdToRemoveFirst An id that should be filtered out first, if any.
     * @return a subset of shortcuts, in sorted order, with size <= MAX_ITEMS.
     * @return a subset of shortcuts, in sorted order, with size <= MAX_SHORTCUTS.
     */
    public static List<ShortcutInfoCompat> sortAndFilterShortcuts(
            List<ShortcutInfoCompat> shortcuts, @Nullable String shortcutIdToRemoveFirst) {
@@ -147,27 +144,27 @@ public class PopupPopulator {
        }

        Collections.sort(shortcuts, SHORTCUT_RANK_COMPARATOR);
        if (shortcuts.size() <= MAX_ITEMS) {
        if (shortcuts.size() <= MAX_SHORTCUTS) {
            return shortcuts;
        }

        // The list of shortcuts is now sorted with static shortcuts followed by dynamic
        // shortcuts. We want to preserve this order, but only keep MAX_ITEMS.
        List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_ITEMS);
        // shortcuts. We want to preserve this order, but only keep MAX_SHORTCUTS.
        List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
        int numDynamic = 0;
        int size = shortcuts.size();
        for (int i = 0; i < size; i++) {
            ShortcutInfoCompat shortcut = shortcuts.get(i);
            int filteredSize = filteredShortcuts.size();
            if (filteredSize < MAX_ITEMS) {
                // Always add the first MAX_ITEMS to the filtered list.
            if (filteredSize < MAX_SHORTCUTS) {
                // Always add the first MAX_SHORTCUTS to the filtered list.
                filteredShortcuts.add(shortcut);
                if (shortcut.isDynamic()) {
                    numDynamic++;
                }
                continue;
            }
            // At this point, we have MAX_ITEMS already, but they may all be static.
            // At this point, we have MAX_SHORTCUTS already, but they may all be static.
            // If there are dynamic shortcuts, remove static shortcuts to add them.
            if (shortcut.isDynamic() && numDynamic < NUM_DYNAMIC) {
                numDynamic++;
Loading