Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0ef1608a authored by Mukta Kulkarni's avatar Mukta Kulkarni Committed by Android (Google) Code Review
Browse files

Merge "Sounddose custom actions for notifications" into main

parents 651cdb64 e3d5ef9a
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -1163,3 +1163,11 @@ flag {
    purpose: PURPOSE_BUGFIX
  }
}

flag {
  name: "sounddose_customization"
  namespace: "systemui"
  description: "Enables custom actions for sounddose notifications"
  bug: "345227709"
}
+1 −0
Original line number Diff line number Diff line
@@ -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);
+2 −0
Original line number Diff line number Diff line
@@ -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>
+132 −17
Original line number Diff line number Diff line
@@ -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}:
@@ -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;
@@ -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;
@@ -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)));
@@ -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);
                        }
@@ -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:
@@ -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);
    }
@@ -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);
        }
    }
}
+15 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;

/**
@@ -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,
@@ -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) {

@@ -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