Loading packages/SystemUI/aconfig/systemui.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -1163,3 +1163,11 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "sounddose_customization" namespace: "systemui" description: "Enables custom actions for sounddose notifications" bug: "345227709" } packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialog.java +1 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.systemui.plugins.annotations.ProvidesInterface; @DependsOn(target = Callback.class) public interface VolumeDialog extends Plugin { String ACTION = "com.android.systemui.action.PLUGIN_VOLUME"; String ACTION_VOLUME_UNDO = "com.android.systemui.volume.ACTION_VOLUME_UNDO"; int VERSION = 1; void init(int windowType, Callback callback); Loading packages/SystemUI/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -3656,4 +3656,6 @@ <string name="home_controls_dream_label">Home Controls</string> <!-- Description for home control panel [CHAR LIMIT=67] --> <string name="home_controls_dream_description">Quickly access your home controls as a screensaver</string> <!-- Label for volume undo action [CHAR LIMIT=NONE] --> <string name="volume_undo_action">Undo</string> </resources> packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java +132 −17 Original line number Diff line number Diff line Loading @@ -30,21 +30,31 @@ import android.content.IntentFilter; import android.media.AudioManager; import android.provider.Settings; import android.util.Log; import android.util.Pair; import android.view.KeyEvent; import android.view.WindowManager; import androidx.annotation.VisibleForTesting; import com.android.internal.annotations.GuardedBy; import com.android.internal.messages.nano.SystemMessageProto; import com.android.systemui.Flags; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.plugins.VolumeDialog; import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.util.NotificationChannels; import com.android.systemui.util.concurrency.DelayableExecutor; import com.google.common.collect.ImmutableList; import dagger.assisted.Assisted; import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; import java.util.Optional; /** * A class that implements the three Computed Sound Dose-related warnings defined in * {@link AudioManager}: Loading Loading @@ -75,6 +85,9 @@ public class CsdWarningDialog extends SystemUIDialog private static final int KEY_CONFIRM_ALLOWED_AFTER_MS = 1000; // milliseconds // time after which action is taken when the user hasn't ack'd or dismissed the dialog public static final int NO_ACTION_TIMEOUT_MS = 5000; // Notification dismiss intent private static final String DISMISS_CSD_NOTIFICATION = "com.android.systemui.volume.DISMISS_CSD_NOTIFICATION"; private final Context mContext; private final AudioManager mAudioManager; Loading @@ -95,21 +108,32 @@ public class CsdWarningDialog extends SystemUIDialog private long mShowTime; @VisibleForTesting public int mCachedMediaStreamVolume; private Optional<ImmutableList<Pair<String, Intent>>> mActionIntents; private final BroadcastDispatcher mBroadcastDispatcher; /** * To inject dependencies and allow for easier testing */ @AssistedFactory public interface Factory { /** * Create a dialog object */ CsdWarningDialog create(int csdWarning, Runnable onCleanup); /** Create a dialog object */ CsdWarningDialog create( int csdWarning, Runnable onCleanup, Optional<ImmutableList<Pair<String, Intent>>> actionIntents); } @AssistedInject public CsdWarningDialog(@Assisted @AudioManager.CsdWarning int csdWarning, Context context, AudioManager audioManager, NotificationManager notificationManager, @Background DelayableExecutor delayableExecutor, @Assisted Runnable onCleanup) { public CsdWarningDialog( @Assisted @AudioManager.CsdWarning int csdWarning, Context context, AudioManager audioManager, NotificationManager notificationManager, @Background DelayableExecutor delayableExecutor, @Assisted Runnable onCleanup, @Assisted Optional<ImmutableList<Pair<String, Intent>>> actionIntents, BroadcastDispatcher broadcastDispatcher) { super(context); mCsdWarning = csdWarning; mContext = context; Loading @@ -118,7 +142,8 @@ public class CsdWarningDialog extends SystemUIDialog mOnCleanup = onCleanup; mDelayableExecutor = delayableExecutor; mActionIntents = actionIntents; mBroadcastDispatcher = broadcastDispatcher; getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); setShowForAllUsers(true); setMessage(mContext.getString(getStringForWarning(csdWarning))); Loading @@ -133,10 +158,13 @@ public class CsdWarningDialog extends SystemUIDialog Context.RECEIVER_EXPORTED_UNAUDITED); if (csdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) { mNoUserActionRunnable = () -> { mNoUserActionRunnable = () -> { if (mCsdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) { // unlike on the 5x dose repeat, level is only reduced to RS1 when the warning // is not acknowledged quickly enough // unlike on the 5x dose repeat, level is only reduced to RS1 when the // warning is not acknowledged quickly enough mCachedMediaStreamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); mAudioManager.lowerVolumeToRs1(); sendNotification(/* for5XCsd= */ false); } Loading Loading @@ -242,6 +270,38 @@ public class CsdWarningDialog extends SystemUIDialog } }; @VisibleForTesting public final BroadcastReceiver mReceiverUndo = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Flags.sounddoseCustomization() && VolumeDialog.ACTION_VOLUME_UNDO.equals(intent.getAction())) { if (D.BUG) Log.d(TAG, "Received ACTION_VOLUME_UNDO"); mAudioManager.setStreamVolume( AudioManager.STREAM_MUSIC, mCachedMediaStreamVolume, AudioManager.FLAG_SHOW_UI); mNotificationManager.cancel( SystemMessageProto.SystemMessage.NOTE_CSD_LOWER_AUDIO); destroy(); } } }; @VisibleForTesting public final BroadcastReceiver mReceiverDismissNotification = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Flags.sounddoseCustomization() && DISMISS_CSD_NOTIFICATION.equals(intent.getAction())) { if (D.BUG) Log.d(TAG, "Received DISMISS_CSD_NOTIFICATION"); destroy(); } } }; private @StringRes int getStringForWarning(@AudioManager.CsdWarning int csdWarning) { switch (csdWarning) { case AudioManager.CSD_WARNING_DOSE_REACHED_1X: Loading @@ -259,7 +319,7 @@ public class CsdWarningDialog extends SystemUIDialog Log.w(TAG, "Notification dose repeat 5x is not shown for " + mCsdWarning); return; } mCachedMediaStreamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); mAudioManager.lowerVolumeToRs1(); sendNotification(/*for5XCsd=*/true); } Loading Loading @@ -288,7 +348,62 @@ public class CsdWarningDialog extends SystemUIDialog .setAutoCancel(true) .setCategory(Notification.CATEGORY_SYSTEM); if (Flags.sounddoseCustomization() && mActionIntents.isPresent() && !mActionIntents.get().isEmpty()) { ImmutableList<Pair<String, Intent>> actionIntentsList = mActionIntents.get(); for (Pair<String, Intent> intentPair : actionIntentsList) { if (intentPair != null && intentPair.first != null && intentPair.second != null) { PendingIntent pendingActionIntent = PendingIntent.getBroadcast(mContext, 0, intentPair.second, FLAG_IMMUTABLE); builder.addAction(0, intentPair.first, pendingActionIntent); // Register receiver to undo volume only when // notification conaining the undo action would be sent. if (intentPair.first == mContext.getString(R.string.volume_undo_action)) { final IntentFilter filterUndo = new IntentFilter( VolumeDialog.ACTION_VOLUME_UNDO); mBroadcastDispatcher.registerReceiver(mReceiverUndo, filterUndo, /* executor = default */ null, /* user = default */ null, Context.RECEIVER_NOT_EXPORTED, /* permission = default */ null); // Register receiver to learn if notification has been dismissed. // This is required to unregister receivers to prevent leak. Intent dismissIntent = new Intent(DISMISS_CSD_NOTIFICATION) .setPackage(mContext.getPackageName()); PendingIntent pendingDismissIntent = PendingIntent.getBroadcast(mContext, 0, dismissIntent, FLAG_IMMUTABLE); mBroadcastDispatcher.registerReceiver(mReceiverDismissNotification, new IntentFilter(DISMISS_CSD_NOTIFICATION), /* executor = default */ null, /* user = default */ null, Context.RECEIVER_NOT_EXPORTED, /* permission = default */ null); builder.setDeleteIntent(pendingDismissIntent); } } } } mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_CSD_LOWER_AUDIO, builder.build()); } /** * Unregister the Undo action receiver when the notification is dismissed. * Unregister cannot happen when CsdWarning dialog is dismissed * as the notification lifecycle would be longer. */ @VisibleForTesting public void destroy() { if (Flags.sounddoseCustomization() && mActionIntents.isPresent() && !mActionIntents.get().isEmpty()) { mBroadcastDispatcher.unregisterReceiver(mReceiverUndo); mBroadcastDispatcher.unregisterReceiver(mReceiverDismissNotification); } } } packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +15 −1 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ import android.app.KeyguardManager; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.content.res.Configuration; Loading Loading @@ -77,6 +78,7 @@ import android.provider.Settings; import android.provider.Settings.Global; import android.text.InputFilter; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; import android.view.ContextThemeWrapper; Loading Loading @@ -140,11 +142,14 @@ import com.android.systemui.volume.domain.interactor.VolumePanelNavigationIntera import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag; import com.android.systemui.volume.ui.navigation.VolumeNavigator; import com.google.common.collect.ImmutableList; import dagger.Lazy; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.Consumer; /** Loading Loading @@ -315,6 +320,9 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, private final com.android.systemui.util.time.SystemClock mSystemClock; private final VolumePanelFlag mVolumePanelFlag; private final VolumeDialogInteractor mInteractor; // Optional actions for soundDose private Optional<ImmutableList<Pair<String, Intent>>> mCsdWarningNotificationActions = Optional.of(ImmutableList.of()); public VolumeDialogImpl( Context context, Loading Loading @@ -2198,6 +2206,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, rescheduleTimeoutH(); } public void setCsdWarningNotificationActionIntents( ImmutableList<Pair<String, Intent>> actionIntent) { mCsdWarningNotificationActions = Optional.of(actionIntent); } @VisibleForTesting void showCsdWarningH(int csdWarning, int durationMs) { synchronized (mSafetyWarningLock) { Loading @@ -2212,7 +2225,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, recheckH(null); }; mCsdDialog = mCsdWarningDialogFactory.create(csdWarning, cleanUp); mCsdDialog = mCsdWarningDialogFactory.create( csdWarning, cleanUp, mCsdWarningNotificationActions); mCsdDialog.show(); } recheckH(null); Loading Loading
packages/SystemUI/aconfig/systemui.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -1163,3 +1163,11 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "sounddose_customization" namespace: "systemui" description: "Enables custom actions for sounddose notifications" bug: "345227709" }
packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialog.java +1 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.systemui.plugins.annotations.ProvidesInterface; @DependsOn(target = Callback.class) public interface VolumeDialog extends Plugin { String ACTION = "com.android.systemui.action.PLUGIN_VOLUME"; String ACTION_VOLUME_UNDO = "com.android.systemui.volume.ACTION_VOLUME_UNDO"; int VERSION = 1; void init(int windowType, Callback callback); Loading
packages/SystemUI/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -3656,4 +3656,6 @@ <string name="home_controls_dream_label">Home Controls</string> <!-- Description for home control panel [CHAR LIMIT=67] --> <string name="home_controls_dream_description">Quickly access your home controls as a screensaver</string> <!-- Label for volume undo action [CHAR LIMIT=NONE] --> <string name="volume_undo_action">Undo</string> </resources>
packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java +132 −17 Original line number Diff line number Diff line Loading @@ -30,21 +30,31 @@ import android.content.IntentFilter; import android.media.AudioManager; import android.provider.Settings; import android.util.Log; import android.util.Pair; import android.view.KeyEvent; import android.view.WindowManager; import androidx.annotation.VisibleForTesting; import com.android.internal.annotations.GuardedBy; import com.android.internal.messages.nano.SystemMessageProto; import com.android.systemui.Flags; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.plugins.VolumeDialog; import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.util.NotificationChannels; import com.android.systemui.util.concurrency.DelayableExecutor; import com.google.common.collect.ImmutableList; import dagger.assisted.Assisted; import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; import java.util.Optional; /** * A class that implements the three Computed Sound Dose-related warnings defined in * {@link AudioManager}: Loading Loading @@ -75,6 +85,9 @@ public class CsdWarningDialog extends SystemUIDialog private static final int KEY_CONFIRM_ALLOWED_AFTER_MS = 1000; // milliseconds // time after which action is taken when the user hasn't ack'd or dismissed the dialog public static final int NO_ACTION_TIMEOUT_MS = 5000; // Notification dismiss intent private static final String DISMISS_CSD_NOTIFICATION = "com.android.systemui.volume.DISMISS_CSD_NOTIFICATION"; private final Context mContext; private final AudioManager mAudioManager; Loading @@ -95,21 +108,32 @@ public class CsdWarningDialog extends SystemUIDialog private long mShowTime; @VisibleForTesting public int mCachedMediaStreamVolume; private Optional<ImmutableList<Pair<String, Intent>>> mActionIntents; private final BroadcastDispatcher mBroadcastDispatcher; /** * To inject dependencies and allow for easier testing */ @AssistedFactory public interface Factory { /** * Create a dialog object */ CsdWarningDialog create(int csdWarning, Runnable onCleanup); /** Create a dialog object */ CsdWarningDialog create( int csdWarning, Runnable onCleanup, Optional<ImmutableList<Pair<String, Intent>>> actionIntents); } @AssistedInject public CsdWarningDialog(@Assisted @AudioManager.CsdWarning int csdWarning, Context context, AudioManager audioManager, NotificationManager notificationManager, @Background DelayableExecutor delayableExecutor, @Assisted Runnable onCleanup) { public CsdWarningDialog( @Assisted @AudioManager.CsdWarning int csdWarning, Context context, AudioManager audioManager, NotificationManager notificationManager, @Background DelayableExecutor delayableExecutor, @Assisted Runnable onCleanup, @Assisted Optional<ImmutableList<Pair<String, Intent>>> actionIntents, BroadcastDispatcher broadcastDispatcher) { super(context); mCsdWarning = csdWarning; mContext = context; Loading @@ -118,7 +142,8 @@ public class CsdWarningDialog extends SystemUIDialog mOnCleanup = onCleanup; mDelayableExecutor = delayableExecutor; mActionIntents = actionIntents; mBroadcastDispatcher = broadcastDispatcher; getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); setShowForAllUsers(true); setMessage(mContext.getString(getStringForWarning(csdWarning))); Loading @@ -133,10 +158,13 @@ public class CsdWarningDialog extends SystemUIDialog Context.RECEIVER_EXPORTED_UNAUDITED); if (csdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) { mNoUserActionRunnable = () -> { mNoUserActionRunnable = () -> { if (mCsdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) { // unlike on the 5x dose repeat, level is only reduced to RS1 when the warning // is not acknowledged quickly enough // unlike on the 5x dose repeat, level is only reduced to RS1 when the // warning is not acknowledged quickly enough mCachedMediaStreamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); mAudioManager.lowerVolumeToRs1(); sendNotification(/* for5XCsd= */ false); } Loading Loading @@ -242,6 +270,38 @@ public class CsdWarningDialog extends SystemUIDialog } }; @VisibleForTesting public final BroadcastReceiver mReceiverUndo = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Flags.sounddoseCustomization() && VolumeDialog.ACTION_VOLUME_UNDO.equals(intent.getAction())) { if (D.BUG) Log.d(TAG, "Received ACTION_VOLUME_UNDO"); mAudioManager.setStreamVolume( AudioManager.STREAM_MUSIC, mCachedMediaStreamVolume, AudioManager.FLAG_SHOW_UI); mNotificationManager.cancel( SystemMessageProto.SystemMessage.NOTE_CSD_LOWER_AUDIO); destroy(); } } }; @VisibleForTesting public final BroadcastReceiver mReceiverDismissNotification = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Flags.sounddoseCustomization() && DISMISS_CSD_NOTIFICATION.equals(intent.getAction())) { if (D.BUG) Log.d(TAG, "Received DISMISS_CSD_NOTIFICATION"); destroy(); } } }; private @StringRes int getStringForWarning(@AudioManager.CsdWarning int csdWarning) { switch (csdWarning) { case AudioManager.CSD_WARNING_DOSE_REACHED_1X: Loading @@ -259,7 +319,7 @@ public class CsdWarningDialog extends SystemUIDialog Log.w(TAG, "Notification dose repeat 5x is not shown for " + mCsdWarning); return; } mCachedMediaStreamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); mAudioManager.lowerVolumeToRs1(); sendNotification(/*for5XCsd=*/true); } Loading Loading @@ -288,7 +348,62 @@ public class CsdWarningDialog extends SystemUIDialog .setAutoCancel(true) .setCategory(Notification.CATEGORY_SYSTEM); if (Flags.sounddoseCustomization() && mActionIntents.isPresent() && !mActionIntents.get().isEmpty()) { ImmutableList<Pair<String, Intent>> actionIntentsList = mActionIntents.get(); for (Pair<String, Intent> intentPair : actionIntentsList) { if (intentPair != null && intentPair.first != null && intentPair.second != null) { PendingIntent pendingActionIntent = PendingIntent.getBroadcast(mContext, 0, intentPair.second, FLAG_IMMUTABLE); builder.addAction(0, intentPair.first, pendingActionIntent); // Register receiver to undo volume only when // notification conaining the undo action would be sent. if (intentPair.first == mContext.getString(R.string.volume_undo_action)) { final IntentFilter filterUndo = new IntentFilter( VolumeDialog.ACTION_VOLUME_UNDO); mBroadcastDispatcher.registerReceiver(mReceiverUndo, filterUndo, /* executor = default */ null, /* user = default */ null, Context.RECEIVER_NOT_EXPORTED, /* permission = default */ null); // Register receiver to learn if notification has been dismissed. // This is required to unregister receivers to prevent leak. Intent dismissIntent = new Intent(DISMISS_CSD_NOTIFICATION) .setPackage(mContext.getPackageName()); PendingIntent pendingDismissIntent = PendingIntent.getBroadcast(mContext, 0, dismissIntent, FLAG_IMMUTABLE); mBroadcastDispatcher.registerReceiver(mReceiverDismissNotification, new IntentFilter(DISMISS_CSD_NOTIFICATION), /* executor = default */ null, /* user = default */ null, Context.RECEIVER_NOT_EXPORTED, /* permission = default */ null); builder.setDeleteIntent(pendingDismissIntent); } } } } mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_CSD_LOWER_AUDIO, builder.build()); } /** * Unregister the Undo action receiver when the notification is dismissed. * Unregister cannot happen when CsdWarning dialog is dismissed * as the notification lifecycle would be longer. */ @VisibleForTesting public void destroy() { if (Flags.sounddoseCustomization() && mActionIntents.isPresent() && !mActionIntents.get().isEmpty()) { mBroadcastDispatcher.unregisterReceiver(mReceiverUndo); mBroadcastDispatcher.unregisterReceiver(mReceiverDismissNotification); } } }
packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +15 −1 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ import android.app.KeyguardManager; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.content.res.Configuration; Loading Loading @@ -77,6 +78,7 @@ import android.provider.Settings; import android.provider.Settings.Global; import android.text.InputFilter; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; import android.view.ContextThemeWrapper; Loading Loading @@ -140,11 +142,14 @@ import com.android.systemui.volume.domain.interactor.VolumePanelNavigationIntera import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag; import com.android.systemui.volume.ui.navigation.VolumeNavigator; import com.google.common.collect.ImmutableList; import dagger.Lazy; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.Consumer; /** Loading Loading @@ -315,6 +320,9 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, private final com.android.systemui.util.time.SystemClock mSystemClock; private final VolumePanelFlag mVolumePanelFlag; private final VolumeDialogInteractor mInteractor; // Optional actions for soundDose private Optional<ImmutableList<Pair<String, Intent>>> mCsdWarningNotificationActions = Optional.of(ImmutableList.of()); public VolumeDialogImpl( Context context, Loading Loading @@ -2198,6 +2206,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, rescheduleTimeoutH(); } public void setCsdWarningNotificationActionIntents( ImmutableList<Pair<String, Intent>> actionIntent) { mCsdWarningNotificationActions = Optional.of(actionIntent); } @VisibleForTesting void showCsdWarningH(int csdWarning, int durationMs) { synchronized (mSafetyWarningLock) { Loading @@ -2212,7 +2225,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, recheckH(null); }; mCsdDialog = mCsdWarningDialogFactory.create(csdWarning, cleanUp); mCsdDialog = mCsdWarningDialogFactory.create( csdWarning, cleanUp, mCsdWarningNotificationActions); mCsdDialog.show(); } recheckH(null); Loading