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

Commit d03518ca authored by Selim Cinek's avatar Selim Cinek
Browse files

Polished the heads up experience

Test: runtest systemui
Fixes: 72748440
Change-Id: I7025119675ed260b5fe53593ea3764918593cc5e
parent 99e9adf5
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -28,11 +28,17 @@ public class CrossFadeHelper {
    public static final long ANIMATION_DURATION_LENGTH = 210;

    public static void fadeOut(final View view, final Runnable endRunnable) {
        fadeOut(view, ANIMATION_DURATION_LENGTH, 0, endRunnable);
    }

    public static void fadeOut(final View view, long duration, int delay,
            final Runnable endRunnable) {
        view.animate().cancel();
        view.animate()
                .alpha(0f)
                .setDuration(ANIMATION_DURATION_LENGTH)
                .setDuration(duration)
                .setInterpolator(Interpolators.ALPHA_OUT)
                .setStartDelay(delay)
                .withEndAction(new Runnable() {
                    @Override
                    public void run() {
@@ -93,6 +99,10 @@ public class CrossFadeHelper {
    }

    public static void fadeIn(final View view) {
        fadeIn(view, ANIMATION_DURATION_LENGTH, 0);
    }

    public static void fadeIn(final View view, long duration, int delay) {
        view.animate().cancel();
        if (view.getVisibility() == View.INVISIBLE) {
            view.setAlpha(0.0f);
@@ -100,7 +110,8 @@ public class CrossFadeHelper {
        }
        view.animate()
                .alpha(1f)
                .setDuration(ANIMATION_DURATION_LENGTH)
                .setDuration(duration)
                .setStartDelay(delay)
                .setInterpolator(Interpolators.ALPHA_IN)
                .withEndAction(null);
        if (view.hasOverlappingRendering()) {
+4 −3
Original line number Diff line number Diff line
@@ -1058,11 +1058,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
        }
    }

    public void setDismissed(boolean dismissed, boolean fromAccessibility) {
        mDismissed = dismissed;
    public void setDismissed(boolean fromAccessibility) {
        mDismissed = true;
        mGroupParentWhenDismissed = mNotificationParent;
        mRefocusOnDismiss = fromAccessibility;
        mChildAfterViewWhenDismissed = null;
        mEntry.icon.setDismissed();
        if (isChildInGroup()) {
            List<ExpandableNotificationRow> notificationChildren =
                    mNotificationParent.getNotificationChildren();
@@ -1146,7 +1147,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
                groupSummary.performDismiss(fromAccessibility);
            }
        }
        setDismissed(true, fromAccessibility);
        setDismissed(fromAccessibility);
        if (isClearable()) {
            if (mOnDismissRunnable != null) {
                mOnDismissRunnable.run();
+35 −5
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -43,7 +42,6 @@ import android.util.FloatProperty;
import android.util.Log;
import android.util.Property;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Interpolator;
@@ -144,6 +142,8 @@ public class StatusBarIconView extends AnimatedImageView {
    private ColorMatrixColorFilter mMatrixColorFilter;
    private boolean mIsInShelf;
    private Runnable mLayoutRunnable;
    private boolean mDismissed;
    private Runnable mOnDismissListener;

    public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
        this(context, slot, sbn, false);
@@ -668,6 +668,19 @@ public class StatusBarIconView extends AnimatedImageView {
    }

    public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) {
        setVisibleState(visibleState, animate, endRunnable, 0);
    }

    /**
     * Set the visibleState of this view.
     *
     * @param visibleState The new state.
     * @param animate Should we animate?
     * @param endRunnable The runnable to run at the end.
     * @param duration The duration of an animation or 0 if the default should be taken.
     */
    public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable,
            long duration) {
        boolean runnableAdded = false;
        if (visibleState != mVisibleState) {
            mVisibleState = visibleState;
@@ -689,7 +702,8 @@ public class StatusBarIconView extends AnimatedImageView {
                    mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT,
                            currentAmount, targetAmount);
                    mIconAppearAnimator.setInterpolator(interpolator);
                    mIconAppearAnimator.setDuration(ANIMATION_DURATION_FAST);
                    mIconAppearAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
                            : duration);
                    mIconAppearAnimator.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
@@ -711,8 +725,9 @@ public class StatusBarIconView extends AnimatedImageView {
                if (targetAmount != currentAmount) {
                    mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT,
                            currentAmount, targetAmount);
                    mDotAnimator.setInterpolator(interpolator);
                    mDotAnimator.setDuration(ANIMATION_DURATION_FAST);
                    mDotAnimator.setInterpolator(interpolator);;
                    mDotAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
                            : duration);
                    final boolean runRunnable = !runnableAdded;
                    mDotAnimator.addListener(new AnimatorListenerAdapter() {
                        @Override
@@ -837,6 +852,21 @@ public class StatusBarIconView extends AnimatedImageView {
        mLayoutRunnable = runnable;
    }

    public void setDismissed() {
        mDismissed = true;
        if (mOnDismissListener != null) {
            mOnDismissListener.run();
        }
    }

    public boolean isDismissed() {
        return mDismissed;
    }

    public void setOnDismissListener(Runnable onDismissListener) {
        mOnDismissListener = onDismissListener;
    }

    public interface OnVisibilityChangedListener {
        void onVisibilityChanged(int newVisibility);
    }
+22 −11
Original line number Diff line number Diff line
@@ -16,17 +16,13 @@

package com.android.systemui.statusbar.phone;

import android.app.Notification;
import android.app.PendingIntent;
import android.graphics.Rect;
import android.service.notification.StatusBarNotification;
import android.util.EventLog;
import android.util.Log;
import android.view.View;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.NotificationData;
@@ -34,13 +30,13 @@ import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;

import java.util.stream.Stream;

/**
 * Controls the appearance of heads up notifications in the icon area and the header itself.
 */
class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
        DarkIconDispatcher.DarkReceiver {
    public static final int CONTENT_FADE_DURATION = 110;
    public static final int CONTENT_FADE_DELAY = 100;
    private final NotificationIconAreaController mNotificationIconAreaController;
    private final HeadsUpManagerPhone mHeadsUpManager;
    private final NotificationStackScrollLayout mStackScroller;
@@ -98,24 +94,39 @@ class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
        NotificationData.Entry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
        mHeadsUpStatusBarView.setEntry(newEntry);
        if (newEntry != previousEntry) {
            boolean animateIsolation = false;
            if (newEntry == null) {
                // no heads up anymore, lets start the disappear animation

                setShown(false);
                animateIsolation = !mIsExpanded;
            } else if (previousEntry == null) {
                // We now have a headsUp and didn't have one before. Let's start the disappear
                // animation
                setShown(true);
            }
            mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
                    : newEntry.icon, mHeadsUpStatusBarView.getIconDrawingRect());
                    : newEntry.icon, mHeadsUpStatusBarView.getIconDrawingRect(), animateIsolation);
        }
    }

    private void setShown(boolean isShown) {
        if (mShown != isShown) {
            mShown = isShown;
        mHeadsUpStatusBarView.setVisibility(isShown ? View.VISIBLE : View.GONE);
        mClockView.setVisibility(!isShown ? View.VISIBLE : View.INVISIBLE);
            if (isShown) {
                mHeadsUpStatusBarView.setVisibility(View.VISIBLE);
                CrossFadeHelper.fadeIn(mHeadsUpStatusBarView, CONTENT_FADE_DURATION /* duration */,
                        CONTENT_FADE_DELAY /* delay */);
                CrossFadeHelper.fadeOut(mClockView, CONTENT_FADE_DURATION/* duration */,
                        0 /* delay */, () -> mClockView.setVisibility(View.INVISIBLE));
            } else {
                CrossFadeHelper.fadeIn(mClockView, CONTENT_FADE_DURATION /* duration */,
                        CONTENT_FADE_DELAY /* delay */);
                CrossFadeHelper.fadeOut(mHeadsUpStatusBarView, CONTENT_FADE_DURATION/* duration */,
                        0 /* delay */, () -> mHeadsUpStatusBarView.setVisibility(View.GONE));

            }
        }
    }

    @VisibleForTesting
+32 −17
Original line number Diff line number Diff line
@@ -12,9 +12,11 @@ import android.widget.FrameLayout;

import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationEntryManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -31,6 +33,8 @@ import java.util.function.Function;
 */
public class NotificationIconAreaController implements DarkReceiver {
    private final NotificationColorUtil mNotificationColorUtil;
    private final NotificationEntryManager mEntryManager;
    private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;

    private int mIconSize;
    private int mIconHPadding;
@@ -48,6 +52,7 @@ public class NotificationIconAreaController implements DarkReceiver {
        mStatusBar = statusBar;
        mNotificationColorUtil = NotificationColorUtil.getInstance(context);
        mContext = context;
        mEntryManager = Dependency.get(NotificationEntryManager.class);

        initializeNotificationAreaViews(context);
    }
@@ -129,8 +134,8 @@ public class NotificationIconAreaController implements DarkReceiver {
    }

    protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
            NotificationData notificationData, boolean showAmbient) {
        if (notificationData.isAmbient(entry.key) && !showAmbient) {
            boolean showAmbient, boolean hideDismissed) {
        if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
            return false;
        }
        if (!StatusBar.isTopLevelChild(entry)) {
@@ -139,9 +144,13 @@ public class NotificationIconAreaController implements DarkReceiver {
        if (entry.row.getVisibility() == View.GONE) {
            return false;
        }
        if (entry.row.isDismissed() && hideDismissed) {
            return false;
        }

        // showAmbient == show in shade but not shelf
        if (!showAmbient && notificationData.shouldSuppressStatusBar(entry.key)) {
        if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(
                entry.key)) {
            return false;
        }

@@ -151,28 +160,30 @@ public class NotificationIconAreaController implements DarkReceiver {
    /**
     * Updates the notifications with the given list of notifications to display.
     */
    public void updateNotificationIcons(NotificationData notificationData) {
    public void updateNotificationIcons() {

        updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons,
                false /* showAmbient */);
        updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons,
                NotificationShelf.SHOW_AMBIENT_ICONS);
        updateStatusBarIcons();
        updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
                NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */);

        applyNotificationIconsTint();
    }

    private void updateStatusBarIcons() {
        updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
                false /* showAmbient */, true /* hideDismissed */);
    }

    /**
     * Updates the notification icons for a host layout. This will ensure that the notification
     * host layout will have the same icons like the ones in here.
     *
     * @param notificationData the notification data to look up which notifications are relevant
     * @param function A function to look up an icon view based on an entry
     * @param hostLayout which layout should be updated
     * @param showAmbient should ambient notification icons be shown
     * @param hideDismissed should dismissed icons be hidden
     */
    private void updateIconsForLayout(NotificationData notificationData,
            Function<NotificationData.Entry, StatusBarIconView> function,
            NotificationIconContainer hostLayout, boolean showAmbient) {
    private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
            NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed) {
        ArrayList<StatusBarIconView> toShow = new ArrayList<>(
                mNotificationScrollLayout.getChildCount());

@@ -181,7 +192,7 @@ public class NotificationIconAreaController implements DarkReceiver {
            View view = mNotificationScrollLayout.getChildAt(i);
            if (view instanceof ExpandableNotificationRow) {
                NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
                if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) {
                if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed)) {
                    toShow.add(function.apply(ent));
                }
            }
@@ -243,10 +254,13 @@ public class NotificationIconAreaController implements DarkReceiver {

        final FrameLayout.LayoutParams params = generateIconLayoutParams();
        for (int i = 0; i < toShow.size(); i++) {
            View v = toShow.get(i);
            StatusBarIconView v = toShow.get(i);
            // The view might still be transiently added if it was just removed and added again
            hostLayout.removeTransientView(v);
            if (v.getParent() == null) {
                if (hideDismissed) {
                    v.setOnDismissListener(mUpdateStatusBarIcons);
                }
                hostLayout.addView(v, i, params);
            }
        }
@@ -297,7 +311,8 @@ public class NotificationIconAreaController implements DarkReceiver {
        mShelfIcons.setDark(dark, false, 0);
    }

    public void showIconIsolated(StatusBarIconView icon, Rect absoluteIconPosition) {
        mNotificationIcons.showIconIsolated(icon, absoluteIconPosition);
    public void showIconIsolated(StatusBarIconView icon, Rect absoluteIconPosition,
            boolean animated) {
        mNotificationIcons.showIconIsolated(icon, absoluteIconPosition, animated);
    }
}
Loading