Reduce jank on DND apps page

1) Remove call to redisplay the api when registering our app listener -
this meant all preferences were always removed/readded on page load
because the app list isn't ready at that time
2) Stop rebuilding the UI for events we don't care about
3) Keep existing preferences when possible and just do the diff of prefs
that need to be added/removed

Fixes: 234298144
Test: ZenModeAddBypassingAppsPreferenceControllerTest
Test: ZenModeAllBypassingAppsPreferenceControllerTest
Test: manually view page; add & remove apps that have dnd breakthrough
Change-Id: I57b36d36135dd25d1d2fd73073cf6b7a033659a6
This commit is contained in:
Julia Reynolds
2023-02-16 15:30:24 -05:00
parent 1692714346
commit 9f7af5946e
3 changed files with 72 additions and 89 deletions

View File

@@ -53,6 +53,7 @@ import java.util.List;
public class ZenModeAddBypassingAppsPreferenceController extends AbstractPreferenceController public class ZenModeAddBypassingAppsPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin { implements PreferenceControllerMixin {
public static final String KEY_NO_APPS = "add_none";
private static final String KEY = "zen_mode_non_bypassing_apps_list"; private static final String KEY = "zen_mode_non_bypassing_apps_list";
private static final String KEY_ADD = "zen_mode_bypassing_apps_add"; private static final String KEY_ADD = "zen_mode_bypassing_apps_add";
private final NotificationBackend mNotificationBackend; private final NotificationBackend mNotificationBackend;
@@ -118,9 +119,7 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere
} }
ApplicationsState.AppFilter filter = ApplicationsState.FILTER_ALL_ENABLED; ApplicationsState.AppFilter filter = ApplicationsState.FILTER_ALL_ENABLED;
List<ApplicationsState.AppEntry> apps = mAppSession.rebuild(filter, mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR);
ApplicationsState.ALPHA_COMPARATOR);
updateAppList(apps);
} }
// Set the icon for the given preference to the entry icon from cache if available, or look // Set the icon for the given preference to the entry icon from cache if available, or look
@@ -153,57 +152,63 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere
mPreferenceScreen.addPreference(mPreferenceCategory); mPreferenceScreen.addPreference(mPreferenceCategory);
} }
List<Preference> appsWithNoBypassingDndNotificationChannels = new ArrayList<>(); boolean doAnyAppsPassCriteria = false;
for (ApplicationsState.AppEntry entry : apps) { for (ApplicationsState.AppEntry app : apps) {
String pkg = entry.info.packageName; String pkg = app.info.packageName;
final int appChannels = mNotificationBackend.getChannelCount(pkg, entry.info.uid); final String key = getKey(pkg, app.info.uid);
final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid);
final int appChannelsBypassingDnd = mNotificationBackend final int appChannelsBypassingDnd = mNotificationBackend
.getNotificationChannelsBypassingDnd(pkg, entry.info.uid).getList().size(); .getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size();
if (appChannelsBypassingDnd == 0 && appChannels > 0) { if (appChannelsBypassingDnd == 0 && appChannels > 0) {
final String key = ZenModeAllBypassingAppsPreferenceController.getKey(pkg); doAnyAppsPassCriteria = true;
Preference pref = mPreferenceCategory.findPreference(""); }
if (pref == null) {
Preference pref = mPreferenceCategory.findPreference(key);
if (pref == null) {
if (appChannelsBypassingDnd == 0 && appChannels > 0) {
// does not exist but should
pref = new AppPreference(mPrefContext); pref = new AppPreference(mPrefContext);
pref.setKey(key); pref.setKey(key);
pref.setOnPreferenceClickListener(preference -> { pref.setOnPreferenceClickListener(preference -> {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString(AppInfoBase.ARG_PACKAGE_NAME, entry.info.packageName); args.putString(AppInfoBase.ARG_PACKAGE_NAME, app.info.packageName);
args.putInt(AppInfoBase.ARG_PACKAGE_UID, entry.info.uid); args.putInt(AppInfoBase.ARG_PACKAGE_UID, app.info.uid);
new SubSettingLauncher(mContext) new SubSettingLauncher(mContext)
.setDestination(AppChannelsBypassingDndSettings.class.getName()) .setDestination(AppChannelsBypassingDndSettings.class.getName())
.setArguments(args) .setArguments(args)
.setResultListener(mHostFragment, 0) .setResultListener(mHostFragment, 0)
.setUserHandle(new UserHandle(UserHandle.getUserId(entry.info.uid))) .setUserHandle(new UserHandle(UserHandle.getUserId(app.info.uid)))
.setSourceMetricsCategory( .setSourceMetricsCategory(
SettingsEnums.NOTIFICATION_ZEN_MODE_OVERRIDING_APP) SettingsEnums.NOTIFICATION_ZEN_MODE_OVERRIDING_APP)
.launch(); .launch();
return true; return true;
}); });
pref.setTitle(BidiFormatter.getInstance().unicodeWrap(app.label));
updateIcon(pref, app);
mPreferenceCategory.addPreference(pref);
} }
pref.setTitle(BidiFormatter.getInstance().unicodeWrap(entry.label)); } else if (appChannelsBypassingDnd != 0 || appChannels == 0) {
updateIcon(pref, entry); // exists but shouldn't anymore
appsWithNoBypassingDndNotificationChannels.add(pref); mPreferenceCategory.removePreference(pref);
} }
} }
if (appsWithNoBypassingDndNotificationChannels.size() == 0) { Preference pref = mPreferenceCategory.findPreference(KEY_NO_APPS);
Preference pref = mPreferenceCategory.findPreference( if (!doAnyAppsPassCriteria) {
ZenModeAllBypassingAppsPreferenceController.KEY_NO_APPS);
if (pref == null) { if (pref == null) {
pref = new Preference(mPrefContext); pref = new Preference(mPrefContext);
pref.setKey(ZenModeAllBypassingAppsPreferenceController.KEY_NO_APPS); pref.setKey(KEY_NO_APPS);
pref.setTitle(R.string.zen_mode_bypassing_apps_subtext_none); pref.setTitle(R.string.zen_mode_bypassing_apps_none);
} }
mPreferenceCategory.addPreference(pref); mPreferenceCategory.addPreference(pref);
} else if (pref != null) {
mPreferenceCategory.removePreference(pref);
} }
}
if (ZenModeAllBypassingAppsPreferenceController.hasAppListChanged( static String getKey(String pkg, int uid) {
appsWithNoBypassingDndNotificationChannels, mPreferenceCategory)) { return "add|" + pkg + "|" + uid;
mPreferenceCategory.removeAll();
for (Preference prefToAdd : appsWithNoBypassingDndNotificationChannels) {
mPreferenceCategory.addPreference(prefToAdd);
}
}
} }
private final ApplicationsState.Callbacks mAppSessionCallbacks = private final ApplicationsState.Callbacks mAppSessionCallbacks =
@@ -211,12 +216,12 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere
@Override @Override
public void onRunningStateChanged(boolean running) { public void onRunningStateChanged(boolean running) {
updateAppList();
} }
@Override @Override
public void onPackageListChanged() { public void onPackageListChanged() {
updateAppList();
} }
@Override @Override
@@ -231,7 +236,7 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere
@Override @Override
public void onPackageSizeChanged(String packageName) { public void onPackageSizeChanged(String packageName) {
updateAppList();
} }
@Override @Override
@@ -239,7 +244,7 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere
@Override @Override
public void onLauncherInfoChanged() { public void onLauncherInfoChanged() {
updateAppList();
} }
@Override @Override

View File

@@ -44,7 +44,6 @@ import com.android.settingslib.widget.AppPreference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
@@ -52,7 +51,7 @@ import java.util.Objects;
*/ */
public class ZenModeAllBypassingAppsPreferenceController extends AbstractPreferenceController public class ZenModeAllBypassingAppsPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin { implements PreferenceControllerMixin {
public static final String KEY_NO_APPS = getKey("none"); public static final String KEY_NO_APPS = "all_none";
private static final String KEY = "zen_mode_bypassing_apps_list"; private static final String KEY = "zen_mode_bypassing_apps_list";
private final NotificationBackend mNotificationBackend; private final NotificationBackend mNotificationBackend;
@@ -109,9 +108,7 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
} }
ApplicationsState.AppFilter filter = ApplicationsState.FILTER_ALL_ENABLED; ApplicationsState.AppFilter filter = ApplicationsState.FILTER_ALL_ENABLED;
List<ApplicationsState.AppEntry> apps = mAppSession.rebuild(filter, mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR);
ApplicationsState.ALPHA_COMPARATOR);
updateAppList(apps);
} }
// Set the icon for the given preference to the entry icon from cache if available, or look // Set the icon for the given preference to the entry icon from cache if available, or look
@@ -138,17 +135,21 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
return; return;
} }
List<Preference> appsBypassingDnd = new ArrayList<>(); boolean doAnyAppsPassCriteria = false;
for (ApplicationsState.AppEntry app : apps) { for (ApplicationsState.AppEntry app : apps) {
String pkg = app.info.packageName; String pkg = app.info.packageName;
final String key = getKey(pkg, app.info.uid);
final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid); final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid);
final int appChannelsBypassingDnd = mNotificationBackend final int appChannelsBypassingDnd = mNotificationBackend
.getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size(); .getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size();
if (appChannelsBypassingDnd > 0) { if (appChannelsBypassingDnd > 0) {
final String key = getKey(pkg); doAnyAppsPassCriteria = true;
// re-use previously created preference when possible }
Preference pref = mPreferenceCategory.findPreference(key);
if (pref == null) { Preference pref = mPreferenceCategory.findPreference(key);
if (pref == null) {
if (appChannelsBypassingDnd > 0) {
// does not exist but should
pref = new AppPreference(mPrefContext); pref = new AppPreference(mPrefContext);
pref.setKey(key); pref.setKey(key);
pref.setOnPreferenceClickListener(preference -> { pref.setOnPreferenceClickListener(preference -> {
@@ -165,59 +166,40 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
.launch(); .launch();
return true; return true;
}); });
pref.setTitle(BidiFormatter.getInstance().unicodeWrap(app.label));
updateIcon(pref, app);
if (appChannels > appChannelsBypassingDnd) {
pref.setSummary(R.string.zen_mode_bypassing_apps_summary_some);
} else {
pref.setSummary(R.string.zen_mode_bypassing_apps_summary_all);
}
mPreferenceCategory.addPreference(pref);
} }
pref.setTitle(BidiFormatter.getInstance().unicodeWrap(app.label)); }
updateIcon(pref, app); else if (appChannelsBypassingDnd == 0) {
if (appChannels > appChannelsBypassingDnd) { // exists but shouldn't anymore
pref.setSummary(R.string.zen_mode_bypassing_apps_summary_some); mPreferenceCategory.removePreference(pref);
} else {
pref.setSummary(R.string.zen_mode_bypassing_apps_summary_all);
}
appsBypassingDnd.add(pref);
} }
} }
if (appsBypassingDnd.size() == 0) { Preference pref = mPreferenceCategory.findPreference(KEY_NO_APPS);
Preference pref = mPreferenceCategory.findPreference(KEY_NO_APPS); if (!doAnyAppsPassCriteria) {
if (pref == null) { if (pref == null) {
pref = new Preference(mPrefContext); pref = new Preference(mPrefContext);
pref.setKey(KEY_NO_APPS); pref.setKey(KEY_NO_APPS);
pref.setTitle(R.string.zen_mode_bypassing_apps_none); pref.setTitle(R.string.zen_mode_bypassing_apps_none);
} }
appsBypassingDnd.add(pref); mPreferenceCategory.addPreference(pref);
} else if (pref != null) {
mPreferenceCategory.removePreference(pref);
} }
if (hasAppListChanged(appsBypassingDnd, mPreferenceCategory)) {
mPreferenceCategory.removeAll();
for (Preference prefToAdd : appsBypassingDnd) {
mPreferenceCategory.addPreference(prefToAdd);
}
}
}
static boolean hasAppListChanged(List<Preference> newAppPreferences,
PreferenceCategory preferenceCategory) {
if (newAppPreferences.size() != preferenceCategory.getPreferenceCount()) {
return true;
}
for (int i = 0; i < newAppPreferences.size(); i++) {
Preference newAppPref = newAppPreferences.get(i);
Preference pref = preferenceCategory.getPreference(i);
if (!Objects.equals(newAppPref.getKey(), pref.getKey())) {
return true;
}
}
return false;
} }
/** /**
* Create a unique key to idenfity an AppPreference * Create a unique key to idenfity an AppPreference
*/ */
static String getKey(String pkg) { static String getKey(String pkg, int uid) {
return pkg; return "all|" + pkg + "|" + uid;
} }
private final ApplicationsState.Callbacks mAppSessionCallbacks = private final ApplicationsState.Callbacks mAppSessionCallbacks =
@@ -225,12 +207,10 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
@Override @Override
public void onRunningStateChanged(boolean running) { public void onRunningStateChanged(boolean running) {
updateAppList();
} }
@Override @Override
public void onPackageListChanged() { public void onPackageListChanged() {
updateAppList();
} }
@Override @Override
@@ -240,12 +220,10 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
@Override @Override
public void onPackageIconChanged() { public void onPackageIconChanged() {
updateAppList();
} }
@Override @Override
public void onPackageSizeChanged(String packageName) { public void onPackageSizeChanged(String packageName) {
updateAppList();
} }
@Override @Override
@@ -253,7 +231,6 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
@Override @Override
public void onLauncherInfoChanged() { public void onLauncherInfoChanged() {
updateAppList();
} }
@Override @Override

View File

@@ -138,8 +138,9 @@ public class ZenModeAddBypassingAppsPreferenceControllerTest {
Preference pref = prefCaptor.getValue(); Preference pref = prefCaptor.getValue();
assertThat(pref.getKey()).isEqualTo( assertThat(pref.getKey()).isEqualTo(
ZenModeAllBypassingAppsPreferenceController.getKey( ZenModeAddBypassingAppsPreferenceController.getKey(
appWithChannelsNoneBypassing.info.packageName)); appWithChannelsNoneBypassing.info.packageName,
appWithChannelsNoneBypassing.info.uid));
} }
@Test @Test
@@ -159,6 +160,6 @@ public class ZenModeAddBypassingAppsPreferenceControllerTest {
Preference pref = prefCaptor.getValue(); Preference pref = prefCaptor.getValue();
assertThat(pref.getKey()).isEqualTo( assertThat(pref.getKey()).isEqualTo(
ZenModeAllBypassingAppsPreferenceController.KEY_NO_APPS); ZenModeAddBypassingAppsPreferenceController.KEY_NO_APPS);
} }
} }