diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java index c8bcdaac1d4ad..5f4cf033a45a8 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java @@ -19,6 +19,7 @@ import android.content.ComponentName; import android.media.AudioManager; import android.media.AudioSystem; import android.os.Handler; +import android.os.VibrationEffect; import android.util.SparseArray; import com.android.systemui.plugins.VolumeDialogController.Callbacks; @@ -44,7 +45,8 @@ public interface VolumeDialogController { void setRingerMode(int ringerModeNormal, boolean external); boolean hasVibrator(); - void vibrate(); + void vibrate(VibrationEffect effect); + void scheduleTouchFeedback(); AudioManager getAudioManager(); diff --git a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml index c3e36f2118450..a4b3c99f7ec6b 100644 --- a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml +++ b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml @@ -1,4 +1,18 @@ + diff --git a/packages/SystemUI/res/drawable/rounded_ripple.xml b/packages/SystemUI/res/drawable/rounded_ripple.xml new file mode 100644 index 0000000000000..5588eb21ad8ed --- /dev/null +++ b/packages/SystemUI/res/drawable/rounded_ripple.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml index e45c0f9f5e4eb..94cc001ca01a0 100644 --- a/packages/SystemUI/res/layout/car_volume_dialog.xml +++ b/packages/SystemUI/res/layout/car_volume_dialog.xml @@ -38,7 +38,7 @@ android:orientation="vertical" android:clipChildren="false" android:clipToPadding="false" - android:elevation="@dimen/volume_panel_elevation" > + android:elevation="@dimen/volume_dialog_elevation" > + android:clipChildren="false" + android:clipToPadding="false"> + android:clipChildren="false" + android:clipToPadding="false" > @@ -59,10 +64,10 @@ android:minWidth="@dimen/volume_dialog_panel_width" android:layout_height="wrap_content" android:orientation="vertical" + android:translationZ="@dimen/volume_dialog_elevation" android:clipChildren="false" android:clipToPadding="false" - android:background="@drawable/rounded_bg_full" - android:elevation="@dimen/volume_panel_elevation" > + android:background="@drawable/rounded_bg_full" > diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml index bcc369252c297..6128da8627a95 100644 --- a/packages/SystemUI/res/layout/volume_dialog_row.xml +++ b/packages/SystemUI/res/layout/volume_dialog_row.xml @@ -60,7 +60,7 @@ style="@style/VolumeButtons" android:layout_width="@dimen/volume_dialog_tap_target_size" android:layout_height="@dimen/volume_dialog_tap_target_size" - android:background="?android:selectableItemBackgroundBorderless" + android:background="@drawable/ripple_drawable_20dp" android:layout_marginBottom="@dimen/volume_dialog_row_margin_bottom" android:tint="@color/accent_tint_color_selector" android:soundEffectsEnabled="false" /> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 91c872495c9db..0f07ca45bc7f9 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -270,13 +270,15 @@ @dimen/notification_panel_width + 8dp + 8dp 64dp - 108dp + 116dp 252dp @@ -286,7 +288,7 @@ 4dp - 22dp + 14dp -2dp @@ -294,6 +296,8 @@ 16dp + 9dp + 0x31 @@ -734,8 +738,6 @@ 2dp 6dp - 8dp - 4dp diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 419e9d28fa4e3..e9b2be9b5b3e9 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1350,7 +1350,7 @@ %s volume controls - Calls and notifications will ring + Calls and notifications will ring (%1$s) Media output Phone call output diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index 1a9655ef62cd2..2a27147359513 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -53,7 +53,8 @@ public final class Prefs { Key.NUM_APPS_LAUNCHED, Key.HAS_SEEN_RECENTS_ONBOARDING, Key.SEEN_RINGER_GUIDANCE_COUNT, - Key.QS_HAS_TURNED_OFF_MOBILE_DATA + Key.QS_HAS_TURNED_OFF_MOBILE_DATA, + Key.TOUCHED_RINGER_TOGGLE }) public @interface Key { @Deprecated @@ -91,6 +92,7 @@ public final class Prefs { String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount"; String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed"; String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData"; + String TOUCHED_RINGER_TOGGLE = "TouchedRingerToggle"; } public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index ee6748eee2f80..a97effd3a0232 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.volume; +import static android.media.AudioManager.RINGER_MODE_NORMAL; + import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -29,6 +31,7 @@ import android.database.ContentObserver; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.IAudioService; import android.media.IVolumeController; import android.media.VolumePolicy; import android.media.session.MediaController.PlaybackInfo; @@ -39,6 +42,7 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings; @@ -73,9 +77,11 @@ import java.util.Objects; public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable { private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class); + + private static final int TOUCH_FEEDBACK_TIMEOUT_MS = 1000; private static final int DYNAMIC_STREAM_START_INDEX = 100; private static final int VIBRATE_HINT_DURATION = 50; - private static final AudioAttributes SONFICIATION_VIBRATION_ATTRIBUTES = + private static final AudioAttributes SONIFICIATION_VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) @@ -100,6 +106,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final W mWorker; private final Context mContext; private AudioManager mAudio; + private IAudioService mAudioService; protected StatusBar mStatusBar; private final NotificationManager mNoMan; private final SettingObserver mObserver; @@ -113,6 +120,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private boolean mShowA11yStream; private boolean mShowVolumeDialog; private boolean mShowSafetyWarning; + private long mLastToggledRingerOn; private final NotificationManager mNotificationManager; private boolean mDestroyed; @@ -140,6 +148,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mReceiver.init(); mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); mHasVibrator = mVibrator != null && mVibrator.hasVibrator(); + mAudioService = IAudioService.Stub.asInterface( + ServiceManager.getService(Context.AUDIO_SERVICE)); updateStatusBar(); boolean accessibilityVolumeStreamActive = context.getSystemService( @@ -300,10 +310,24 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mShowSafetyWarning = safetyWarning; } - public void vibrate() { + @Override + public void scheduleTouchFeedback() { + mLastToggledRingerOn = System.currentTimeMillis(); + } + + private void playTouchFeedback() { + if (System.currentTimeMillis() - mLastToggledRingerOn < TOUCH_FEEDBACK_TIMEOUT_MS) { + try { + mAudioService.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD); + } catch (RemoteException e) { + // ignore + } + } + } + + public void vibrate(VibrationEffect effect) { if (mHasVibrator) { - mVibrator.vibrate(VibrationEffect.createOneShot(VIBRATE_HINT_DURATION, - VibrationEffect.DEFAULT_AMPLITUDE), SONFICIATION_VIBRATION_ATTRIBUTES); + mVibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES); } } @@ -425,7 +449,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa for (int stream : STREAMS.keySet()) { updateStreamLevelW(stream, getAudioManagerStreamVolume(stream)); streamStateW(stream).levelMin = getAudioManagerStreamMinVolume(stream); - streamStateW(stream).levelMax = getAudioManagerStreamMaxVolume(stream); + streamStateW(stream).levelMax = Math.max(1, getAudioManagerStreamMaxVolume(stream)); updateStreamMuteW(stream, mAudio.isStreamMute(stream)); final StreamState ss = streamStateW(stream); ss.muteSupported = mAudio.isStreamAffectedByMute(stream); @@ -556,6 +580,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa if (rm == mState.ringerModeInternal) return false; mState.ringerModeInternal = rm; Events.writeEvent(mContext, Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm); + + if (mState.ringerModeInternal == RINGER_MODE_NORMAL) { + playTouchFeedback(); + } + return true; } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 71c7f80ef6594..acdd0c71b4914 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -22,6 +22,10 @@ import static android.media.AudioManager.RINGER_MODE_NORMAL; import static android.media.AudioManager.RINGER_MODE_SILENT; import static android.media.AudioManager.RINGER_MODE_VIBRATE; import static android.media.AudioManager.STREAM_ACCESSIBILITY; +import static android.media.AudioManager.STREAM_ALARM; +import static android.media.AudioManager.STREAM_MUSIC; +import static android.media.AudioManager.STREAM_RING; +import static android.media.AudioManager.STREAM_VOICE_CALL; import static android.view.View.GONE; import static android.view.View.VISIBLE; @@ -41,13 +45,17 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.drawable.ColorDrawable; +import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.MediaPlayer; import android.os.Debug; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; +import android.os.VibrationEffect; +import android.os.Vibrator; import android.provider.Settings; import android.provider.Settings.Global; import android.text.InputFilter; @@ -126,6 +134,7 @@ public class VolumeDialogImpl implements VolumeDialog { private final Accessibility mAccessibility = new Accessibility(); private final ColorStateList mActiveSliderTint; private final ColorStateList mInactiveSliderTint; + private final Vibrator mVibrator; private boolean mShowing; private boolean mShowA11yStream; @@ -146,6 +155,7 @@ public class VolumeDialogImpl implements VolumeDialog { mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext)); mInactiveSliderTint = loadColorStateList(R.color.volume_slider_inactive); mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); + mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); } public void init(int windowType, Callback callback) { @@ -203,6 +213,9 @@ public class VolumeDialogImpl implements VolumeDialog { .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator()) .withEndAction(() -> { mWindow.getDecorView().requestAccessibilityFocus(); + if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true)) { + mRingerIcon.postOnAnimationDelayed(mSinglePress, 1500); + } }) .start(); }); @@ -223,12 +236,16 @@ public class VolumeDialogImpl implements VolumeDialog { mSettingsIcon = mDialog.findViewById(R.id.settings); if (mRows.isEmpty()) { + if (!AudioSystem.isSingleVolume(mContext)) { + addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility, + R.drawable.ic_volume_accessibility, true, false); + } addRow(AudioManager.STREAM_MUSIC, R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true); if (!AudioSystem.isSingleVolume(mContext)) { addRow(AudioManager.STREAM_RING, R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false); - addRow(AudioManager.STREAM_ALARM, + addRow(STREAM_ALARM, R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, true, false); addRow(AudioManager.STREAM_VOICE_CALL, R.drawable.ic_volume_voice, R.drawable.ic_volume_voice, false, false); @@ -236,8 +253,6 @@ public class VolumeDialogImpl implements VolumeDialog { R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false, false); addRow(AudioManager.STREAM_SYSTEM, R.drawable.ic_volume_system, R.drawable.ic_volume_system_mute, false, false); - addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility, - R.drawable.ic_volume_accessibility, true, false); } } else { addExistingRows(); @@ -287,11 +302,11 @@ public class VolumeDialogImpl implements VolumeDialog { if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream); VolumeRow row = new VolumeRow(); initRow(row, stream, iconRes, iconMuteRes, important, defaultStream); - int rowSize; - if (mShowA11yStream && dynamic && (rowSize = mRows.size()) > 1) { - // A11y Stream should be the first in the list, so it's shown to start of other rows - mDialogRowsView.addView(row.view, 0); - mRows.add(rowSize - 2, row); + if (dynamic && mRows.size() > 2) { + // Dynamic Streams should be the first in the list, so they're shown to start of + // everything except a11y + mDialogRowsView.addView(row.view, 1); + mRows.add(1, row); } else { mDialogRowsView.addView(row.view); mRows.add(row); @@ -315,6 +330,11 @@ public class VolumeDialogImpl implements VolumeDialog { return row; } } + for (VolumeRow row : mRows) { + if (row.stream == STREAM_MUSIC) { + return row; + } + } return mRows.get(0); } @@ -414,6 +434,7 @@ public class VolumeDialogImpl implements VolumeDialog { mRingerIcon.setOnClickListener(v -> { Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, AudioManager.STREAM_RING, mRingerIcon.getTag()); + Prefs.putBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true); final StreamState ss = mState.states.get(AudioManager.STREAM_RING); if (ss == null) { return; @@ -424,7 +445,6 @@ public class VolumeDialogImpl implements VolumeDialog { final boolean hasVibrator = mController.hasVibrator(); if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) { if (hasVibrator) { - mController.vibrate(); newRingerMode = AudioManager.RINGER_MODE_VIBRATE; } else { newRingerMode = AudioManager.RINGER_MODE_SILENT; @@ -438,30 +458,56 @@ public class VolumeDialogImpl implements VolumeDialog { } } updateRingerH(); - + provideTouchFeedbackH(newRingerMode); mController.setRingerMode(newRingerMode, false); maybeShowToastH(newRingerMode); }); updateRingerH(); } + + private void provideTouchFeedbackH(int newRingerMode) { + VibrationEffect effect = null; + switch (newRingerMode) { + case RINGER_MODE_NORMAL: + mController.scheduleTouchFeedback(); + break; + case RINGER_MODE_SILENT: + effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + break; + case RINGER_MODE_VIBRATE: + default: + effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); + } + if (effect != null) { + mController.vibrate(effect); + } + } + private void maybeShowToastH(int newRingerMode) { int seenToastCount = Prefs.getInt(mContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, 0); if (seenToastCount > VolumePrefs.SHOW_RINGER_TOAST_COUNT) { return; } - int toastText; + CharSequence toastText = null; switch (newRingerMode) { case RINGER_MODE_NORMAL: - toastText = R.string.volume_dialog_ringer_guidance_ring; + final StreamState ss = mState.states.get(AudioManager.STREAM_RING); + if (ss != null) { + toastText = mContext.getString( + R.string.volume_dialog_ringer_guidance_ring, + Utils.formatPercentage(ss.level, ss.levelMax)); + } break; case RINGER_MODE_SILENT: - toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_silent; + toastText = mContext.getString( + com.android.internal.R.string.volume_dialog_ringer_guidance_silent); break; case RINGER_MODE_VIBRATE: default: - toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate; + toastText = mContext.getString( + com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate); } Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT).show(); @@ -538,7 +584,7 @@ public class VolumeDialogImpl implements VolumeDialog { } private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) { - boolean isActive = row == activeRow; + boolean isActive = row.stream == activeRow.stream; if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) { return mShowA11yStream; } @@ -550,7 +596,17 @@ public class VolumeDialogImpl implements VolumeDialog { return true; } - return row.defaultStream || isActive; + if (isActive) { + return true; + } + + if (row.defaultStream) { + return activeRow.stream == STREAM_RING + || activeRow.stream == STREAM_ALARM + || activeRow.stream == STREAM_VOICE_CALL; + } + + return false; } private void updateRowsH(final VolumeRow activeRow) { @@ -670,7 +726,8 @@ public class VolumeDialogImpl implements VolumeDialog { if (mActiveStream != state.activeStream) { mPrevActiveStream = mActiveStream; mActiveStream = state.activeStream; - updateRowsH(getActiveRow()); + VolumeRow activeRow = getActiveRow(); + updateRowsH(activeRow); rescheduleTimeoutH(); } for (VolumeRow row : mRows) { @@ -696,7 +753,7 @@ public class VolumeDialogImpl implements VolumeDialog { final boolean isA11yStream = row.stream == STREAM_ACCESSIBILITY; final boolean isRingStream = row.stream == AudioManager.STREAM_RING; final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM; - final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM; + final boolean isAlarmStream = row.stream == STREAM_ALARM; final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC; final boolean isRingVibrate = isRingStream && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE; @@ -921,6 +978,21 @@ public class VolumeDialogImpl implements VolumeDialog { } } + private Runnable mSinglePress = new Runnable() { + @Override + public void run() { + mRingerIcon.setPressed(true); + mRingerIcon.postOnAnimationDelayed(mSingleUnpress, 200); + } + }; + + private Runnable mSingleUnpress = new Runnable() { + @Override + public void run() { + mRingerIcon.setPressed(false); + } + }; + private final VolumeDialogController.Callbacks mControllerCallbackH = new VolumeDialogController.Callbacks() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java index 173400f5e31af..434327cd491c8 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java @@ -43,7 +43,7 @@ public class VolumePrefs { public static final String PREF_ADJUST_ALARMS = "pref_adjust_alarms"; public static final String PREF_ADJUST_NOTIFICATION = "pref_adjust_notification"; - public static final int SHOW_RINGER_TOAST_COUNT = 9; + public static final int SHOW_RINGER_TOAST_COUNT = 12; public static final boolean DEFAULT_SHOW_HEADERS = true; public static final boolean DEFAULT_ENABLE_AUTOMUTE = true;