Loading src/com/android/settings/applications/AppStateNotificationBridge.java +64 −2 Original line number Diff line number Diff line Loading @@ -20,8 +20,12 @@ import android.app.usage.UsageStatsManager; import android.content.Context; import android.text.format.DateUtils; import android.util.ArrayMap; import android.view.View; import android.view.ViewGroup; import android.widget.Switch; import com.android.settings.R; import com.android.settings.notification.NotificationBackend; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.AppFilter; Loading @@ -37,13 +41,18 @@ import java.util.Map; */ public class AppStateNotificationBridge extends AppStateBaseBridge { private final Context mContext; private UsageStatsManager mUsageStatsManager; private NotificationBackend mBackend; private static final int DAYS_TO_CHECK = 7; public AppStateNotificationBridge(ApplicationsState appState, Callback callback, UsageStatsManager usageStatsManager) { public AppStateNotificationBridge(Context context, ApplicationsState appState, Callback callback, UsageStatsManager usageStatsManager, NotificationBackend backend) { super(appState, callback); mContext = context; mUsageStatsManager = usageStatsManager; mBackend = backend; } @Override Loading @@ -55,6 +64,7 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { for (AppEntry entry : apps) { NotificationsSentState stats = map.get(entry.info.packageName); calculateAvgSentCounts(stats); addBlockStatus(entry, stats); entry.extraInfo = stats; } } Loading @@ -64,6 +74,7 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { Map<String, NotificationsSentState> map = getAggregatedUsageEvents(); NotificationsSentState stats = map.get(entry.info.packageName); calculateAvgSentCounts(stats); addBlockStatus(entry, stats); entry.extraInfo = stats; } Loading @@ -83,6 +94,14 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { } } private void addBlockStatus(AppEntry entry, NotificationsSentState stats) { if (stats != null) { stats.blocked = mBackend.getNotificationsBanned(entry.info.packageName, entry.info.uid); stats.systemApp = mBackend.isSystemApp(mContext, entry.info); stats.blockable = !stats.systemApp || (stats.systemApp && stats.blocked); } } private void calculateAvgSentCounts(NotificationsSentState stats) { if (stats != null) { stats.avgSentDaily = Math.round((float) stats.sentCount / DAYS_TO_CHECK); Loading Loading @@ -130,6 +149,28 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { return null; } public View.OnClickListener getSwitchOnClickListener(final AppEntry entry) { if (entry != null) { return v -> { ViewGroup view = (ViewGroup) v; Switch toggle = view.findViewById(R.id.switchWidget); if (toggle != null) { if (!toggle.isEnabled()) { return; } toggle.toggle(); mBackend.setNotificationsEnabledForPackage( entry.info.packageName, entry.info.uid, toggle.isChecked()); NotificationsSentState stats = getNotificationsSentState(entry); if (stats != null) { stats.blocked = !toggle.isChecked(); } } }; } return null; } public static final AppFilter FILTER_APP_NOTIFICATION_RECENCY = new AppFilter() { @Override public void init() { Loading Loading @@ -192,6 +233,24 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { } }; public static final boolean enableSwitch(AppEntry entry) { NotificationsSentState stats = getNotificationsSentState(entry); if (stats == null) { return false; } return stats.blockable; } public static final boolean checkSwitch(AppEntry entry) { NotificationsSentState stats = getNotificationsSentState(entry); if (stats == null) { return false; } return !stats.blocked; } /** * NotificationsSentState contains how often an app sends notifications and how recently it sent * one. Loading @@ -201,5 +260,8 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { public int avgSentWeekly = 0; public long lastSent = 0; public int sentCount = 0; public boolean blockable; public boolean blocked; public boolean systemApp; } } src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java +33 −2 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.Switch; import android.widget.TextView; import com.android.settings.R; Loading @@ -47,7 +48,10 @@ public class ApplicationViewHolder extends RecyclerView.ViewHolder { final TextView mSummary; @VisibleForTesting final TextView mDisabled; @VisibleForTesting final ViewGroup mWidgetContainer; @VisibleForTesting final Switch mSwitch; ApplicationViewHolder(View itemView, boolean keepStableHeight) { super(itemView); Loading @@ -57,11 +61,30 @@ public class ApplicationViewHolder extends RecyclerView.ViewHolder { mSummary = itemView.findViewById(android.R.id.summary); mDisabled = itemView.findViewById(R.id.appendix); mKeepStableHeight = keepStableHeight; mSwitch = itemView.findViewById(R.id.switchWidget); mWidgetContainer = itemView.findViewById(android.R.id.widget_frame); } static View newView(ViewGroup parent) { return LayoutInflater.from(parent.getContext()) return newView(parent, false); } static View newView(ViewGroup parent, boolean twoTarget) { ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext()) .inflate(R.layout.preference_app, parent, false); if (twoTarget) { final ViewGroup widgetFrame = view.findViewById(android.R.id.widget_frame); if (widgetFrame != null) { LayoutInflater.from(parent.getContext()) .inflate(R.layout.preference_widget_master_switch, widgetFrame, true); View divider = LayoutInflater.from(parent.getContext()).inflate( R.layout.preference_two_target_divider, view, false); // second to last, before widget frame view.addView(divider, view.getChildCount() - 1); } } return view; } void setSummary(CharSequence summary) { Loading Loading @@ -141,4 +164,12 @@ public class ApplicationViewHolder extends RecyclerView.ViewHolder { setSummary(invalidSizeStr); } } void updateSwitch(View.OnClickListener listener, boolean enabled, boolean checked) { if (mSwitch != null && mWidgetContainer != null) { mWidgetContainer.setOnClickListener(listener); mSwitch.setChecked(checked); mSwitch.setEnabled(enabled); } } } src/com/android/settings/applications/manageapplications/ManageApplications.java +31 −3 Original line number Diff line number Diff line Loading @@ -109,6 +109,7 @@ import com.android.settings.dashboard.SummaryLoader; import com.android.settings.fuelgauge.HighPowerDetail; import com.android.settings.notification.AppNotificationSettings; import com.android.settings.notification.ConfigureNotificationSettings; import com.android.settings.notification.NotificationBackend; import com.android.settings.widget.LoadingViewController; import com.android.settings.wifi.AppStateChangeWifiStateBridge; import com.android.settings.wifi.ChangeWifiStateDetails; Loading Loading @@ -223,6 +224,7 @@ public class ManageApplications extends InstrumentedFragment private Spinner mFilterSpinner; private FilterSpinnerAdapter mFilterAdapter; private UsageStatsManager mUsageStatsManager; private NotificationBackend mNotificationBackend; private ResetAppsHelper mResetAppsHelper; private String mVolumeUuid; private int mStorageType; Loading Loading @@ -292,6 +294,7 @@ public class ManageApplications extends InstrumentedFragment mListType = LIST_TYPE_NOTIFICATION; mUsageStatsManager = (UsageStatsManager) getContext().getSystemService(Context.USAGE_STATS_SERVICE); mNotificationBackend = new NotificationBackend(); mSortOrder = R.id.sort_order_recent_notification; screenTitle = R.string.app_notifications_title; } else { Loading Loading @@ -869,8 +872,9 @@ public class ManageApplications extends InstrumentedFragment mContext = manageApplications.getActivity(); mAppFilter = appFilter; if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) { mExtraInfoBridge = new AppStateNotificationBridge(mState, this, manageApplications.mUsageStatsManager); mExtraInfoBridge = new AppStateNotificationBridge(mContext, mState, this, manageApplications.mUsageStatsManager, manageApplications.mNotificationBackend); } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) { mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this); } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) { Loading Loading @@ -988,7 +992,12 @@ public class ManageApplications extends InstrumentedFragment @Override public ApplicationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { final View view = ApplicationViewHolder.newView(parent); View view; if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) { view = ApplicationViewHolder.newView(parent, true /* twoTarget */); } else { view = ApplicationViewHolder.newView(parent, false /* twoTarget */); } return new ApplicationViewHolder(view, shouldUseStableItemHeight(mManageApplications.mListType)); } Loading Loading @@ -1276,6 +1285,7 @@ public class ManageApplications extends InstrumentedFragment mState.ensureIcon(entry); holder.setIcon(entry.icon); updateSummary(holder, entry); updateSwitch(holder, entry); holder.updateDisableView(entry.info); } holder.setEnabled(isEnabled(position)); Loading Loading @@ -1328,6 +1338,24 @@ public class ManageApplications extends InstrumentedFragment } } private void updateSwitch(ApplicationViewHolder holder, AppEntry entry) { switch (mManageApplications.mListType) { case LIST_TYPE_NOTIFICATION: holder.updateSwitch(((AppStateNotificationBridge) mExtraInfoBridge) .getSwitchOnClickListener(entry), AppStateNotificationBridge.enableSwitch(entry), AppStateNotificationBridge.checkSwitch(entry)); if (entry.extraInfo != null) { holder.setSummary(AppStateNotificationBridge.getSummary(mContext, (NotificationsSentState) entry.extraInfo, (mLastSortMode == R.id.sort_order_recent_notification))); } else { holder.setSummary(null); } break; } } private boolean hasExtraView() { return mExtraViewController != null && mExtraViewController.shouldShow(); Loading src/com/android/settings/notification/NotificationBackend.java +19 −2 Original line number Diff line number Diff line Loading @@ -65,11 +65,15 @@ public class NotificationBackend { public AppRow loadAppRow(Context context, PackageManager pm, PackageInfo app) { final AppRow row = loadAppRow(context, pm, app.applicationInfo); recordCanBeBlocked(context, pm, app, row); return row; } void recordCanBeBlocked(Context context, PackageManager pm, PackageInfo app, AppRow row) { row.systemApp = Utils.isSystemPackage(context.getResources(), pm, app); final String[] nonBlockablePkgs = context.getResources().getStringArray( com.android.internal.R.array.config_nonBlockableNotificationPackages); markAppRowWithBlockables(nonBlockablePkgs, row, app.packageName); return row; } @VisibleForTesting static void markAppRowWithBlockables(String[] nonBlockablePkgs, AppRow row, Loading @@ -92,6 +96,19 @@ public class NotificationBackend { } } public boolean isSystemApp(Context context, ApplicationInfo app) { try { PackageInfo info = context.getPackageManager().getPackageInfo( app.packageName, PackageManager.GET_SIGNATURES); final AppRow row = new AppRow(); recordCanBeBlocked(context, context.getPackageManager(), info, row); return row.systemApp; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return false; } public boolean getNotificationsBanned(String pkg, int uid) { try { final boolean enabled = sINM.areNotificationsEnabledForPackage(pkg, uid); Loading tests/robotests/src/com/android/settings/applications/AppStateNotificationBridgeTest.java +47 −2 Original line number Diff line number Diff line Loading @@ -33,8 +33,11 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.usage.UsageEvents; Loading @@ -44,9 +47,12 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Looper; import android.os.Parcel; import android.view.ViewGroup; import android.widget.Switch; import com.android.settings.R; import com.android.settings.applications.AppStateNotificationBridge.NotificationsSentState; import com.android.settings.notification.NotificationBackend; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; Loading Loading @@ -74,6 +80,8 @@ public class AppStateNotificationBridgeTest { private ApplicationsState mState; @Mock private UsageStatsManager mUsageStats; @Mock private NotificationBackend mBackend; private Context mContext; private AppStateNotificationBridge mBridge; Loading @@ -82,10 +90,12 @@ public class AppStateNotificationBridgeTest { MockitoAnnotations.initMocks(this); when(mState.newSession(any())).thenReturn(mSession); when(mState.getBackgroundLooper()).thenReturn(mock(Looper.class)); when(mBackend.getNotificationsBanned(anyString(), anyInt())).thenReturn(true); when(mBackend.isSystemApp(any(), any())).thenReturn(true); mContext = RuntimeEnvironment.application.getApplicationContext(); mBridge = new AppStateNotificationBridge(mState, mock(AppStateBaseBridge.Callback.class), mUsageStats); mBridge = new AppStateNotificationBridge(mContext, mState, mock(AppStateBaseBridge.Callback.class), mUsageStats, mBackend); } private AppEntry getMockAppEntry(String pkg) { Loading Loading @@ -213,6 +223,9 @@ public class AppStateNotificationBridgeTest { assertThat(((NotificationsSentState) apps.get(0).extraInfo).lastSent).isEqualTo(6); assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentDaily).isEqualTo(1); assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentWeekly).isEqualTo(0); assertThat(((NotificationsSentState) apps.get(0).extraInfo).blocked).isTrue(); assertThat(((NotificationsSentState) apps.get(0).extraInfo).systemApp).isTrue(); assertThat(((NotificationsSentState) apps.get(0).extraInfo).blockable).isTrue(); } @Test Loading Loading @@ -281,6 +294,9 @@ public class AppStateNotificationBridgeTest { assertThat(((NotificationsSentState) entry.extraInfo).lastSent).isEqualTo(12); assertThat(((NotificationsSentState) entry.extraInfo).avgSentDaily).isEqualTo(2); assertThat(((NotificationsSentState) entry.extraInfo).avgSentWeekly).isEqualTo(0); assertThat(((NotificationsSentState) entry.extraInfo).blocked).isTrue(); assertThat(((NotificationsSentState) entry.extraInfo).systemApp).isTrue(); assertThat(((NotificationsSentState) entry.extraInfo).blockable).isTrue(); } @Test Loading Loading @@ -410,4 +426,33 @@ public class AppStateNotificationBridgeTest { assertThat(entries).containsExactly(veryFrequentDailyEntry, notFrequentDailyEntry, veryFrequentWeeklyEntry, notFrequentWeeklyEntry); } @Test public void testSwitchOnClickListener() { ViewGroup parent = mock(ViewGroup.class); Switch toggle = mock(Switch.class); when(toggle.isChecked()).thenReturn(true); when(toggle.isEnabled()).thenReturn(true); when(parent.findViewById(anyInt())).thenReturn(toggle); AppEntry entry = mock(AppEntry.class); entry.info = new ApplicationInfo(); entry.info.packageName = "pkg"; entry.info.uid = 1356; entry.extraInfo = new NotificationsSentState(); ViewGroup.OnClickListener listener = mBridge.getSwitchOnClickListener(entry); listener.onClick(parent); verify(toggle).toggle(); verify(mBackend).setNotificationsEnabledForPackage( entry.info.packageName, entry.info.uid, true); assertThat(((NotificationsSentState) entry.extraInfo).blocked).isFalse(); } @Test public void testSwitchViews_nullDoesNotCrash() { mBridge.enableSwitch(null); mBridge.checkSwitch(null); } } Loading
src/com/android/settings/applications/AppStateNotificationBridge.java +64 −2 Original line number Diff line number Diff line Loading @@ -20,8 +20,12 @@ import android.app.usage.UsageStatsManager; import android.content.Context; import android.text.format.DateUtils; import android.util.ArrayMap; import android.view.View; import android.view.ViewGroup; import android.widget.Switch; import com.android.settings.R; import com.android.settings.notification.NotificationBackend; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.AppFilter; Loading @@ -37,13 +41,18 @@ import java.util.Map; */ public class AppStateNotificationBridge extends AppStateBaseBridge { private final Context mContext; private UsageStatsManager mUsageStatsManager; private NotificationBackend mBackend; private static final int DAYS_TO_CHECK = 7; public AppStateNotificationBridge(ApplicationsState appState, Callback callback, UsageStatsManager usageStatsManager) { public AppStateNotificationBridge(Context context, ApplicationsState appState, Callback callback, UsageStatsManager usageStatsManager, NotificationBackend backend) { super(appState, callback); mContext = context; mUsageStatsManager = usageStatsManager; mBackend = backend; } @Override Loading @@ -55,6 +64,7 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { for (AppEntry entry : apps) { NotificationsSentState stats = map.get(entry.info.packageName); calculateAvgSentCounts(stats); addBlockStatus(entry, stats); entry.extraInfo = stats; } } Loading @@ -64,6 +74,7 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { Map<String, NotificationsSentState> map = getAggregatedUsageEvents(); NotificationsSentState stats = map.get(entry.info.packageName); calculateAvgSentCounts(stats); addBlockStatus(entry, stats); entry.extraInfo = stats; } Loading @@ -83,6 +94,14 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { } } private void addBlockStatus(AppEntry entry, NotificationsSentState stats) { if (stats != null) { stats.blocked = mBackend.getNotificationsBanned(entry.info.packageName, entry.info.uid); stats.systemApp = mBackend.isSystemApp(mContext, entry.info); stats.blockable = !stats.systemApp || (stats.systemApp && stats.blocked); } } private void calculateAvgSentCounts(NotificationsSentState stats) { if (stats != null) { stats.avgSentDaily = Math.round((float) stats.sentCount / DAYS_TO_CHECK); Loading Loading @@ -130,6 +149,28 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { return null; } public View.OnClickListener getSwitchOnClickListener(final AppEntry entry) { if (entry != null) { return v -> { ViewGroup view = (ViewGroup) v; Switch toggle = view.findViewById(R.id.switchWidget); if (toggle != null) { if (!toggle.isEnabled()) { return; } toggle.toggle(); mBackend.setNotificationsEnabledForPackage( entry.info.packageName, entry.info.uid, toggle.isChecked()); NotificationsSentState stats = getNotificationsSentState(entry); if (stats != null) { stats.blocked = !toggle.isChecked(); } } }; } return null; } public static final AppFilter FILTER_APP_NOTIFICATION_RECENCY = new AppFilter() { @Override public void init() { Loading Loading @@ -192,6 +233,24 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { } }; public static final boolean enableSwitch(AppEntry entry) { NotificationsSentState stats = getNotificationsSentState(entry); if (stats == null) { return false; } return stats.blockable; } public static final boolean checkSwitch(AppEntry entry) { NotificationsSentState stats = getNotificationsSentState(entry); if (stats == null) { return false; } return !stats.blocked; } /** * NotificationsSentState contains how often an app sends notifications and how recently it sent * one. Loading @@ -201,5 +260,8 @@ public class AppStateNotificationBridge extends AppStateBaseBridge { public int avgSentWeekly = 0; public long lastSent = 0; public int sentCount = 0; public boolean blockable; public boolean blocked; public boolean systemApp; } }
src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java +33 −2 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.Switch; import android.widget.TextView; import com.android.settings.R; Loading @@ -47,7 +48,10 @@ public class ApplicationViewHolder extends RecyclerView.ViewHolder { final TextView mSummary; @VisibleForTesting final TextView mDisabled; @VisibleForTesting final ViewGroup mWidgetContainer; @VisibleForTesting final Switch mSwitch; ApplicationViewHolder(View itemView, boolean keepStableHeight) { super(itemView); Loading @@ -57,11 +61,30 @@ public class ApplicationViewHolder extends RecyclerView.ViewHolder { mSummary = itemView.findViewById(android.R.id.summary); mDisabled = itemView.findViewById(R.id.appendix); mKeepStableHeight = keepStableHeight; mSwitch = itemView.findViewById(R.id.switchWidget); mWidgetContainer = itemView.findViewById(android.R.id.widget_frame); } static View newView(ViewGroup parent) { return LayoutInflater.from(parent.getContext()) return newView(parent, false); } static View newView(ViewGroup parent, boolean twoTarget) { ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext()) .inflate(R.layout.preference_app, parent, false); if (twoTarget) { final ViewGroup widgetFrame = view.findViewById(android.R.id.widget_frame); if (widgetFrame != null) { LayoutInflater.from(parent.getContext()) .inflate(R.layout.preference_widget_master_switch, widgetFrame, true); View divider = LayoutInflater.from(parent.getContext()).inflate( R.layout.preference_two_target_divider, view, false); // second to last, before widget frame view.addView(divider, view.getChildCount() - 1); } } return view; } void setSummary(CharSequence summary) { Loading Loading @@ -141,4 +164,12 @@ public class ApplicationViewHolder extends RecyclerView.ViewHolder { setSummary(invalidSizeStr); } } void updateSwitch(View.OnClickListener listener, boolean enabled, boolean checked) { if (mSwitch != null && mWidgetContainer != null) { mWidgetContainer.setOnClickListener(listener); mSwitch.setChecked(checked); mSwitch.setEnabled(enabled); } } }
src/com/android/settings/applications/manageapplications/ManageApplications.java +31 −3 Original line number Diff line number Diff line Loading @@ -109,6 +109,7 @@ import com.android.settings.dashboard.SummaryLoader; import com.android.settings.fuelgauge.HighPowerDetail; import com.android.settings.notification.AppNotificationSettings; import com.android.settings.notification.ConfigureNotificationSettings; import com.android.settings.notification.NotificationBackend; import com.android.settings.widget.LoadingViewController; import com.android.settings.wifi.AppStateChangeWifiStateBridge; import com.android.settings.wifi.ChangeWifiStateDetails; Loading Loading @@ -223,6 +224,7 @@ public class ManageApplications extends InstrumentedFragment private Spinner mFilterSpinner; private FilterSpinnerAdapter mFilterAdapter; private UsageStatsManager mUsageStatsManager; private NotificationBackend mNotificationBackend; private ResetAppsHelper mResetAppsHelper; private String mVolumeUuid; private int mStorageType; Loading Loading @@ -292,6 +294,7 @@ public class ManageApplications extends InstrumentedFragment mListType = LIST_TYPE_NOTIFICATION; mUsageStatsManager = (UsageStatsManager) getContext().getSystemService(Context.USAGE_STATS_SERVICE); mNotificationBackend = new NotificationBackend(); mSortOrder = R.id.sort_order_recent_notification; screenTitle = R.string.app_notifications_title; } else { Loading Loading @@ -869,8 +872,9 @@ public class ManageApplications extends InstrumentedFragment mContext = manageApplications.getActivity(); mAppFilter = appFilter; if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) { mExtraInfoBridge = new AppStateNotificationBridge(mState, this, manageApplications.mUsageStatsManager); mExtraInfoBridge = new AppStateNotificationBridge(mContext, mState, this, manageApplications.mUsageStatsManager, manageApplications.mNotificationBackend); } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) { mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this); } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) { Loading Loading @@ -988,7 +992,12 @@ public class ManageApplications extends InstrumentedFragment @Override public ApplicationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { final View view = ApplicationViewHolder.newView(parent); View view; if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) { view = ApplicationViewHolder.newView(parent, true /* twoTarget */); } else { view = ApplicationViewHolder.newView(parent, false /* twoTarget */); } return new ApplicationViewHolder(view, shouldUseStableItemHeight(mManageApplications.mListType)); } Loading Loading @@ -1276,6 +1285,7 @@ public class ManageApplications extends InstrumentedFragment mState.ensureIcon(entry); holder.setIcon(entry.icon); updateSummary(holder, entry); updateSwitch(holder, entry); holder.updateDisableView(entry.info); } holder.setEnabled(isEnabled(position)); Loading Loading @@ -1328,6 +1338,24 @@ public class ManageApplications extends InstrumentedFragment } } private void updateSwitch(ApplicationViewHolder holder, AppEntry entry) { switch (mManageApplications.mListType) { case LIST_TYPE_NOTIFICATION: holder.updateSwitch(((AppStateNotificationBridge) mExtraInfoBridge) .getSwitchOnClickListener(entry), AppStateNotificationBridge.enableSwitch(entry), AppStateNotificationBridge.checkSwitch(entry)); if (entry.extraInfo != null) { holder.setSummary(AppStateNotificationBridge.getSummary(mContext, (NotificationsSentState) entry.extraInfo, (mLastSortMode == R.id.sort_order_recent_notification))); } else { holder.setSummary(null); } break; } } private boolean hasExtraView() { return mExtraViewController != null && mExtraViewController.shouldShow(); Loading
src/com/android/settings/notification/NotificationBackend.java +19 −2 Original line number Diff line number Diff line Loading @@ -65,11 +65,15 @@ public class NotificationBackend { public AppRow loadAppRow(Context context, PackageManager pm, PackageInfo app) { final AppRow row = loadAppRow(context, pm, app.applicationInfo); recordCanBeBlocked(context, pm, app, row); return row; } void recordCanBeBlocked(Context context, PackageManager pm, PackageInfo app, AppRow row) { row.systemApp = Utils.isSystemPackage(context.getResources(), pm, app); final String[] nonBlockablePkgs = context.getResources().getStringArray( com.android.internal.R.array.config_nonBlockableNotificationPackages); markAppRowWithBlockables(nonBlockablePkgs, row, app.packageName); return row; } @VisibleForTesting static void markAppRowWithBlockables(String[] nonBlockablePkgs, AppRow row, Loading @@ -92,6 +96,19 @@ public class NotificationBackend { } } public boolean isSystemApp(Context context, ApplicationInfo app) { try { PackageInfo info = context.getPackageManager().getPackageInfo( app.packageName, PackageManager.GET_SIGNATURES); final AppRow row = new AppRow(); recordCanBeBlocked(context, context.getPackageManager(), info, row); return row.systemApp; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return false; } public boolean getNotificationsBanned(String pkg, int uid) { try { final boolean enabled = sINM.areNotificationsEnabledForPackage(pkg, uid); Loading
tests/robotests/src/com/android/settings/applications/AppStateNotificationBridgeTest.java +47 −2 Original line number Diff line number Diff line Loading @@ -33,8 +33,11 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.usage.UsageEvents; Loading @@ -44,9 +47,12 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Looper; import android.os.Parcel; import android.view.ViewGroup; import android.widget.Switch; import com.android.settings.R; import com.android.settings.applications.AppStateNotificationBridge.NotificationsSentState; import com.android.settings.notification.NotificationBackend; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; Loading Loading @@ -74,6 +80,8 @@ public class AppStateNotificationBridgeTest { private ApplicationsState mState; @Mock private UsageStatsManager mUsageStats; @Mock private NotificationBackend mBackend; private Context mContext; private AppStateNotificationBridge mBridge; Loading @@ -82,10 +90,12 @@ public class AppStateNotificationBridgeTest { MockitoAnnotations.initMocks(this); when(mState.newSession(any())).thenReturn(mSession); when(mState.getBackgroundLooper()).thenReturn(mock(Looper.class)); when(mBackend.getNotificationsBanned(anyString(), anyInt())).thenReturn(true); when(mBackend.isSystemApp(any(), any())).thenReturn(true); mContext = RuntimeEnvironment.application.getApplicationContext(); mBridge = new AppStateNotificationBridge(mState, mock(AppStateBaseBridge.Callback.class), mUsageStats); mBridge = new AppStateNotificationBridge(mContext, mState, mock(AppStateBaseBridge.Callback.class), mUsageStats, mBackend); } private AppEntry getMockAppEntry(String pkg) { Loading Loading @@ -213,6 +223,9 @@ public class AppStateNotificationBridgeTest { assertThat(((NotificationsSentState) apps.get(0).extraInfo).lastSent).isEqualTo(6); assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentDaily).isEqualTo(1); assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentWeekly).isEqualTo(0); assertThat(((NotificationsSentState) apps.get(0).extraInfo).blocked).isTrue(); assertThat(((NotificationsSentState) apps.get(0).extraInfo).systemApp).isTrue(); assertThat(((NotificationsSentState) apps.get(0).extraInfo).blockable).isTrue(); } @Test Loading Loading @@ -281,6 +294,9 @@ public class AppStateNotificationBridgeTest { assertThat(((NotificationsSentState) entry.extraInfo).lastSent).isEqualTo(12); assertThat(((NotificationsSentState) entry.extraInfo).avgSentDaily).isEqualTo(2); assertThat(((NotificationsSentState) entry.extraInfo).avgSentWeekly).isEqualTo(0); assertThat(((NotificationsSentState) entry.extraInfo).blocked).isTrue(); assertThat(((NotificationsSentState) entry.extraInfo).systemApp).isTrue(); assertThat(((NotificationsSentState) entry.extraInfo).blockable).isTrue(); } @Test Loading Loading @@ -410,4 +426,33 @@ public class AppStateNotificationBridgeTest { assertThat(entries).containsExactly(veryFrequentDailyEntry, notFrequentDailyEntry, veryFrequentWeeklyEntry, notFrequentWeeklyEntry); } @Test public void testSwitchOnClickListener() { ViewGroup parent = mock(ViewGroup.class); Switch toggle = mock(Switch.class); when(toggle.isChecked()).thenReturn(true); when(toggle.isEnabled()).thenReturn(true); when(parent.findViewById(anyInt())).thenReturn(toggle); AppEntry entry = mock(AppEntry.class); entry.info = new ApplicationInfo(); entry.info.packageName = "pkg"; entry.info.uid = 1356; entry.extraInfo = new NotificationsSentState(); ViewGroup.OnClickListener listener = mBridge.getSwitchOnClickListener(entry); listener.onClick(parent); verify(toggle).toggle(); verify(mBackend).setNotificationsEnabledForPackage( entry.info.packageName, entry.info.uid, true); assertThat(((NotificationsSentState) entry.extraInfo).blocked).isFalse(); } @Test public void testSwitchViews_nullDoesNotCrash() { mBridge.enableSwitch(null); mBridge.checkSwitch(null); } }