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

This commit is contained in:
Matías Hernández
2024-08-15 18:53:55 +00:00
committed by Android (Google) Code Review
3 changed files with 112 additions and 10 deletions

View File

@@ -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();
}
}
);

View File

@@ -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) {
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);
}

View File

@@ -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();