Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundledNotificationInfoTest.kt +52 −25 Original line number Diff line number Diff line Loading @@ -18,9 +18,6 @@ package com.android.systemui.statusbar.notification.row import android.app.INotificationManager import android.app.Notification import android.app.NotificationChannel import android.app.NotificationChannel.NEWS_ID import android.app.NotificationChannel.PROMOTIONS_ID import android.app.NotificationChannel.RECS_ID import android.app.NotificationChannel.SOCIAL_MEDIA_ID import android.app.NotificationManager.IMPORTANCE_LOW import android.content.ComponentName Loading Loading @@ -82,7 +79,6 @@ class BundledNotificationInfoTest : SysuiTestCase() { private lateinit var underTest: NotificationInfo private lateinit var notificationChannel: NotificationChannel private lateinit var defaultNotificationChannel: NotificationChannel private lateinit var classifiedNotificationChannel: NotificationChannel private lateinit var sbn: StatusBarNotification private lateinit var entry: NotificationEntry Loading @@ -99,6 +95,7 @@ class BundledNotificationInfoTest : SysuiTestCase() { private val channelEditorDialogController = mock<ChannelEditorDialogController>() private val packageDemotionInteractor = mock<PackageDemotionInteractor>() private val assistantFeedbackController = mock<AssistantFeedbackController>() private val onSettingsClick = mock<NotificationInfo.OnSettingsClickListener>() @Before fun setUp() { Loading Loading @@ -127,8 +124,7 @@ class BundledNotificationInfoTest : SysuiTestCase() { systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME whenever(mockPackageManager.getPackageInfo(eq(TEST_SYSTEM_PACKAGE_NAME), anyInt())) .thenReturn(systemPackageInfo) whenever(mockPackageManager.getPackageInfo(eq("android"), anyInt())) .thenReturn(packageInfo) whenever(mockPackageManager.getPackageInfo(eq("android"), anyInt())).thenReturn(packageInfo) whenever(mockPackageManager.getApplicationLabel(applicationInfo)).thenReturn("App") val assistant = ComponentName("package", "service") Loading @@ -151,16 +147,10 @@ class BundledNotificationInfoTest : SysuiTestCase() { // Some test channels. notificationChannel = NotificationChannel(TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW) defaultNotificationChannel = NotificationChannel( NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_LOW, ) classifiedNotificationChannel = NotificationChannel(SOCIAL_MEDIA_ID, "social", IMPORTANCE_LOW) val notification = Notification() val notification = Notification.Builder(mContext, notificationChannel.id).build() notification.extras.putParcelable( Notification.EXTRA_BUILDER_APPLICATION_INFO, applicationInfo, Loading @@ -187,8 +177,17 @@ class BundledNotificationInfoTest : SysuiTestCase() { whenever(assistantFeedbackController.isFeedbackEnabled).thenReturn(false) whenever(assistantFeedbackController.getInlineDescriptionResource(any())) .thenReturn(R.string.notification_channel_summary_automatic) } whenever( mockINotificationManager.getNotificationChannel( anyString(), anyInt(), eq(sbn.packageName), eq(notificationChannel.id), ) ) .thenReturn(notificationChannel) } @Test fun testHandleCloseControls_DoesNotMakeBinderCalllIfUnchanged() { Loading @@ -202,8 +201,14 @@ class BundledNotificationInfoTest : SysuiTestCase() { @Test fun testToggleCallsUpdate() { whenever(mockINotificationManager.isAdjustmentSupportedForPackage( anyInt(), anyString(), anyString())).thenReturn(true) whenever( mockINotificationManager.isAdjustmentSupportedForPackage( anyInt(), anyString(), anyString(), ) ) .thenReturn(true) bindNotification() Loading @@ -218,8 +223,14 @@ class BundledNotificationInfoTest : SysuiTestCase() { @Test fun testToggleContainerCallsUpdate() { whenever(mockINotificationManager.isAdjustmentSupportedForPackage( anyInt(), anyString(), anyString())).thenReturn(true) whenever( mockINotificationManager.isAdjustmentSupportedForPackage( anyInt(), anyString(), anyString(), ) ) .thenReturn(true) bindNotification() Loading @@ -234,13 +245,29 @@ class BundledNotificationInfoTest : SysuiTestCase() { @Test fun testSummaryText() { val channel = NotificationChannel(NEWS_ID, "news", 2) entry = NotificationEntryBuilder(entry) .updateRanking { it.setChannel(channel) } entry = NotificationEntryBuilder(entry) .updateRanking { it.setChannel(classifiedNotificationChannel) } .build() bindNotification() assertThat((underTest.findViewById(R.id.feature_summary) as TextView).text).isEqualTo( "For App") assertThat((underTest.findViewById(R.id.feature_summary) as TextView).text) .isEqualTo("For App") } @Test fun testTurnOffNotifications() { bindNotification() underTest.findViewById<View>(R.id.turn_off_notifications).performClick() verify(channelEditorDialogController) .prepareDialogForApp( "App", sbn.packageName, sbn.uid, notificationChannel, null, onSettingsClick, ) verify(channelEditorDialogController).show() } private fun bindNotification( Loading @@ -255,7 +282,7 @@ class BundledNotificationInfoTest : SysuiTestCase() { pkg: String = TEST_PACKAGE_NAME, entry: NotificationEntry = this.entry, entryAdapter: EntryAdapter = this.entryAdapter, onSettingsClick: NotificationInfo.OnSettingsClickListener? = mock(), onSettingsClick: NotificationInfo.OnSettingsClickListener? = this.onSettingsClick, onAppSettingsClick: NotificationInfo.OnAppSettingsClickListener? = mock(), onFeedbackClickListener: NotificationInfo.OnFeedbackClickListener? = mock(), uiEventLogger: UiEventLogger = this.uiEventLogger, Loading packages/SystemUI/res/layout/bundled_notification_info.xml +25 −11 Original line number Diff line number Diff line Loading @@ -188,10 +188,9 @@ android:id="@+id/bottom_buttons" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="60dp" android:gravity="center_vertical" android:paddingStart="4dp" android:paddingEnd="4dp" android:layout_marginTop="@*android:dimen/notification_2025_margin" android:minHeight="@dimen/notification_2025_guts_button_size" android:layout_gravity="center_vertical" android:theme="@style/Theme.SystemUI.Dialog" > <Button Loading @@ -201,21 +200,36 @@ android:layout_height="wrap_content" android:layout_alignParentStart="true" android:gravity="center" android:minWidth="@dimen/notification_importance_toggle_size" android:minHeight="@dimen/notification_importance_toggle_size" android:minWidth="@dimen/notification_2025_min_tap_target_size" android:minHeight="@dimen/notification_2025_min_tap_target_size" android:maxWidth="200dp" android:layout_marginEnd="8dp" style="@style/Widget.Dialog.Button.BorderButton" /> <Button android:id="@+id/turn_off_notifications" android:text="@string/inline_turn_off_notifications" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toEndOf="@id/inline_dismiss" android:gravity="center" android:minWidth="@dimen/notification_2025_min_tap_target_size" android:minHeight="@dimen/notification_2025_min_tap_target_size" android:maxWidth="200dp" style="@style/Widget.Dialog.Button.BorderButton"/> style="@style/Widget.Dialog.Button.BorderButton" /> <Button android:id="@+id/done" android:text="@string/inline_ok_button" android:text="@string/inline_done_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:gravity="center" android:minWidth="@dimen/notification_importance_toggle_size" android:minHeight="@dimen/notification_importance_toggle_size" android:minWidth="@dimen/notification_2025_min_tap_target_size" android:minHeight="@dimen/notification_2025_min_tap_target_size" android:maxWidth="125dp" style="@style/Widget.Dialog.Button"/> style="@style/Widget.Dialog.Button" /> </RelativeLayout> </LinearLayout> </com.android.systemui.statusbar.notification.row.BundledNotificationInfo> packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundledNotificationInfo.kt +11 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,13 @@ class BundledNotificationInfo(context: Context?, attrs: AttributeSet?) : NotificationInfo(context, attrs) { override fun bindInlineControls() { val originalChannel = mINotificationManager.getNotificationChannel( mContext.packageName, mSbn.normalizedUserId, mSbn.packageName, mSbn.notification.channelId, ) val enabled = mINotificationManager.isAdjustmentSupportedForPackage( mSbn.normalizedUserId, Loading Loading @@ -79,6 +86,10 @@ class BundledNotificationInfo(context: Context?, attrs: AttributeSet?) : findViewById<TextView>(R.id.feature_summary) .setText(resources.getString(R.string.notification_guts_bundle_summary, mAppName)) val turnOffButton = findViewById<View>(R.id.turn_off_notifications) turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener(originalChannel)) turnOffButton.visibility = if (turnOffButton.hasOnClickListeners()) VISIBLE else GONE val dismissButton = findViewById<View>(R.id.inline_dismiss) dismissButton.setOnClickListener(mOnCloseClickListener) dismissButton.visibility = Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt +48 −50 Original line number Diff line number Diff line Loading @@ -37,18 +37,17 @@ import android.view.WindowInsets.Type.statusBars import android.view.WindowManager import android.widget.TextView import com.android.internal.annotations.VisibleForTesting import com.android.systemui.res.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor import javax.inject.Inject private const val TAG = "ChannelDialogController" /** * ChannelEditorDialogController is the controller for the dialog half-shelf * that allows users to quickly turn off channels. It is launched from the NotificationInfo * guts view and displays controls for toggling app notifications as well as up to 4 channels * from that app like so: * ChannelEditorDialogController is the controller for the dialog half-shelf that allows users to * quickly turn off channels. It is launched from the NotificationInfo guts view and displays * controls for toggling app notifications as well as up to 4 channels from that app like so: * * APP TOGGLE <on/off> * - Channel from which we launched <on/off> Loading @@ -57,7 +56,9 @@ private const val TAG = "ChannelDialogController" * - <on/off> */ @SysUISingleton class ChannelEditorDialogController @Inject constructor( class ChannelEditorDialogController @Inject constructor( private val shadeDialogContextInteractor: ShadeDialogContextInteractor, private val noMan: INotificationManager, private val dialogBuilder: ChannelEditorDialog.Builder, Loading @@ -77,8 +78,7 @@ class ChannelEditorDialogController @Inject constructor( var onFinishListener: OnChannelEditorDialogFinishedListener? = null // Channels handed to us from NotificationInfo @VisibleForTesting internal val channelList = mutableListOf<NotificationChannel>() @VisibleForTesting internal val channelList = mutableListOf<NotificationChannel>() // Map from NotificationChannel to importance private val edits = mutableMapOf<NotificationChannel, Int>() Loading @@ -87,21 +87,20 @@ class ChannelEditorDialogController @Inject constructor( private var appNotificationsCurrentlyEnabled: Boolean? = null // Keep a mapping of NotificationChannel.getGroup() to the actual group name for display @VisibleForTesting internal val groupNameLookup = hashMapOf<String, CharSequence>() @VisibleForTesting internal val groupNameLookup = hashMapOf<String, CharSequence>() private val channelGroupList = mutableListOf<NotificationChannelGroup>() /** * Give the controller all the information it needs to present the dialog * for a given app. Does a bunch of querying of NoMan, but won't present anything yet * Give the controller all the information it needs to present the dialog for a given app. Does * a bunch of querying of NoMan, but won't present anything yet */ fun prepareDialogForApp( appName: String, packageName: String, uid: Int, channel: NotificationChannel, appIcon: Drawable, onSettingsClickListener: NotificationInfo.OnSettingsClickListener? appIcon: Drawable?, onSettingsClickListener: NotificationInfo.OnSettingsClickListener?, ) { this.appName = appName this.packageName = packageName Loading Loading @@ -135,22 +134,23 @@ class ChannelEditorDialogController @Inject constructor( channelList.clear() if (DEFAULT_CHANNEL_ID != channel!!.id) { channelList.add(0, channel!!) channelList.addAll(getDisplayableChannels(channelGroupList.asSequence()) channelList.addAll( getDisplayableChannels(channelGroupList.asSequence()) .filterNot { it.id == channel!!.id } .distinct()) .distinct() ) } } private fun getDisplayableChannels( groupList: Sequence<NotificationChannelGroup> ): Sequence<NotificationChannel> { val channels = groupList .flatMap { group -> group.channels.asSequence() val channels = groupList.flatMap { group -> group.channels .asSequence() .sortedWith(compareBy { group.name?.toString() ?: group.id }) .filterNot { channel -> channel.isImportanceLockedByCriticalDeviceFunction } .filterNot { channel -> channel.isImportanceLockedByCriticalDeviceFunction } } // TODO: sort these by avgSentWeekly, but for now let's just do alphabetical (why not) Loading @@ -164,9 +164,7 @@ class ChannelEditorDialogController @Inject constructor( dialog.show() } /** * Close the dialog without saving. For external callers */ /** Close the dialog without saving. For external callers */ fun close() { done() } Loading Loading @@ -218,8 +216,8 @@ class ChannelEditorDialogController @Inject constructor( @Suppress("unchecked_cast") private fun fetchNotificationChannelGroups(): List<NotificationChannelGroup> { return try { noMan.getRecentBlockedNotificationChannelGroupsForPackage(packageName!!, appUid!!) .list as? List<NotificationChannelGroup> ?: listOf() noMan.getRecentBlockedNotificationChannelGroupsForPackage(packageName!!, appUid!!).list as? List<NotificationChannelGroup> ?: listOf() } catch (e: Exception) { Log.e(TAG, "Error fetching channel groups", e) listOf() Loading Loading @@ -311,7 +309,8 @@ class ChannelEditorDialogController @Inject constructor( setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL) setWindowAnimations(com.android.internal.R.style.Animation_InputMethod) attributes = attributes.apply { attributes = attributes.apply { format = PixelFormat.TRANSLUCENT title = ChannelEditorDialogController::class.java.simpleName gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL Loading @@ -323,22 +322,21 @@ class ChannelEditorDialogController @Inject constructor( } } private val wmFlags = (WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) private val wmFlags = (WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) } class ChannelEditorDialog(context: Context, theme: Int) : Dialog(context, theme) { fun updateDoneButtonText(hasChanges: Boolean) { findViewById<TextView>(R.id.done_button)?.setText( if (hasChanges) R.string.inline_ok_button else R.string.inline_done_button) findViewById<TextView>(R.id.done_button) ?.setText(if (hasChanges) R.string.inline_ok_button else R.string.inline_done_button) } class Builder @Inject constructor() { private lateinit var context: Context fun setContext(context: Context): Builder { this.context = context return this Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +4 −3 Original line number Diff line number Diff line Loading @@ -294,7 +294,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } View turnOffButton = findViewById(R.id.turn_off_notifications); turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener()); turnOffButton.setOnClickListener( getTurnOffNotificationsClickListener(mSingleNotificationChannel)); turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable ? VISIBLE : GONE); Loading Loading @@ -512,13 +513,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G return null; } private OnClickListener getTurnOffNotificationsClickListener() { OnClickListener getTurnOffNotificationsClickListener(NotificationChannel channel) { return ((View view) -> { if (!mPresentingChannelEditorDialog && mChannelEditorDialogController != null) { mPresentingChannelEditorDialog = true; mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid, mSingleNotificationChannel, mPkgIcon, mOnSettingsClickListener); channel, mPkgIcon, mOnSettingsClickListener); mChannelEditorDialogController.setOnFinishListener(() -> { mPresentingChannelEditorDialog = false; mGutsContainer.closeControls(this, false); Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundledNotificationInfoTest.kt +52 −25 Original line number Diff line number Diff line Loading @@ -18,9 +18,6 @@ package com.android.systemui.statusbar.notification.row import android.app.INotificationManager import android.app.Notification import android.app.NotificationChannel import android.app.NotificationChannel.NEWS_ID import android.app.NotificationChannel.PROMOTIONS_ID import android.app.NotificationChannel.RECS_ID import android.app.NotificationChannel.SOCIAL_MEDIA_ID import android.app.NotificationManager.IMPORTANCE_LOW import android.content.ComponentName Loading Loading @@ -82,7 +79,6 @@ class BundledNotificationInfoTest : SysuiTestCase() { private lateinit var underTest: NotificationInfo private lateinit var notificationChannel: NotificationChannel private lateinit var defaultNotificationChannel: NotificationChannel private lateinit var classifiedNotificationChannel: NotificationChannel private lateinit var sbn: StatusBarNotification private lateinit var entry: NotificationEntry Loading @@ -99,6 +95,7 @@ class BundledNotificationInfoTest : SysuiTestCase() { private val channelEditorDialogController = mock<ChannelEditorDialogController>() private val packageDemotionInteractor = mock<PackageDemotionInteractor>() private val assistantFeedbackController = mock<AssistantFeedbackController>() private val onSettingsClick = mock<NotificationInfo.OnSettingsClickListener>() @Before fun setUp() { Loading Loading @@ -127,8 +124,7 @@ class BundledNotificationInfoTest : SysuiTestCase() { systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME whenever(mockPackageManager.getPackageInfo(eq(TEST_SYSTEM_PACKAGE_NAME), anyInt())) .thenReturn(systemPackageInfo) whenever(mockPackageManager.getPackageInfo(eq("android"), anyInt())) .thenReturn(packageInfo) whenever(mockPackageManager.getPackageInfo(eq("android"), anyInt())).thenReturn(packageInfo) whenever(mockPackageManager.getApplicationLabel(applicationInfo)).thenReturn("App") val assistant = ComponentName("package", "service") Loading @@ -151,16 +147,10 @@ class BundledNotificationInfoTest : SysuiTestCase() { // Some test channels. notificationChannel = NotificationChannel(TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW) defaultNotificationChannel = NotificationChannel( NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_LOW, ) classifiedNotificationChannel = NotificationChannel(SOCIAL_MEDIA_ID, "social", IMPORTANCE_LOW) val notification = Notification() val notification = Notification.Builder(mContext, notificationChannel.id).build() notification.extras.putParcelable( Notification.EXTRA_BUILDER_APPLICATION_INFO, applicationInfo, Loading @@ -187,8 +177,17 @@ class BundledNotificationInfoTest : SysuiTestCase() { whenever(assistantFeedbackController.isFeedbackEnabled).thenReturn(false) whenever(assistantFeedbackController.getInlineDescriptionResource(any())) .thenReturn(R.string.notification_channel_summary_automatic) } whenever( mockINotificationManager.getNotificationChannel( anyString(), anyInt(), eq(sbn.packageName), eq(notificationChannel.id), ) ) .thenReturn(notificationChannel) } @Test fun testHandleCloseControls_DoesNotMakeBinderCalllIfUnchanged() { Loading @@ -202,8 +201,14 @@ class BundledNotificationInfoTest : SysuiTestCase() { @Test fun testToggleCallsUpdate() { whenever(mockINotificationManager.isAdjustmentSupportedForPackage( anyInt(), anyString(), anyString())).thenReturn(true) whenever( mockINotificationManager.isAdjustmentSupportedForPackage( anyInt(), anyString(), anyString(), ) ) .thenReturn(true) bindNotification() Loading @@ -218,8 +223,14 @@ class BundledNotificationInfoTest : SysuiTestCase() { @Test fun testToggleContainerCallsUpdate() { whenever(mockINotificationManager.isAdjustmentSupportedForPackage( anyInt(), anyString(), anyString())).thenReturn(true) whenever( mockINotificationManager.isAdjustmentSupportedForPackage( anyInt(), anyString(), anyString(), ) ) .thenReturn(true) bindNotification() Loading @@ -234,13 +245,29 @@ class BundledNotificationInfoTest : SysuiTestCase() { @Test fun testSummaryText() { val channel = NotificationChannel(NEWS_ID, "news", 2) entry = NotificationEntryBuilder(entry) .updateRanking { it.setChannel(channel) } entry = NotificationEntryBuilder(entry) .updateRanking { it.setChannel(classifiedNotificationChannel) } .build() bindNotification() assertThat((underTest.findViewById(R.id.feature_summary) as TextView).text).isEqualTo( "For App") assertThat((underTest.findViewById(R.id.feature_summary) as TextView).text) .isEqualTo("For App") } @Test fun testTurnOffNotifications() { bindNotification() underTest.findViewById<View>(R.id.turn_off_notifications).performClick() verify(channelEditorDialogController) .prepareDialogForApp( "App", sbn.packageName, sbn.uid, notificationChannel, null, onSettingsClick, ) verify(channelEditorDialogController).show() } private fun bindNotification( Loading @@ -255,7 +282,7 @@ class BundledNotificationInfoTest : SysuiTestCase() { pkg: String = TEST_PACKAGE_NAME, entry: NotificationEntry = this.entry, entryAdapter: EntryAdapter = this.entryAdapter, onSettingsClick: NotificationInfo.OnSettingsClickListener? = mock(), onSettingsClick: NotificationInfo.OnSettingsClickListener? = this.onSettingsClick, onAppSettingsClick: NotificationInfo.OnAppSettingsClickListener? = mock(), onFeedbackClickListener: NotificationInfo.OnFeedbackClickListener? = mock(), uiEventLogger: UiEventLogger = this.uiEventLogger, Loading
packages/SystemUI/res/layout/bundled_notification_info.xml +25 −11 Original line number Diff line number Diff line Loading @@ -188,10 +188,9 @@ android:id="@+id/bottom_buttons" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="60dp" android:gravity="center_vertical" android:paddingStart="4dp" android:paddingEnd="4dp" android:layout_marginTop="@*android:dimen/notification_2025_margin" android:minHeight="@dimen/notification_2025_guts_button_size" android:layout_gravity="center_vertical" android:theme="@style/Theme.SystemUI.Dialog" > <Button Loading @@ -201,21 +200,36 @@ android:layout_height="wrap_content" android:layout_alignParentStart="true" android:gravity="center" android:minWidth="@dimen/notification_importance_toggle_size" android:minHeight="@dimen/notification_importance_toggle_size" android:minWidth="@dimen/notification_2025_min_tap_target_size" android:minHeight="@dimen/notification_2025_min_tap_target_size" android:maxWidth="200dp" android:layout_marginEnd="8dp" style="@style/Widget.Dialog.Button.BorderButton" /> <Button android:id="@+id/turn_off_notifications" android:text="@string/inline_turn_off_notifications" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toEndOf="@id/inline_dismiss" android:gravity="center" android:minWidth="@dimen/notification_2025_min_tap_target_size" android:minHeight="@dimen/notification_2025_min_tap_target_size" android:maxWidth="200dp" style="@style/Widget.Dialog.Button.BorderButton"/> style="@style/Widget.Dialog.Button.BorderButton" /> <Button android:id="@+id/done" android:text="@string/inline_ok_button" android:text="@string/inline_done_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:gravity="center" android:minWidth="@dimen/notification_importance_toggle_size" android:minHeight="@dimen/notification_importance_toggle_size" android:minWidth="@dimen/notification_2025_min_tap_target_size" android:minHeight="@dimen/notification_2025_min_tap_target_size" android:maxWidth="125dp" style="@style/Widget.Dialog.Button"/> style="@style/Widget.Dialog.Button" /> </RelativeLayout> </LinearLayout> </com.android.systemui.statusbar.notification.row.BundledNotificationInfo>
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundledNotificationInfo.kt +11 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,13 @@ class BundledNotificationInfo(context: Context?, attrs: AttributeSet?) : NotificationInfo(context, attrs) { override fun bindInlineControls() { val originalChannel = mINotificationManager.getNotificationChannel( mContext.packageName, mSbn.normalizedUserId, mSbn.packageName, mSbn.notification.channelId, ) val enabled = mINotificationManager.isAdjustmentSupportedForPackage( mSbn.normalizedUserId, Loading Loading @@ -79,6 +86,10 @@ class BundledNotificationInfo(context: Context?, attrs: AttributeSet?) : findViewById<TextView>(R.id.feature_summary) .setText(resources.getString(R.string.notification_guts_bundle_summary, mAppName)) val turnOffButton = findViewById<View>(R.id.turn_off_notifications) turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener(originalChannel)) turnOffButton.visibility = if (turnOffButton.hasOnClickListeners()) VISIBLE else GONE val dismissButton = findViewById<View>(R.id.inline_dismiss) dismissButton.setOnClickListener(mOnCloseClickListener) dismissButton.visibility = Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt +48 −50 Original line number Diff line number Diff line Loading @@ -37,18 +37,17 @@ import android.view.WindowInsets.Type.statusBars import android.view.WindowManager import android.widget.TextView import com.android.internal.annotations.VisibleForTesting import com.android.systemui.res.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor import javax.inject.Inject private const val TAG = "ChannelDialogController" /** * ChannelEditorDialogController is the controller for the dialog half-shelf * that allows users to quickly turn off channels. It is launched from the NotificationInfo * guts view and displays controls for toggling app notifications as well as up to 4 channels * from that app like so: * ChannelEditorDialogController is the controller for the dialog half-shelf that allows users to * quickly turn off channels. It is launched from the NotificationInfo guts view and displays * controls for toggling app notifications as well as up to 4 channels from that app like so: * * APP TOGGLE <on/off> * - Channel from which we launched <on/off> Loading @@ -57,7 +56,9 @@ private const val TAG = "ChannelDialogController" * - <on/off> */ @SysUISingleton class ChannelEditorDialogController @Inject constructor( class ChannelEditorDialogController @Inject constructor( private val shadeDialogContextInteractor: ShadeDialogContextInteractor, private val noMan: INotificationManager, private val dialogBuilder: ChannelEditorDialog.Builder, Loading @@ -77,8 +78,7 @@ class ChannelEditorDialogController @Inject constructor( var onFinishListener: OnChannelEditorDialogFinishedListener? = null // Channels handed to us from NotificationInfo @VisibleForTesting internal val channelList = mutableListOf<NotificationChannel>() @VisibleForTesting internal val channelList = mutableListOf<NotificationChannel>() // Map from NotificationChannel to importance private val edits = mutableMapOf<NotificationChannel, Int>() Loading @@ -87,21 +87,20 @@ class ChannelEditorDialogController @Inject constructor( private var appNotificationsCurrentlyEnabled: Boolean? = null // Keep a mapping of NotificationChannel.getGroup() to the actual group name for display @VisibleForTesting internal val groupNameLookup = hashMapOf<String, CharSequence>() @VisibleForTesting internal val groupNameLookup = hashMapOf<String, CharSequence>() private val channelGroupList = mutableListOf<NotificationChannelGroup>() /** * Give the controller all the information it needs to present the dialog * for a given app. Does a bunch of querying of NoMan, but won't present anything yet * Give the controller all the information it needs to present the dialog for a given app. Does * a bunch of querying of NoMan, but won't present anything yet */ fun prepareDialogForApp( appName: String, packageName: String, uid: Int, channel: NotificationChannel, appIcon: Drawable, onSettingsClickListener: NotificationInfo.OnSettingsClickListener? appIcon: Drawable?, onSettingsClickListener: NotificationInfo.OnSettingsClickListener?, ) { this.appName = appName this.packageName = packageName Loading Loading @@ -135,22 +134,23 @@ class ChannelEditorDialogController @Inject constructor( channelList.clear() if (DEFAULT_CHANNEL_ID != channel!!.id) { channelList.add(0, channel!!) channelList.addAll(getDisplayableChannels(channelGroupList.asSequence()) channelList.addAll( getDisplayableChannels(channelGroupList.asSequence()) .filterNot { it.id == channel!!.id } .distinct()) .distinct() ) } } private fun getDisplayableChannels( groupList: Sequence<NotificationChannelGroup> ): Sequence<NotificationChannel> { val channels = groupList .flatMap { group -> group.channels.asSequence() val channels = groupList.flatMap { group -> group.channels .asSequence() .sortedWith(compareBy { group.name?.toString() ?: group.id }) .filterNot { channel -> channel.isImportanceLockedByCriticalDeviceFunction } .filterNot { channel -> channel.isImportanceLockedByCriticalDeviceFunction } } // TODO: sort these by avgSentWeekly, but for now let's just do alphabetical (why not) Loading @@ -164,9 +164,7 @@ class ChannelEditorDialogController @Inject constructor( dialog.show() } /** * Close the dialog without saving. For external callers */ /** Close the dialog without saving. For external callers */ fun close() { done() } Loading Loading @@ -218,8 +216,8 @@ class ChannelEditorDialogController @Inject constructor( @Suppress("unchecked_cast") private fun fetchNotificationChannelGroups(): List<NotificationChannelGroup> { return try { noMan.getRecentBlockedNotificationChannelGroupsForPackage(packageName!!, appUid!!) .list as? List<NotificationChannelGroup> ?: listOf() noMan.getRecentBlockedNotificationChannelGroupsForPackage(packageName!!, appUid!!).list as? List<NotificationChannelGroup> ?: listOf() } catch (e: Exception) { Log.e(TAG, "Error fetching channel groups", e) listOf() Loading Loading @@ -311,7 +309,8 @@ class ChannelEditorDialogController @Inject constructor( setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL) setWindowAnimations(com.android.internal.R.style.Animation_InputMethod) attributes = attributes.apply { attributes = attributes.apply { format = PixelFormat.TRANSLUCENT title = ChannelEditorDialogController::class.java.simpleName gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL Loading @@ -323,22 +322,21 @@ class ChannelEditorDialogController @Inject constructor( } } private val wmFlags = (WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) private val wmFlags = (WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) } class ChannelEditorDialog(context: Context, theme: Int) : Dialog(context, theme) { fun updateDoneButtonText(hasChanges: Boolean) { findViewById<TextView>(R.id.done_button)?.setText( if (hasChanges) R.string.inline_ok_button else R.string.inline_done_button) findViewById<TextView>(R.id.done_button) ?.setText(if (hasChanges) R.string.inline_ok_button else R.string.inline_done_button) } class Builder @Inject constructor() { private lateinit var context: Context fun setContext(context: Context): Builder { this.context = context return this Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +4 −3 Original line number Diff line number Diff line Loading @@ -294,7 +294,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } View turnOffButton = findViewById(R.id.turn_off_notifications); turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener()); turnOffButton.setOnClickListener( getTurnOffNotificationsClickListener(mSingleNotificationChannel)); turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable ? VISIBLE : GONE); Loading Loading @@ -512,13 +513,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G return null; } private OnClickListener getTurnOffNotificationsClickListener() { OnClickListener getTurnOffNotificationsClickListener(NotificationChannel channel) { return ((View view) -> { if (!mPresentingChannelEditorDialog && mChannelEditorDialogController != null) { mPresentingChannelEditorDialog = true; mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid, mSingleNotificationChannel, mPkgIcon, mOnSettingsClickListener); channel, mPkgIcon, mOnSettingsClickListener); mChannelEditorDialogController.setOnFinishListener(() -> { mPresentingChannelEditorDialog = false; mGutsContainer.closeControls(this, false); Loading