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

Commit 8cf83077 authored by Steve Elliott's avatar Steve Elliott Committed by Android (Google) Code Review
Browse files

Merge "Allow moving notifs when user changes bundle setting" into main

parents fc739f83 58c8c22f
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -591,9 +591,8 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
        underTest = factory.create(entry) as NotificationEntryAdapter

        underTest.onBundleDisabled()
        assertThat(underTest.isMarkedForUserTriggeredMovement).isTrue()
        verify(kosmos.mockVisualStabilityCoordinator)
            .temporarilyAllowSectionChanges(eq(entry), anyLong())
            .temporarilyAllowFreeMovement(eq(entry), anyLong())
    }

    @Test
@@ -630,8 +629,8 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
        underTest = factory.create(summaryEntry) as NotificationEntryAdapter
        underTest.onBundleDisabled()
        verify(kosmos.mockVisualStabilityCoordinator)
            .temporarilyAllowSectionChanges(eq(summaryEntry), anyLong())
            .temporarilyAllowFreeMovement(eq(summaryEntry), anyLong())
        verify(kosmos.mockVisualStabilityCoordinator)
            .temporarilyAllowSectionChanges(eq(childEntry), anyLong())
            .temporarilyAllowFreeMovement(eq(childEntry), anyLong())
    }
}
+1 −3
Original line number Diff line number Diff line
@@ -291,9 +291,7 @@ class NotificationEntryAdapter(
    }

    override fun onBundleDisabled() {
        markForUserTriggeredMovement(true)
        onImportanceChanged()

        visualStabilityCoordinator.temporarilyAllowFreeMovement(entry, SystemClock.uptimeMillis())
        if (isGroupRoot()) {
            row.attachedChildren?.forEach { it.entryAdapter.onBundleDisabled() }
        }
+66 −4
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.android.systemui.Dumpable;
@@ -47,6 +48,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository;
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.shared.NotificationMinimalism;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -109,6 +111,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
    // value: runnable that when run removes its associated RemoveOverrideSuppressionRunnable
    // from the DelayableExecutor's queue
    private Map<String, Runnable> mEntriesThatCanChangeSection = new HashMap<>();
    private Map<String, Runnable> mEntriesThatCanMoveFreely = new HashMap<>();

    @VisibleForTesting
    protected static final long ALLOW_SECTION_CHANGE_TIMEOUT = 500;
@@ -324,10 +327,12 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
                        isGroupChangeAllowedForEntry =
                                isEveryChangeAllowed()
                                        || canReorderNotificationEntry(entry)
                                        || canMoveForHeadsUp(entry);
                                        || canMoveForHeadsUp(entry)
                                        || canFreelyMoveEntry(entry);
                    } else {
                        isGroupChangeAllowedForEntry = mReorderingAllowed
                                || canMoveForHeadsUp(entry);
                                || canMoveForHeadsUp(entry)
                                || canFreelyMoveEntry(entry);
                    }
                    mIsSuppressingParentChange |= !isGroupChangeAllowedForEntry;
                    return isGroupChangeAllowedForEntry;
@@ -335,7 +340,8 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {

                @Override
                public boolean isParentChangeAllowed(@NonNull GroupEntry entry) {
                    final boolean isBundleChangeAllowedForGroup = isEveryChangeAllowed();
                    final boolean isBundleChangeAllowedForGroup = isEveryChangeAllowed()
                            || canFreelyMoveEntry(entry);
                    mIsSuppressingParentChange |= !isBundleChangeAllowedForGroup;
                    return isBundleChangeAllowedForGroup;
                }
@@ -387,7 +393,9 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
                        }

                        return canReorderNotificationEntry(notificationEntry)
                                || canMoveForHeadsUp(notificationEntry);
                                || canMoveForHeadsUp(notificationEntry)
                                || (notificationEntry != null
                                        && canFreelyMoveEntry(notificationEntry));
                    } else {
                        return mReorderingAllowed || canMoveForHeadsUp(notificationEntry);
                    }
@@ -497,6 +505,41 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
        }
    }

    /**
     * Allows this notification entry to be re-ordered and re-parented in the notification list
     * temporarily until the timeout has passed.
     *
     * Typically this is allowed because the user has directly changed something about the
     * notification and we are reordering based on the user's change.
     *
     * @param entry notification entry that can move freely even if it would be otherwise suppressed
     * @param now current time SystemClock.elapsedRealtime
     */
    public void temporarilyAllowFreeMovement(@NonNull NotificationEntry entry, long now) {
        if (NotificationBundleUi.isUnexpectedlyInLegacyMode()) {
            return;
        }
        final String entryKey = entry.getKey();
        final Runnable existing = mEntriesThatCanMoveFreely.get(entryKey);
        final boolean wasAllowedToMoveFreely = existing != null;

        // If it exists, cancel previous timeout
        if (wasAllowedToMoveFreely) {
            existing.run();
        }

        // Schedule & store new timeout cancellable
        mEntriesThatCanMoveFreely.put(
                entryKey,
                mDelayableExecutor.executeAtTime(
                        () -> mEntriesThatCanMoveFreely.remove(entryKey),
                        now + ALLOW_SECTION_CHANGE_TIMEOUT));

        if (!wasAllowedToMoveFreely) {
            mNotifStabilityManager.invalidateList("temporarilyAllowFreeMovement");
        }
    }

    /**
     * Allows this notification entry to be re-ordered in the notification list temporarily until
     * the timeout has passed.
@@ -529,6 +572,25 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
        }
    }

    private boolean canFreelyMoveEntry(@NonNull GroupEntry entry) {
        if (!NotificationBundleUi.isEnabled()) {
            return false;
        }
        @Nullable NotificationEntry representativeEntry = entry.getRepresentativeEntry();
        if (representativeEntry == null) {
            return false;
        }
        return canFreelyMoveEntry(representativeEntry);
    }

    private boolean canFreelyMoveEntry(@NonNull NotificationEntry entry) {
        if (NotificationBundleUi.isEnabled()) {
            return mEntriesThatCanMoveFreely.containsKey(entry.getKey());
        } else {
            return false;
        }
    }

    final StatusBarStateController.StateListener mStatusBarStateControllerListener =
            new StatusBarStateController.StateListener() {
                @Override
+10 −40
Original line number Diff line number Diff line
@@ -15,34 +15,17 @@
 */
package com.android.systemui.statusbar.notification.row

import android.app.INotificationManager
import android.app.NotificationChannel.NEWS_ID
import android.app.NotificationChannel.PROMOTIONS_ID
import android.app.NotificationChannel.RECS_ID
import android.app.NotificationChannel.SOCIAL_MEDIA_ID
import android.content.Context
import android.content.pm.PackageManager
import android.os.RemoteException
import android.service.notification.Adjustment
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.AssistantFeedbackController
import com.android.systemui.statusbar.notification.collection.EntryAdapter
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.google.android.material.materialswitch.MaterialSwitch


/**
 * The guts of a notification revealed when performing a long press, specifically for notifications
 * that are bundled. Contains controls to allow user to disable the feature for the app that posted
@@ -63,20 +46,16 @@ class BundledNotificationInfo(context: Context?, attrs: AttributeSet?) :
        toggle.setOnCheckedChangeListener { buttonView, isChecked ->
            val isAChange = isChecked != enabled
            val done = findViewById<TextView>(R.id.done)
            done.setText(
                if (isAChange)
                    R.string.inline_ok_button
                else
                    R.string.inline_done_button
            )
            done.setText(if (isAChange) R.string.inline_ok_button else R.string.inline_done_button)
        }

        val done = findViewById<TextView>(R.id.done)
        done.setOnClickListener {
            try {
                if (NotificationBundleUi.isEnabled) {
                    mEntryAdapter.markForUserTriggeredMovement(true)
                    mEntryAdapter.onImportanceChanged()
                    if (enabled && !toggle.isChecked) {
                        mEntryAdapter.onBundleDisabled()
                    }
                } else {
                    mEntry.markForUserTriggeredMovement(true)
                    mOnUserInteractionCallback.onImportanceChanged(mEntry)
@@ -92,27 +71,18 @@ class BundledNotificationInfo(context: Context?, attrs: AttributeSet?) :
                throw RuntimeException(e)
            }
        }
        done.setText(
            if (enabled)
                R.string.inline_done_button
            else
                R.string.inline_ok_button
        )
        done.setText(if (enabled) R.string.inline_done_button else R.string.inline_ok_button)
        done.setAccessibilityDelegate(mGutsContainer.accessibilityDelegate)
        val toggleWrapper = findViewById<ViewGroup>(R.id.classification_toggle)
        toggleWrapper.setOnClickListener {
            toggle.performClick()
        }
        toggleWrapper.setOnClickListener { toggle.performClick() }

        findViewById<TextView>(R.id.feature_summary).setText(
            resources.getString(R.string.notification_guts_bundle_summary, mAppName));
        findViewById<TextView>(R.id.feature_summary)
            .setText(resources.getString(R.string.notification_guts_bundle_summary, mAppName))

        val dismissButton = findViewById<View>(R.id.inline_dismiss)
        dismissButton.setOnClickListener(mOnCloseClickListener)
        dismissButton.visibility = if (dismissButton.hasOnClickListeners() && mIsDismissable)
            VISIBLE
        else
            GONE
        dismissButton.visibility =
            if (dismissButton.hasOnClickListeners() && mIsDismissable) VISIBLE else GONE
    }

    companion object {