Merge "Move preference loading to a background thread" into sc-dev

This commit is contained in:
Julia Reynolds
2021-04-24 00:34:26 +00:00
committed by Android (Google) Code Review
3 changed files with 67 additions and 56 deletions

View File

@@ -42,6 +42,15 @@
android:key="recent_notifications_category" android:key="recent_notifications_category"
android:title="@string/recent_notifications"> android:title="@string/recent_notifications">
<!-- Placeholder for a list of recent apps --> <!-- Placeholder for a list of recent apps -->
<com.android.settings.widget.PrimarySwitchPreference
android:key="app1"
android:order="5"/>
<com.android.settings.widget.PrimarySwitchPreference
android:key="app2"
android:order="6"/>
<com.android.settings.widget.PrimarySwitchPreference
android:key="app3"
android:order="7"/>
<!-- See all apps button --> <!-- See all apps button -->
<Preference <Preference

View File

@@ -49,6 +49,7 @@ import com.android.settings.widget.PrimarySwitchPreference;
import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.utils.StringUtil; import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.TwoTargetPreference; import com.android.settingslib.widget.TwoTargetPreference;
import java.util.ArrayList; import java.util.ArrayList;
@@ -70,9 +71,9 @@ public class RecentNotifyingAppsPreferenceController extends AbstractPreferenceC
@VisibleForTesting @VisibleForTesting
static final String KEY_SEE_ALL = "all_notifications"; static final String KEY_SEE_ALL = "all_notifications";
static final String KEY_PLACEHOLDER = "app";
private static final int SHOW_RECENT_APP_COUNT = 3; private static final int SHOW_RECENT_APP_COUNT = 3;
private static final int DAYS = 3; private static final int DAYS = 3;
private static final Set<String> SKIP_SYSTEM_PACKAGES = new ArraySet<>();
private final Fragment mHost; private final Fragment mHost;
private final PackageManager mPm; private final PackageManager mPm;
@@ -148,13 +149,17 @@ public class RecentNotifyingAppsPreferenceController extends AbstractPreferenceC
@VisibleForTesting @VisibleForTesting
void refreshUi(Context prefContext) { void refreshUi(Context prefContext) {
ThreadUtils.postOnBackgroundThread(() -> {
reloadData(); reloadData();
final List<NotifyingApp> recentApps = getDisplayableRecentAppList(); final List<NotifyingApp> recentApps = getDisplayableRecentAppList();
ThreadUtils.postOnMainThread(() -> {
if (recentApps != null && !recentApps.isEmpty()) { if (recentApps != null && !recentApps.isEmpty()) {
displayRecentApps(prefContext, recentApps); displayRecentApps(prefContext, recentApps);
} else { } else {
displayOnlyAllAppsLink(); displayOnlyAllAppsLink();
} }
});
});
} }
@VisibleForTesting @VisibleForTesting
@@ -198,8 +203,7 @@ public class RecentNotifyingAppsPreferenceController extends AbstractPreferenceC
} }
} }
@VisibleForTesting private static String getKey(int userId, String pkg) {
static String getKey(int userId, String pkg) {
return userId + "|" + pkg; return userId + "|" + pkg;
} }
@@ -221,19 +225,9 @@ public class RecentNotifyingAppsPreferenceController extends AbstractPreferenceC
mSeeAllPref.setSummary(null); mSeeAllPref.setSummary(null);
mSeeAllPref.setIcon(R.drawable.ic_chevron_right_24dp); mSeeAllPref.setIcon(R.drawable.ic_chevron_right_24dp);
// Rebind prefs/avoid adding new prefs if possible. Adding/removing prefs causes jank. int keyIndex = 1;
// Build a cached preference pool
final Map<String, PrimarySwitchPreference> appPreferences = new ArrayMap<>();
int prefCount = mCategory.getPreferenceCount();
for (int i = 0; i < prefCount; i++) {
final Preference pref = mCategory.getPreference(i);
final String key = pref.getKey();
if (!TextUtils.equals(key, KEY_SEE_ALL)) {
appPreferences.put(key, (PrimarySwitchPreference) pref);
}
}
final int recentAppsCount = recentApps.size(); final int recentAppsCount = recentApps.size();
for (int i = 0; i < recentAppsCount; i++) { for (int i = 0; i < recentAppsCount; i++, keyIndex++) {
final NotifyingApp app = recentApps.get(i); final NotifyingApp app = recentApps.get(i);
// Bind recent apps to existing prefs if possible, or create a new pref. // Bind recent apps to existing prefs if possible, or create a new pref.
final String pkgName = app.getPackage(); final String pkgName = app.getPackage();
@@ -243,20 +237,12 @@ public class RecentNotifyingAppsPreferenceController extends AbstractPreferenceC
continue; continue;
} }
boolean rebindPref = true; PrimarySwitchPreference pref = mCategory.findPreference(KEY_PLACEHOLDER + keyIndex);
PrimarySwitchPreference pref = appPreferences.remove(getKey(app.getUserId(),
pkgName));
if (pref == null) {
pref = new PrimarySwitchPreference(prefContext);
rebindPref = false;
}
pref.setKey(getKey(app.getUserId(), pkgName));
pref.setTitle(appEntry.label); pref.setTitle(appEntry.label);
pref.setIcon(mIconDrawableFactory.getBadgedIcon(appEntry.info)); pref.setIcon(mIconDrawableFactory.getBadgedIcon(appEntry.info));
pref.setIconSize(TwoTargetPreference.ICON_SIZE_SMALL); pref.setIconSize(TwoTargetPreference.ICON_SIZE_SMALL);
pref.setSummary(StringUtil.formatRelativeTime(mContext, pref.setSummary(StringUtil.formatRelativeTime(mContext,
System.currentTimeMillis() - app.getLastNotified(), true)); System.currentTimeMillis() - app.getLastNotified(), true));
pref.setOrder(i);
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkgName); args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkgName);
args.putInt(AppInfoBase.ARG_PACKAGE_UID, appEntry.info.uid); args.putInt(AppInfoBase.ARG_PACKAGE_UID, appEntry.info.uid);
@@ -280,13 +266,10 @@ public class RecentNotifyingAppsPreferenceController extends AbstractPreferenceC
pref.setChecked( pref.setChecked(
!mNotificationBackend.getNotificationsBanned(pkgName, appEntry.info.uid)); !mNotificationBackend.getNotificationsBanned(pkgName, appEntry.info.uid));
if (!rebindPref) {
mCategory.addPreference(pref);
} }
} // If there are less than SHOW_RECENT_APP_COUNT recent apps, remove placeholders
// Remove unused prefs from pref cache pool for (int i = keyIndex; i <= SHOW_RECENT_APP_COUNT; i++) {
for (Preference unusedPrefs : appPreferences.values()) { mCategory.removePreferenceRecursively(KEY_PLACEHOLDER + i);
mCategory.removePreference(unusedPrefs);
} }
} }

View File

@@ -48,6 +48,7 @@ import android.service.notification.NotifyingApp;
import android.text.TextUtils; import android.text.TextUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.widget.PrimarySwitchPreference;
import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.instantapps.InstantAppDataProvider; import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
@@ -80,6 +81,9 @@ public class RecentNotifyingAppsPreferenceControllerTest {
private PreferenceScreen mScreen; private PreferenceScreen mScreen;
@Mock @Mock
private PreferenceCategory mCategory; private PreferenceCategory mCategory;
private PrimarySwitchPreference mApp1;
private PrimarySwitchPreference mApp2;
private PrimarySwitchPreference mApp3;
@Mock @Mock
private Preference mSeeAllPref; private Preference mSeeAllPref;
@Mock @Mock
@@ -115,6 +119,15 @@ public class RecentNotifyingAppsPreferenceControllerTest {
mController = new RecentNotifyingAppsPreferenceController( mController = new RecentNotifyingAppsPreferenceController(
mContext, mBackend, mIUsageStatsManager, mUserManager, mAppState, mHost); mContext, mBackend, mIUsageStatsManager, mUserManager, mAppState, mHost);
when(mScreen.findPreference(anyString())).thenReturn(mCategory); when(mScreen.findPreference(anyString())).thenReturn(mCategory);
mApp1 = new PrimarySwitchPreference(mContext);
mApp1.setKey("app1");
mApp2 = new PrimarySwitchPreference(mContext);
mApp2.setKey("app2");
mApp3 = new PrimarySwitchPreference(mContext);
mApp3.setKey("app3");
when(mCategory.findPreference("app1")).thenReturn(mApp1);
when(mCategory.findPreference("app2")).thenReturn(mApp2);
when(mCategory.findPreference("app3")).thenReturn(mApp3);
when(mScreen.findPreference(RecentNotifyingAppsPreferenceController.KEY_SEE_ALL)) when(mScreen.findPreference(RecentNotifyingAppsPreferenceController.KEY_SEE_ALL))
.thenReturn(mSeeAllPref); .thenReturn(mSeeAllPref);
@@ -169,12 +182,18 @@ public class RecentNotifyingAppsPreferenceControllerTest {
app2.mPackage = "pkg.class2"; app2.mPackage = "pkg.class2";
app2.mTimeStamp = System.currentTimeMillis() - 1000; app2.mTimeStamp = System.currentTimeMillis() - 1000;
events.add(app2); events.add(app2);
ApplicationsState.AppEntry app1Entry = mock(ApplicationsState.AppEntry.class);
ApplicationsState.AppEntry app2Entry = mock(ApplicationsState.AppEntry.class);
app1Entry.info = mApplicationInfo;
app1Entry.label = "app 1";
app2Entry.info = mApplicationInfo;
app2Entry.label = "app 2";
// app1, app2 are valid apps. app3 is invalid. // app1, app2 are valid apps. app3 is invalid.
when(mAppState.getEntry(app.getPackageName(), UserHandle.myUserId())) when(mAppState.getEntry(app.getPackageName(), UserHandle.myUserId()))
.thenReturn(mAppEntry); .thenReturn(app1Entry);
when(mAppState.getEntry(app1.getPackageName(), UserHandle.myUserId())) when(mAppState.getEntry(app1.getPackageName(), UserHandle.myUserId()))
.thenReturn(mAppEntry); .thenReturn(app2Entry);
when(mAppState.getEntry(app2.getPackageName(), UserHandle.myUserId())) when(mAppState.getEntry(app2.getPackageName(), UserHandle.myUserId()))
.thenReturn(null); .thenReturn(null);
when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn( when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(
@@ -192,7 +211,10 @@ public class RecentNotifyingAppsPreferenceControllerTest {
verify(mCategory).setTitle(R.string.recent_notifications); verify(mCategory).setTitle(R.string.recent_notifications);
// Only add app1 & app2. app3 skipped because it's invalid app. // Only add app1 & app2. app3 skipped because it's invalid app.
verify(mCategory, times(2)).addPreference(any(Preference.class)); assertThat(mApp1.getTitle()).isEqualTo(app1Entry.label);
assertThat(mApp2.getTitle()).isEqualTo(app2Entry.label);
verify(mCategory).removePreferenceRecursively(mApp3.getKey());
verify(mSeeAllPref).setSummary(null); verify(mSeeAllPref).setSummary(null);
verify(mSeeAllPref).setIcon(R.drawable.ic_chevron_right_24dp); verify(mSeeAllPref).setIcon(R.drawable.ic_chevron_right_24dp);
@@ -209,22 +231,24 @@ public class RecentNotifyingAppsPreferenceControllerTest {
Event app1 = new Event(); Event app1 = new Event();
app1.mEventType = Event.NOTIFICATION_INTERRUPTION; app1.mEventType = Event.NOTIFICATION_INTERRUPTION;
app1.mPackage = "com.foo.barinstant"; app1.mPackage = "com.foo.barinstant";
app1.mTimeStamp = System.currentTimeMillis() + 200; app1.mTimeStamp = System.currentTimeMillis() - 200;
events.add(app1); events.add(app1);
UsageEvents usageEvents = getUsageEvents( UsageEvents usageEvents = getUsageEvents(
new String[] {"com.foo.bar", "com.foo.barinstant"}, events); new String[] {"com.foo.bar", "com.foo.barinstant"}, events);
when(mIUsageStatsManager.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString())) when(mIUsageStatsManager.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
.thenReturn(usageEvents); .thenReturn(usageEvents);
ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
ApplicationsState.AppEntry app1Entry = mock(ApplicationsState.AppEntry.class); ApplicationsState.AppEntry app1Entry = mock(ApplicationsState.AppEntry.class);
ApplicationsState.AppEntry app2Entry = mock(ApplicationsState.AppEntry.class); appEntry.info = mApplicationInfo;
appEntry.label = "app 1";
app1Entry.info = mApplicationInfo; app1Entry.info = mApplicationInfo;
app2Entry.info = mApplicationInfo; app1Entry.label = "app 2";
when(mAppState.getEntry( when(mAppState.getEntry(
app.getPackageName(), UserHandle.myUserId())).thenReturn(app1Entry); app.getPackageName(), UserHandle.myUserId())).thenReturn(appEntry);
when(mAppState.getEntry( when(mAppState.getEntry(
app1.getPackageName(), UserHandle.myUserId())).thenReturn(app2Entry); app1.getPackageName(), UserHandle.myUserId())).thenReturn(app1Entry);
// Only the regular app app1 should have its intent resolve. // Only the regular app app1 should have its intent resolve.
when(mPackageManager.resolveActivity(argThat(intentMatcher(app.getPackageName())), when(mPackageManager.resolveActivity(argThat(intentMatcher(app.getPackageName())),
@@ -233,7 +257,7 @@ public class RecentNotifyingAppsPreferenceControllerTest {
// Make sure app2 is considered an instant app. // Make sure app2 is considered an instant app.
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
(InstantAppDataProvider) (ApplicationInfo info) -> { (InstantAppDataProvider) (ApplicationInfo info) -> {
if (info == app2Entry.info) { if (info == app1Entry.info) {
return true; return true;
} else { } else {
return false; return false;
@@ -242,15 +266,10 @@ public class RecentNotifyingAppsPreferenceControllerTest {
mController.displayPreference(mScreen); mController.displayPreference(mScreen);
ArgumentCaptor<Preference> prefCaptor = ArgumentCaptor.forClass(Preference.class); assertThat(mApp1.getTitle()).isEqualTo(appEntry.label);
verify(mCategory, times(2)).addPreference(prefCaptor.capture()); assertThat(mApp2.getTitle()).isEqualTo(app1Entry.label);
List<Preference> prefs = prefCaptor.getAllValues();
assertThat(prefs.get(1).getKey()).isEqualTo( verify(mCategory).removePreferenceRecursively(mApp3.getKey());
RecentNotifyingAppsPreferenceController.getKey(UserHandle.myUserId(),
app.getPackageName()));
assertThat(prefs.get(0).getKey()).isEqualTo(
RecentNotifyingAppsPreferenceController.getKey(UserHandle.myUserId(),
app1.getPackageName()));
} }
@Test @Test
@@ -274,7 +293,7 @@ public class RecentNotifyingAppsPreferenceControllerTest {
mController.displayPreference(mScreen); mController.displayPreference(mScreen);
verify(mCategory).addPreference(argThat(summaryMatches("Just now"))); assertThat(mApp1.getSummary()).isEqualTo("Just now");
} }
@Test @Test