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

Commit e7c670ea authored by Matías Hernández's avatar Matías Hernández Committed by Android (Google) Code Review
Browse files

Merge "Fix refresh of "apps that can interrupt" segment" into main

parents 42815119 195c698c
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -49,7 +49,9 @@ public class CircularIconsPreference extends RestrictedPreference {

    private static final float DISABLED_ITEM_ALPHA = 0.3f;

    record LoadedIcons(ImmutableList<Drawable> icons, int extraItems) { }
    record LoadedIcons(ImmutableList<Drawable> icons, int extraItems) {
        static final LoadedIcons EMPTY = new LoadedIcons(ImmutableList.of(), 0);
    }

    private Executor mUiExecutor;

@@ -126,6 +128,7 @@ public class CircularIconsPreference extends RestrictedPreference {
            // We know what icons we want, but haven't yet loaded them.
            if (mIconSet.size() == 0) {
                container.setVisibility(View.GONE);
                mLoadedIcons = LoadedIcons.EMPTY;
                return;
            }
            container.setVisibility(View.VISIBLE);
@@ -137,7 +140,7 @@ public class CircularIconsPreference extends RestrictedPreference {
                            @Override
                            public void onGlobalLayout() {
                                container.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                                startLoadingIcons(container, mIconSet);
                                notifyChanged();
                            }
                        }
                );
+20 −6
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
import android.app.Application;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -49,6 +51,7 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;

/**
 * Preference with a link and summary about what apps can break through the mode
@@ -65,24 +68,26 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
    private ZenMode mZenMode;
    private CircularIconsPreference mPreference;
    private final Fragment mHost;
    private final Function<ApplicationInfo, Drawable> mAppIconRetriever;

    ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host,
            ZenModesBackend backend, ZenHelperBackend helperBackend) {
        this(context, key, host,
                ApplicationsState.getInstance((Application) context.getApplicationContext()),
                backend, helperBackend);
                backend, helperBackend, appInfo -> Utils.getBadgedIcon(context, appInfo));
    }

    @VisibleForTesting
    ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host,
            ApplicationsState applicationsState, ZenModesBackend backend,
            ZenHelperBackend helperBackend) {
            ZenHelperBackend helperBackend, Function<ApplicationInfo, Drawable> appIconRetriever) {
        super(context, key, backend);
        mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend);
        mHelperBackend = helperBackend;
        mApplicationsState = applicationsState;
        mUserManager = context.getSystemService(UserManager.class);
        mHost = host;
        mAppIconRetriever = appIconRetriever;
    }

    @Override
@@ -105,13 +110,18 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
        if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
            mPreference.setSummary(R.string.zen_mode_apps_none_apps);
            mPreference.displayIcons(CircularIconSet.EMPTY);
            if (mAppSession != null) {
                mAppSession.deactivateSession();
            }
        } else {
            if (TextUtils.isEmpty(mPreference.getSummary())) {
                mPreference.setSummary(R.string.zen_mode_apps_calculating);
            }
            if (mApplicationsState != null && mHost != null) {
            if (mAppSession == null) {
                mAppSession = mApplicationsState.newSession(mAppSessionCallbacks,
                        mHost.getLifecycle());
            } else {
                mAppSession.activateSession();
            }
            triggerUpdateAppsBypassingDnd();
        }
@@ -133,12 +143,16 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
    }

    private void displayAppsBypassingDnd(List<AppEntry> allApps) {
        ImmutableList<AppEntry> apps = getAppsBypassingDndSortedByName(allApps);
        if (mZenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
            // Can get this callback when resuming, if we had CHANNEL_POLICY_PRIORITY and just
            // switched to CHANNEL_POLICY_NONE.
            return;
        }

        ImmutableList<AppEntry> apps = getAppsBypassingDndSortedByName(allApps);
        mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, apps));

        mPreference.displayIcons(new CircularIconSet<>(apps,
                app -> Utils.getBadgedIcon(mContext, app.info)),
                app -> mAppIconRetriever.apply(app.info)),
                APP_ENTRY_EQUIVALENCE);
    }

+87 −2
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.UserInfo;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -103,11 +104,12 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
        mContext = RuntimeEnvironment.application;
        CircularIconSet.sExecutorService = MoreExecutors.newDirectExecutorService();
        mPreference = new TestableCircularIconsPreference(mContext);

        when(mApplicationsState.newSession(any(), any())).thenReturn(mSession);

        mController = new ZenModeAppsLinkPreferenceController(
                mContext, "controller_key", mock(Fragment.class), mApplicationsState,
                mZenModesBackend, mHelperBackend);
                mZenModesBackend, mHelperBackend,
                /* appIconRetriever= */ appInfo -> new ColorDrawable());

        // Ensure the preference view is bound & measured (needed to add child ImageViews).
        View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(),
@@ -296,6 +298,89 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
        verify(mSession, times(2)).rebuild(any(), any(), eq(false));
    }

    @Test
    public void updateState_noneToPriority_loadsBypassingAppsAndListensForChanges() {
        ZenMode zenModeWithNone = new TestModeBuilder()
                .setZenPolicy(new ZenPolicy.Builder().allowPriorityChannels(false).build())
                .build();
        ZenMode zenModeWithPriority = new TestModeBuilder()
                .setZenPolicy(new ZenPolicy.Builder().allowPriorityChannels(true).build())
                .build();
        ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
        appEntries.add(createAppEntry("test", mContext.getUserId()));
        when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId(), false))
                .thenReturn(List.of("test"));

        mController.updateState(mPreference, zenModeWithNone);

        assertThat(mPreference.getLoadedIcons().icons()).hasSize(0);
        verifyNoMoreInteractions(mApplicationsState);
        verifyNoMoreInteractions(mSession);

        mController.updateState(mPreference, zenModeWithPriority);

        verify(mApplicationsState).newSession(any(), any());
        verify(mSession).rebuild(any(), any(), anyBoolean());
        mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
        assertThat(mPreference.getLoadedIcons().icons()).hasSize(1);
    }

    @Test
    public void updateState_priorityToNone_clearsBypassingAppsAndStopsListening() {
        ZenMode zenModeWithNone = new TestModeBuilder()
                .setZenPolicy(new ZenPolicy.Builder().allowPriorityChannels(false).build())
                .build();
        ZenMode zenModeWithPriority = new TestModeBuilder()
                .setZenPolicy(new ZenPolicy.Builder().allowPriorityChannels(true).build())
                .build();
        ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
        appEntries.add(createAppEntry("test", mContext.getUserId()));
        when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId(), false))
                .thenReturn(List.of("test"));

        mController.updateState(mPreference, zenModeWithPriority);

        verify(mApplicationsState).newSession(any(), any());
        verify(mSession).rebuild(any(), any(), anyBoolean());
        mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
        assertThat(mPreference.getLoadedIcons().icons()).hasSize(1);

        mController.updateState(mPreference, zenModeWithNone);

        assertThat(mPreference.getLoadedIcons().icons()).hasSize(0);
        verify(mSession).deactivateSession();
        verifyNoMoreInteractions(mSession);
        verifyNoMoreInteractions(mApplicationsState);

        // An errant callback (triggered by onResume and received asynchronously after
        // updateState()) is ignored.
        mController.mAppSessionCallbacks.onRebuildComplete(appEntries);

        assertThat(mPreference.getLoadedIcons().icons()).hasSize(0);
    }

    @Test
    public void updateState_priorityToNoneToPriority_restartsListening() {
        ZenMode zenModeWithNone = new TestModeBuilder()
                .setZenPolicy(new ZenPolicy.Builder().allowPriorityChannels(false).build())
                .build();
        ZenMode zenModeWithPriority = new TestModeBuilder()
                .setZenPolicy(new ZenPolicy.Builder().allowPriorityChannels(true).build())
                .build();

        mController.updateState(mPreference, zenModeWithPriority);
        verify(mApplicationsState).newSession(any(), any());
        verify(mSession).rebuild(any(), any(), anyBoolean());

        mController.updateState(mPreference, zenModeWithNone);
        verifyNoMoreInteractions(mApplicationsState);
        verify(mSession).deactivateSession();

        mController.updateState(mPreference, zenModeWithPriority);
        verifyNoMoreInteractions(mApplicationsState);
        verify(mSession).activateSession();
    }

    @Test
    public void testNoCrashIfAppsReadyBeforeRuleAvailable() {
        mController.mAppSessionCallbacks.onLoadEntriesCompleted();