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

Commit e3d5ef9a authored by muktakulkarni's avatar muktakulkarni
Browse files

Sounddose custom actions for notifications

Flag: com.android.systemui.sounddose_customization
Test: Manual took video
Change-Id: I21bbfb1bb5bfe5dfedce9d88bf652d8b1fb1ea91
Bug: 345227709
parent 47d3c329
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -1163,3 +1163,11 @@ flag {
    purpose: PURPOSE_BUGFIX
    purpose: PURPOSE_BUGFIX
  }
  }
}
}

flag {
  name: "sounddose_customization"
  namespace: "systemui"
  description: "Enables custom actions for sounddose notifications"
  bug: "345227709"
}
+1 −0
Original line number Original line Diff line number Diff line
@@ -26,6 +26,7 @@ import com.android.systemui.plugins.annotations.ProvidesInterface;
@DependsOn(target = Callback.class)
@DependsOn(target = Callback.class)
public interface VolumeDialog extends Plugin {
public interface VolumeDialog extends Plugin {
    String ACTION = "com.android.systemui.action.PLUGIN_VOLUME";
    String ACTION = "com.android.systemui.action.PLUGIN_VOLUME";
    String ACTION_VOLUME_UNDO = "com.android.systemui.volume.ACTION_VOLUME_UNDO";
    int VERSION = 1;
    int VERSION = 1;


    void init(int windowType, Callback callback);
    void init(int windowType, Callback callback);
+2 −0
Original line number Original line Diff line number Diff line
@@ -3656,4 +3656,6 @@
    <string name="home_controls_dream_label">Home Controls</string>
    <string name="home_controls_dream_label">Home Controls</string>
    <!-- Description for home control panel [CHAR LIMIT=67] -->
    <!-- Description for home control panel [CHAR LIMIT=67] -->
    <string name="home_controls_dream_description">Quickly access your home controls as a screensaver</string>
    <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>
</resources>
+132 −17
Original line number Original line Diff line number Diff line
@@ -30,21 +30,31 @@ import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.AudioManager;
import android.provider.Settings;
import android.provider.Settings;
import android.util.Log;
import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.view.WindowManager;


import androidx.annotation.VisibleForTesting;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.messages.nano.SystemMessageProto;
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.dagger.qualifiers.Background;
import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.res.R;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.NotificationChannels;
import com.android.systemui.util.NotificationChannels;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.DelayableExecutor;


import com.google.common.collect.ImmutableList;

import dagger.assisted.Assisted;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
import dagger.assisted.AssistedInject;


import java.util.Optional;

/**
/**
 * A class that implements the three Computed Sound Dose-related warnings defined in
 * A class that implements the three Computed Sound Dose-related warnings defined in
 * {@link AudioManager}:
 * {@link AudioManager}:
@@ -75,6 +85,9 @@ public class CsdWarningDialog extends SystemUIDialog
    private static final int KEY_CONFIRM_ALLOWED_AFTER_MS = 1000; // milliseconds
    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
    // 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;
    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 Context mContext;
    private final AudioManager mAudioManager;
    private final AudioManager mAudioManager;
@@ -95,21 +108,32 @@ public class CsdWarningDialog extends SystemUIDialog


    private long mShowTime;
    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
     * To inject dependencies and allow for easier testing
     */
     */
    @AssistedFactory
    @AssistedFactory
    public interface Factory {
    public interface Factory {
        /**
        /** Create a dialog object */
         * Create a dialog object
        CsdWarningDialog create(
         */
                int csdWarning,
        CsdWarningDialog create(int csdWarning, Runnable onCleanup);
                Runnable onCleanup,
                Optional<ImmutableList<Pair<String, Intent>>> actionIntents);
    }
    }


    @AssistedInject
    @AssistedInject
    public CsdWarningDialog(@Assisted @AudioManager.CsdWarning int csdWarning, Context context,
    public CsdWarningDialog(
            AudioManager audioManager, NotificationManager notificationManager,
            @Assisted @AudioManager.CsdWarning int csdWarning,
            @Background DelayableExecutor delayableExecutor, @Assisted Runnable onCleanup) {
            Context context,
            AudioManager audioManager,
            NotificationManager notificationManager,
            @Background DelayableExecutor delayableExecutor,
            @Assisted Runnable onCleanup,
            @Assisted Optional<ImmutableList<Pair<String, Intent>>> actionIntents,
            BroadcastDispatcher broadcastDispatcher) {
        super(context);
        super(context);
        mCsdWarning = csdWarning;
        mCsdWarning = csdWarning;
        mContext = context;
        mContext = context;
@@ -118,7 +142,8 @@ public class CsdWarningDialog extends SystemUIDialog
        mOnCleanup = onCleanup;
        mOnCleanup = onCleanup;


        mDelayableExecutor = delayableExecutor;
        mDelayableExecutor = delayableExecutor;

        mActionIntents = actionIntents;
        mBroadcastDispatcher = broadcastDispatcher;
        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
        setShowForAllUsers(true);
        setShowForAllUsers(true);
        setMessage(mContext.getString(getStringForWarning(csdWarning)));
        setMessage(mContext.getString(getStringForWarning(csdWarning)));
@@ -133,10 +158,13 @@ public class CsdWarningDialog extends SystemUIDialog
                Context.RECEIVER_EXPORTED_UNAUDITED);
                Context.RECEIVER_EXPORTED_UNAUDITED);


        if (csdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) {
        if (csdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) {
            mNoUserActionRunnable = () -> {
            mNoUserActionRunnable =
                    () -> {
                        if (mCsdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) {
                        if (mCsdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) {
                    // unlike on the 5x dose repeat, level is only reduced to RS1 when the warning
                            // unlike on the 5x dose repeat, level is only reduced to RS1 when the
                    // is not acknowledged quickly enough
                            // warning is not acknowledged quickly enough
                            mCachedMediaStreamVolume =
                                    mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
                            mAudioManager.lowerVolumeToRs1();
                            mAudioManager.lowerVolumeToRs1();
                            sendNotification(/* for5XCsd= */ false);
                            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) {
    private @StringRes int getStringForWarning(@AudioManager.CsdWarning int csdWarning) {
        switch (csdWarning) {
        switch (csdWarning) {
            case AudioManager.CSD_WARNING_DOSE_REACHED_1X:
            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);
            Log.w(TAG, "Notification dose repeat 5x is not shown for " + mCsdWarning);
            return;
            return;
        }
        }

        mCachedMediaStreamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        mAudioManager.lowerVolumeToRs1();
        mAudioManager.lowerVolumeToRs1();
        sendNotification(/*for5XCsd=*/true);
        sendNotification(/*for5XCsd=*/true);
    }
    }
@@ -288,7 +348,62 @@ public class CsdWarningDialog extends SystemUIDialog
                        .setAutoCancel(true)
                        .setAutoCancel(true)
                        .setCategory(Notification.CATEGORY_SYSTEM);
                        .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,
        mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_CSD_LOWER_AUDIO,
                builder.build());
                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 Original line Diff line number Diff line
@@ -50,6 +50,7 @@ import android.app.KeyguardManager;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Configuration;
@@ -77,6 +78,7 @@ import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.Global;
import android.text.InputFilter;
import android.text.InputFilter;
import android.util.Log;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseBooleanArray;
import android.view.ContextThemeWrapper;
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.panel.shared.flag.VolumePanelFlag;
import com.android.systemui.volume.ui.navigation.VolumeNavigator;
import com.android.systemui.volume.ui.navigation.VolumeNavigator;


import com.google.common.collect.ImmutableList;

import dagger.Lazy;
import dagger.Lazy;


import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
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 com.android.systemui.util.time.SystemClock mSystemClock;
    private final VolumePanelFlag mVolumePanelFlag;
    private final VolumePanelFlag mVolumePanelFlag;
    private final VolumeDialogInteractor mInteractor;
    private final VolumeDialogInteractor mInteractor;
    // Optional actions for soundDose
    private Optional<ImmutableList<Pair<String, Intent>>> mCsdWarningNotificationActions =
            Optional.of(ImmutableList.of());


    public VolumeDialogImpl(
    public VolumeDialogImpl(
            Context context,
            Context context,
@@ -2198,6 +2206,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
        rescheduleTimeoutH();
        rescheduleTimeoutH();
    }
    }


    public void setCsdWarningNotificationActionIntents(
            ImmutableList<Pair<String, Intent>> actionIntent) {
        mCsdWarningNotificationActions = Optional.of(actionIntent);
    }

    @VisibleForTesting void showCsdWarningH(int csdWarning, int durationMs) {
    @VisibleForTesting void showCsdWarningH(int csdWarning, int durationMs) {
        synchronized (mSafetyWarningLock) {
        synchronized (mSafetyWarningLock) {


@@ -2212,7 +2225,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
                recheckH(null);
                recheckH(null);
            };
            };


            mCsdDialog = mCsdWarningDialogFactory.create(csdWarning, cleanUp);
            mCsdDialog = mCsdWarningDialogFactory.create(
                    csdWarning, cleanUp, mCsdWarningNotificationActions);
            mCsdDialog.show();
            mCsdDialog.show();
        }
        }
        recheckH(null);
        recheckH(null);
Loading