Loading res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -6965,6 +6965,9 @@ <!-- Configure Notifications Settings title. [CHAR LIMIT=30] --> <string name="configure_notification_settings">Notifications</string> <!-- notification header - apps that have recently sent notifications --> <string name="recent_notifications">Recently sent</string> <!-- Configure Notifications: Advanced section header [CHAR LIMIT=30] --> <string name="advanced_section_header">Advanced</string> res/xml/configure_notification_settings.xml +22 −2 Original line number Diff line number Diff line Loading @@ -19,8 +19,28 @@ android:key="configure_notification_settings"> <PreferenceCategory android:key="dashboard_tile_placeholder" android:order="1"/> android:key="recent_notifications_category" android:title="@string/recent_notifications" android:order="-200"> <!-- Placeholder for a list of recent apps --> <!-- See all apps button --> <Preference android:title="@string/notifications_title" android:key="all_notifications" android:order="20"> <intent android:action="android.intent.action.MAIN" android:targetPackage="com.android.settings" android:targetClass="com.android.settings.Settings$NotificationAppListActivity"> </intent> </Preference> </PreferenceCategory> <!-- Empty category to draw divider --> <PreferenceCategory android:key="all_notifications_divider" android:order="-190"/> <!-- When device is locked --> <com.android.settings.notification.RestrictedDropDownPreference Loading src/com/android/settings/notification/ConfigureNotificationSettings.java +14 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.settings.notification; import android.app.Activity; import android.app.Application; import android.app.Fragment; import android.content.Context; import android.content.Intent; import android.os.Bundle; Loading Loading @@ -77,11 +79,18 @@ public class ConfigureNotificationSettings extends DashboardFragment { @Override protected List<AbstractPreferenceController> getPreferenceControllers(Context context) { return buildPreferenceControllers(context, getLifecycle()); final Activity activity = getActivity(); final Application app; if (activity != null) { app = activity.getApplication(); } else { app = null; } return buildPreferenceControllers(context, getLifecycle(), app, this); } private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, Lifecycle lifecycle) { Lifecycle lifecycle, Application app, Fragment host) { final List<AbstractPreferenceController> controllers = new ArrayList<>(); final BadgingNotificationPreferenceController badgeController = new BadgingNotificationPreferenceController(context); Loading @@ -96,6 +105,8 @@ public class ConfigureNotificationSettings extends DashboardFragment { lifecycle.addObserver(pulseController); lifecycle.addObserver(lockScreenNotificationController); } controllers.add(new RecentNotifyingAppsPreferenceController( context, new NotificationBackend(), app, host)); controllers.add(new SwipeToNotificationPreferenceController(context, lifecycle, KEY_SWIPE_DOWN)); controllers.add(badgeController); Loading Loading @@ -167,7 +178,7 @@ public class ConfigureNotificationSettings extends DashboardFragment { @Override public List<AbstractPreferenceController> getPreferenceControllers( Context context) { return buildPreferenceControllers(context, null); return buildPreferenceControllers(context, null, null, null); } @Override Loading src/com/android/settings/notification/NotificationBackend.java +13 −1 Original line number Diff line number Diff line Loading @@ -27,12 +27,16 @@ import android.content.pm.ParceledListSlice; import android.graphics.drawable.Drawable; import android.os.ServiceManager; import android.os.UserHandle; import android.service.notification.NotifyingApp; import android.util.IconDrawableFactory; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.Utils; import java.util.ArrayList; import java.util.List; public class NotificationBackend { private static final String TAG = "NotificationBackend"; Loading Loading @@ -185,7 +189,6 @@ public class NotificationBackend { } } public int getDeletedChannelCount(String pkg, int uid) { try { return sINM.getDeletedChannelCount(pkg, uid); Loading @@ -204,6 +207,15 @@ public class NotificationBackend { } } public List<NotifyingApp> getRecentApps() { try { return sINM.getRecentNotifyingAppsForUser(UserHandle.myUserId()).getList(); } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); return new ArrayList<>(); } } static class Row { public String section; } Loading src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java 0 → 100644 +293 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.notification; import android.app.Application; import android.app.Fragment; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.UserHandle; import android.service.notification.NotifyingApp; import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceCategory; import android.support.v7.preference.PreferenceScreen; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IconDrawableFactory; import android.util.Log; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.InstalledAppCounter; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.widget.AppPreference; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.wrapper.PackageManagerWrapper; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; /** * This controller displays a list of recently used apps and a "See all" button. If there is * no recently used app, "See all" will be displayed as "Notifications". */ public class RecentNotifyingAppsPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin { private static final String TAG = "RecentNotisCtrl"; private static final String KEY_PREF_CATEGORY = "recent_notifications_category"; @VisibleForTesting static final String KEY_DIVIDER = "all_notifications_divider"; @VisibleForTesting static final String KEY_SEE_ALL = "all_notifications"; private static final int SHOW_RECENT_APP_COUNT = 5; private static final Set<String> SKIP_SYSTEM_PACKAGES = new ArraySet<>(); private final Fragment mHost; private final PackageManager mPm; private final NotificationBackend mNotificationBackend; private final int mUserId; private final IconDrawableFactory mIconDrawableFactory; private List<NotifyingApp> mApps; private final ApplicationsState mApplicationsState; private PreferenceCategory mCategory; private Preference mSeeAllPref; private Preference mDivider; private boolean mHasRecentApps; static { SKIP_SYSTEM_PACKAGES.addAll(Arrays.asList( "android", "com.android.phone", "com.android.settings", "com.android.systemui", "com.android.providers.calendar", "com.android.providers.media" )); } public RecentNotifyingAppsPreferenceController(Context context, NotificationBackend backend, Application app, Fragment host) { this(context, backend, app == null ? null : ApplicationsState.getInstance(app), host); } @VisibleForTesting(otherwise = VisibleForTesting.NONE) RecentNotifyingAppsPreferenceController(Context context, NotificationBackend backend, ApplicationsState appState, Fragment host) { super(context); mIconDrawableFactory = IconDrawableFactory.newInstance(context); mUserId = UserHandle.myUserId(); mPm = context.getPackageManager(); mHost = host; mApplicationsState = appState; mNotificationBackend = backend; } @Override public boolean isAvailable() { return true; } @Override public String getPreferenceKey() { return KEY_PREF_CATEGORY; } @Override public void updateNonIndexableKeys(List<String> keys) { PreferenceControllerMixin.super.updateNonIndexableKeys(keys); // Don't index category name into search. It's not actionable. keys.add(KEY_PREF_CATEGORY); keys.add(KEY_DIVIDER); } @Override public void displayPreference(PreferenceScreen screen) { mCategory = (PreferenceCategory) screen.findPreference(getPreferenceKey()); mSeeAllPref = screen.findPreference(KEY_SEE_ALL); mDivider = screen.findPreference(KEY_DIVIDER); super.displayPreference(screen); refreshUi(mCategory.getContext()); } @Override public void updateState(Preference preference) { super.updateState(preference); refreshUi(mCategory.getContext()); // Show total number of installed apps as See all's summary. new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON, new PackageManagerWrapper(mContext.getPackageManager())) { @Override protected void onCountComplete(int num) { if (mHasRecentApps) { mSeeAllPref.setTitle(mContext.getString(R.string.see_all_apps_title, num)); } else { mSeeAllPref.setSummary(mContext.getString(R.string.apps_summary, num)); } } }.execute(); } @VisibleForTesting void refreshUi(Context prefContext) { reloadData(); final List<NotifyingApp> recentApps = getDisplayableRecentAppList(); if (recentApps != null && !recentApps.isEmpty()) { mHasRecentApps = true; displayRecentApps(prefContext, recentApps); } else { mHasRecentApps = false; displayOnlyAllAppsLink(); } } @VisibleForTesting void reloadData() { mApps = mNotificationBackend.getRecentApps(); } private void displayOnlyAllAppsLink() { mCategory.setTitle(null); mDivider.setVisible(false); mSeeAllPref.setTitle(R.string.notifications_title); mSeeAllPref.setIcon(null); int prefCount = mCategory.getPreferenceCount(); for (int i = prefCount - 1; i >= 0; i--) { final Preference pref = mCategory.getPreference(i); if (!TextUtils.equals(pref.getKey(), KEY_SEE_ALL)) { mCategory.removePreference(pref); } } } private void displayRecentApps(Context prefContext, List<NotifyingApp> recentApps) { mCategory.setTitle(R.string.recent_notifications); mDivider.setVisible(true); mSeeAllPref.setSummary(null); mSeeAllPref.setIcon(R.drawable.ic_chevron_right_24dp); // Rebind prefs/avoid adding new prefs if possible. Adding/removing prefs causes jank. // Build a cached preference pool final Map<String, Preference> 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, pref); } } final int recentAppsCount = recentApps.size(); for (int i = 0; i < recentAppsCount; i++) { final NotifyingApp app = recentApps.get(i); // Bind recent apps to existing prefs if possible, or create a new pref. final String pkgName = app.getPackage(); final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry(app.getPackage(), mUserId); if (appEntry == null) { continue; } boolean rebindPref = true; Preference pref = appPreferences.remove(pkgName); if (pref == null) { pref = new AppPreference(prefContext); rebindPref = false; } pref.setKey(pkgName); pref.setTitle(appEntry.label); pref.setIcon(mIconDrawableFactory.getBadgedIcon(appEntry.info)); pref.setSummary(Utils.formatRelativeTime(mContext, System.currentTimeMillis() - app.getLastNotified(), false)); pref.setOrder(i); pref.setOnPreferenceClickListener(preference -> { AppInfoBase.startAppInfoFragment(AppNotificationSettings.class, R.string.notifications_title, pkgName, appEntry.info.uid, mHost, 1001 /*RequestCode */, MetricsProto.MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS); return true; }); if (!rebindPref) { mCategory.addPreference(pref); } } // Remove unused prefs from pref cache pool for (Preference unusedPrefs : appPreferences.values()) { mCategory.removePreference(unusedPrefs); } } private List<NotifyingApp> getDisplayableRecentAppList() { Collections.sort(mApps); List<NotifyingApp> displayableApps = new ArrayList<>(SHOW_RECENT_APP_COUNT); int count = 0; for (NotifyingApp app : mApps) { final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry( app.getPackage(), mUserId); if (appEntry == null) { continue; } if (!shouldIncludePkgInRecents(app.getPackage())) { continue; } displayableApps.add(app); count++; if (count >= SHOW_RECENT_APP_COUNT) { break; } } return displayableApps; } /** * Whether or not the app should be included in recent list. */ private boolean shouldIncludePkgInRecents(String pkgName) { if (SKIP_SYSTEM_PACKAGES.contains(pkgName)) { Log.d(TAG, "System package, skipping " + pkgName); return false; } final Intent launchIntent = new Intent().addCategory(Intent.CATEGORY_LAUNCHER) .setPackage(pkgName); if (mPm.resolveActivity(launchIntent, 0) == null) { // Not visible on launcher -> likely not a user visible app, skip if non-instant. final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry(pkgName, mUserId); if (!AppUtils.isInstant(appEntry.info)) { Log.d(TAG, "Not a user visible or instant app, skipping " + pkgName); return false; } } return true; } } Loading
res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -6965,6 +6965,9 @@ <!-- Configure Notifications Settings title. [CHAR LIMIT=30] --> <string name="configure_notification_settings">Notifications</string> <!-- notification header - apps that have recently sent notifications --> <string name="recent_notifications">Recently sent</string> <!-- Configure Notifications: Advanced section header [CHAR LIMIT=30] --> <string name="advanced_section_header">Advanced</string>
res/xml/configure_notification_settings.xml +22 −2 Original line number Diff line number Diff line Loading @@ -19,8 +19,28 @@ android:key="configure_notification_settings"> <PreferenceCategory android:key="dashboard_tile_placeholder" android:order="1"/> android:key="recent_notifications_category" android:title="@string/recent_notifications" android:order="-200"> <!-- Placeholder for a list of recent apps --> <!-- See all apps button --> <Preference android:title="@string/notifications_title" android:key="all_notifications" android:order="20"> <intent android:action="android.intent.action.MAIN" android:targetPackage="com.android.settings" android:targetClass="com.android.settings.Settings$NotificationAppListActivity"> </intent> </Preference> </PreferenceCategory> <!-- Empty category to draw divider --> <PreferenceCategory android:key="all_notifications_divider" android:order="-190"/> <!-- When device is locked --> <com.android.settings.notification.RestrictedDropDownPreference Loading
src/com/android/settings/notification/ConfigureNotificationSettings.java +14 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.settings.notification; import android.app.Activity; import android.app.Application; import android.app.Fragment; import android.content.Context; import android.content.Intent; import android.os.Bundle; Loading Loading @@ -77,11 +79,18 @@ public class ConfigureNotificationSettings extends DashboardFragment { @Override protected List<AbstractPreferenceController> getPreferenceControllers(Context context) { return buildPreferenceControllers(context, getLifecycle()); final Activity activity = getActivity(); final Application app; if (activity != null) { app = activity.getApplication(); } else { app = null; } return buildPreferenceControllers(context, getLifecycle(), app, this); } private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, Lifecycle lifecycle) { Lifecycle lifecycle, Application app, Fragment host) { final List<AbstractPreferenceController> controllers = new ArrayList<>(); final BadgingNotificationPreferenceController badgeController = new BadgingNotificationPreferenceController(context); Loading @@ -96,6 +105,8 @@ public class ConfigureNotificationSettings extends DashboardFragment { lifecycle.addObserver(pulseController); lifecycle.addObserver(lockScreenNotificationController); } controllers.add(new RecentNotifyingAppsPreferenceController( context, new NotificationBackend(), app, host)); controllers.add(new SwipeToNotificationPreferenceController(context, lifecycle, KEY_SWIPE_DOWN)); controllers.add(badgeController); Loading Loading @@ -167,7 +178,7 @@ public class ConfigureNotificationSettings extends DashboardFragment { @Override public List<AbstractPreferenceController> getPreferenceControllers( Context context) { return buildPreferenceControllers(context, null); return buildPreferenceControllers(context, null, null, null); } @Override Loading
src/com/android/settings/notification/NotificationBackend.java +13 −1 Original line number Diff line number Diff line Loading @@ -27,12 +27,16 @@ import android.content.pm.ParceledListSlice; import android.graphics.drawable.Drawable; import android.os.ServiceManager; import android.os.UserHandle; import android.service.notification.NotifyingApp; import android.util.IconDrawableFactory; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.Utils; import java.util.ArrayList; import java.util.List; public class NotificationBackend { private static final String TAG = "NotificationBackend"; Loading Loading @@ -185,7 +189,6 @@ public class NotificationBackend { } } public int getDeletedChannelCount(String pkg, int uid) { try { return sINM.getDeletedChannelCount(pkg, uid); Loading @@ -204,6 +207,15 @@ public class NotificationBackend { } } public List<NotifyingApp> getRecentApps() { try { return sINM.getRecentNotifyingAppsForUser(UserHandle.myUserId()).getList(); } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); return new ArrayList<>(); } } static class Row { public String section; } Loading
src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java 0 → 100644 +293 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.notification; import android.app.Application; import android.app.Fragment; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.UserHandle; import android.service.notification.NotifyingApp; import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceCategory; import android.support.v7.preference.PreferenceScreen; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IconDrawableFactory; import android.util.Log; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.InstalledAppCounter; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.widget.AppPreference; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.wrapper.PackageManagerWrapper; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; /** * This controller displays a list of recently used apps and a "See all" button. If there is * no recently used app, "See all" will be displayed as "Notifications". */ public class RecentNotifyingAppsPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin { private static final String TAG = "RecentNotisCtrl"; private static final String KEY_PREF_CATEGORY = "recent_notifications_category"; @VisibleForTesting static final String KEY_DIVIDER = "all_notifications_divider"; @VisibleForTesting static final String KEY_SEE_ALL = "all_notifications"; private static final int SHOW_RECENT_APP_COUNT = 5; private static final Set<String> SKIP_SYSTEM_PACKAGES = new ArraySet<>(); private final Fragment mHost; private final PackageManager mPm; private final NotificationBackend mNotificationBackend; private final int mUserId; private final IconDrawableFactory mIconDrawableFactory; private List<NotifyingApp> mApps; private final ApplicationsState mApplicationsState; private PreferenceCategory mCategory; private Preference mSeeAllPref; private Preference mDivider; private boolean mHasRecentApps; static { SKIP_SYSTEM_PACKAGES.addAll(Arrays.asList( "android", "com.android.phone", "com.android.settings", "com.android.systemui", "com.android.providers.calendar", "com.android.providers.media" )); } public RecentNotifyingAppsPreferenceController(Context context, NotificationBackend backend, Application app, Fragment host) { this(context, backend, app == null ? null : ApplicationsState.getInstance(app), host); } @VisibleForTesting(otherwise = VisibleForTesting.NONE) RecentNotifyingAppsPreferenceController(Context context, NotificationBackend backend, ApplicationsState appState, Fragment host) { super(context); mIconDrawableFactory = IconDrawableFactory.newInstance(context); mUserId = UserHandle.myUserId(); mPm = context.getPackageManager(); mHost = host; mApplicationsState = appState; mNotificationBackend = backend; } @Override public boolean isAvailable() { return true; } @Override public String getPreferenceKey() { return KEY_PREF_CATEGORY; } @Override public void updateNonIndexableKeys(List<String> keys) { PreferenceControllerMixin.super.updateNonIndexableKeys(keys); // Don't index category name into search. It's not actionable. keys.add(KEY_PREF_CATEGORY); keys.add(KEY_DIVIDER); } @Override public void displayPreference(PreferenceScreen screen) { mCategory = (PreferenceCategory) screen.findPreference(getPreferenceKey()); mSeeAllPref = screen.findPreference(KEY_SEE_ALL); mDivider = screen.findPreference(KEY_DIVIDER); super.displayPreference(screen); refreshUi(mCategory.getContext()); } @Override public void updateState(Preference preference) { super.updateState(preference); refreshUi(mCategory.getContext()); // Show total number of installed apps as See all's summary. new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON, new PackageManagerWrapper(mContext.getPackageManager())) { @Override protected void onCountComplete(int num) { if (mHasRecentApps) { mSeeAllPref.setTitle(mContext.getString(R.string.see_all_apps_title, num)); } else { mSeeAllPref.setSummary(mContext.getString(R.string.apps_summary, num)); } } }.execute(); } @VisibleForTesting void refreshUi(Context prefContext) { reloadData(); final List<NotifyingApp> recentApps = getDisplayableRecentAppList(); if (recentApps != null && !recentApps.isEmpty()) { mHasRecentApps = true; displayRecentApps(prefContext, recentApps); } else { mHasRecentApps = false; displayOnlyAllAppsLink(); } } @VisibleForTesting void reloadData() { mApps = mNotificationBackend.getRecentApps(); } private void displayOnlyAllAppsLink() { mCategory.setTitle(null); mDivider.setVisible(false); mSeeAllPref.setTitle(R.string.notifications_title); mSeeAllPref.setIcon(null); int prefCount = mCategory.getPreferenceCount(); for (int i = prefCount - 1; i >= 0; i--) { final Preference pref = mCategory.getPreference(i); if (!TextUtils.equals(pref.getKey(), KEY_SEE_ALL)) { mCategory.removePreference(pref); } } } private void displayRecentApps(Context prefContext, List<NotifyingApp> recentApps) { mCategory.setTitle(R.string.recent_notifications); mDivider.setVisible(true); mSeeAllPref.setSummary(null); mSeeAllPref.setIcon(R.drawable.ic_chevron_right_24dp); // Rebind prefs/avoid adding new prefs if possible. Adding/removing prefs causes jank. // Build a cached preference pool final Map<String, Preference> 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, pref); } } final int recentAppsCount = recentApps.size(); for (int i = 0; i < recentAppsCount; i++) { final NotifyingApp app = recentApps.get(i); // Bind recent apps to existing prefs if possible, or create a new pref. final String pkgName = app.getPackage(); final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry(app.getPackage(), mUserId); if (appEntry == null) { continue; } boolean rebindPref = true; Preference pref = appPreferences.remove(pkgName); if (pref == null) { pref = new AppPreference(prefContext); rebindPref = false; } pref.setKey(pkgName); pref.setTitle(appEntry.label); pref.setIcon(mIconDrawableFactory.getBadgedIcon(appEntry.info)); pref.setSummary(Utils.formatRelativeTime(mContext, System.currentTimeMillis() - app.getLastNotified(), false)); pref.setOrder(i); pref.setOnPreferenceClickListener(preference -> { AppInfoBase.startAppInfoFragment(AppNotificationSettings.class, R.string.notifications_title, pkgName, appEntry.info.uid, mHost, 1001 /*RequestCode */, MetricsProto.MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS); return true; }); if (!rebindPref) { mCategory.addPreference(pref); } } // Remove unused prefs from pref cache pool for (Preference unusedPrefs : appPreferences.values()) { mCategory.removePreference(unusedPrefs); } } private List<NotifyingApp> getDisplayableRecentAppList() { Collections.sort(mApps); List<NotifyingApp> displayableApps = new ArrayList<>(SHOW_RECENT_APP_COUNT); int count = 0; for (NotifyingApp app : mApps) { final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry( app.getPackage(), mUserId); if (appEntry == null) { continue; } if (!shouldIncludePkgInRecents(app.getPackage())) { continue; } displayableApps.add(app); count++; if (count >= SHOW_RECENT_APP_COUNT) { break; } } return displayableApps; } /** * Whether or not the app should be included in recent list. */ private boolean shouldIncludePkgInRecents(String pkgName) { if (SKIP_SYSTEM_PACKAGES.contains(pkgName)) { Log.d(TAG, "System package, skipping " + pkgName); return false; } final Intent launchIntent = new Intent().addCategory(Intent.CATEGORY_LAUNCHER) .setPackage(pkgName); if (mPm.resolveActivity(launchIntent, 0) == null) { // Not visible on launcher -> likely not a user visible app, skip if non-instant. final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry(pkgName, mUserId); if (!AppUtils.isInstant(appEntry.info)) { Log.d(TAG, "Not a user visible or instant app, skipping " + pkgName); return false; } } return true; } }