Move and reshape volume dialog
- Move the volume dialog back to the top
- curve some corners
- Add a ringer toggle row
Bug: 63096355
Test: manual
Change-Id: Ife8a3fa4fabf3a0f26ddca3cf05b16425b325ef3
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index fac254a..f563794 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.systemui.HardwareUiLayout
+<com.android.systemui.volume.VolumeUiLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -21,18 +21,19 @@
android:clipChildren="false" >
<RelativeLayout
android:id="@+id/volume_dialog"
- android:layout_width="@dimen/volume_dialog_panel_width"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
android:paddingTop="@dimen/volume_row_padding_bottom"
- android:layout_margin="12dp"
- android:background="?android:attr/actionBarItemBackground"
- android:translationZ="8dp" >
+ android:background="@drawable/rounded_full_bg_bottom"
+ android:translationZ="8dp"
+ android:clipChildren="false" >
<LinearLayout
android:id="@+id/volume_dialog_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:orientation="vertical" >
<LinearLayout
@@ -42,7 +43,55 @@
android:orientation="vertical" >
<!-- volume rows added and removed here! :-) -->
</LinearLayout>
- </LinearLayout>
+ <!-- special row for ringer mode -->
+ <RelativeLayout
+ android:id="@+id/ringer_mode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/rounded_bg_full"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_margin="10dp">
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/ringer_icon"
+ style="@style/VolumeButtons"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="@dimen/volume_button_size"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:soundEffectsEnabled="false" />
+
+ <TextView
+ android:id="@+id/ringer_title"
+ android:text="@string/stream_ring"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@+id/ringer_icon"
+ android:layout_marginStart="64dp"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:paddingStart="@dimen/volume_row_header_padding_start" />
+
+ <TextView
+ android:id="@+id/ringer_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:layout_marginEnd="14dp"
+ android:maxLines="1"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ </RelativeLayout>
+ </LinearLayout>
</RelativeLayout>
-</com.android.systemui.HardwareUiLayout>
+</com.android.systemui.volume.VolumeUiLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ca85445..d8dafb3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1248,6 +1248,10 @@
<string name="stream_tts" translatable="false">Transmitted Through Speaker</string> <!-- STREAM_TTS -->
<string name="stream_accessibility">Accessibility</string> <!-- STREAM_ACCESSIBILITY -->
+ <string name="volume_ringer_status_normal">Ring</string>
+ <string name="volume_ringer_status_vibrate">Vibrate</string>
+ <string name="volume_ringer_status_silent">Mute</string>
+
<string name="volume_stream_muted" translatable="false">%s silent</string>
<string name="volume_stream_vibrate" translatable="false">%s vibrate</string>
<string name="volume_stream_suppressed" translatable="false">%1$s silent — %2$s</string>
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 0d41e20..3e70980 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -30,7 +30,6 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.ColorStateList;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
@@ -67,8 +66,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
-import com.android.systemui.HardwareUiLayout;
-import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
@@ -97,11 +94,12 @@
private final VolumeDialogController mController;
private Window mWindow;
- private HardwareUiLayout mHardwareLayout;
private CustomDialog mDialog;
private ViewGroup mDialogView;
private ViewGroup mDialogRowsView;
private ViewGroup mDialogContentView;
+ private ImageButton mRingerIcon;
+ private TextView mRingerStatus;
private final List<VolumeRow> mRows = new ArrayList<>();
private ConfigurableTexts mConfigurableTexts;
private final SparseBooleanArray mDynamic = new SparseBooleanArray();
@@ -111,6 +109,7 @@
private final Accessibility mAccessibility = new Accessibility();
private final ColorStateList mActiveSliderTint;
private final ColorStateList mInactiveSliderTint;
+ private VolumeUiLayout mHardwareLayout;
private boolean mShowing;
private boolean mShowA11yStream;
@@ -160,17 +159,34 @@
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
- mWindow.addFlags(
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ mWindow.setTitle(VolumeDialogImpl.class.getSimpleName());
mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
mDialog.setContentView(R.layout.volume_dialog);
- mDialogView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog);
+ mDialog.setOnShowListener(new DialogInterface.OnShowListener() {
+ @Override
+ public void onShow(DialogInterface dialog) {
+ mDialogView.setTranslationY(-mDialogView.getHeight());
+ mDialogView.setAlpha(0);
+ mDialogView.animate()
+ .alpha(1)
+ .translationY(0)
+ .setDuration(300)
+ .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
+ .withEndAction(() -> {
+ mWindow.getDecorView().requestAccessibilityFocus();
+ })
+ .start();
+ }
+ });
+ mDialogView = mDialog.findViewById(R.id.volume_dialog);
mDialogView.setOnHoverListener(new View.OnHoverListener() {
@Override
public boolean onHover(View v, MotionEvent event) {
@@ -181,18 +197,20 @@
return true;
}
});
- mHardwareLayout = HardwareUiLayout.get(mDialogView);
+ mHardwareLayout = VolumeUiLayout.get(mDialogView);
mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
mDialogContentView = mDialog.findViewById(R.id.volume_dialog_content);
mDialogRowsView = mDialogContentView.findViewById(R.id.volume_dialog_rows);
+ mRingerIcon = mDialogContentView.findViewById(R.id.ringer_icon);
+ mRingerStatus = mDialogContentView.findViewById(R.id.ringer_status);
if (mRows.isEmpty()) {
addRow(AudioManager.STREAM_MUSIC,
R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true);
if (!AudioSystem.isSingleVolume(mContext)) {
addRow(AudioManager.STREAM_RING,
- R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true);
+ R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, false);
addRow(AudioManager.STREAM_ALARM,
R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, false);
addRow(AudioManager.STREAM_VOICE_CALL,
@@ -208,6 +226,7 @@
addExistingRows();
}
updateRowsH(getActiveRow());
+ initRingerH();
}
private ColorStateList loadColorStateList(int colorResId) {
@@ -374,6 +393,30 @@
}
}
+ public void initRingerH() {
+ mRingerIcon.setOnClickListener(v -> {
+ Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, AudioManager.STREAM_RING,
+ mRingerIcon.getTag());
+ final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
+ final boolean hasVibrator = mController.hasVibrator();
+ if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
+ if (hasVibrator) {
+ mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
+ } else {
+ final boolean wasZero = ss.level == 0;
+ mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0);
+ }
+ } else {
+ mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
+ if (ss.level == 0) {
+ mController.setStreamVolume(AudioManager.STREAM_RING, 1);
+ }
+ }
+ updateRingerH();
+ });
+ updateRingerH();
+ }
+
public void show(int reason) {
mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
}
@@ -389,27 +432,12 @@
rescheduleTimeoutH();
if (mShowing) return;
mShowing = true;
- mHardwareLayout.setTranslationX(getAnimTranslation());
- mHardwareLayout.setAlpha(0);
- mHardwareLayout.animate()
- .alpha(1)
- .translationX(0)
- .setDuration(300)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .withEndAction(() -> {
- mDialog.show();
- mWindow.getDecorView().requestAccessibilityFocus();
- })
- .start();
+
+ mDialog.show();
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
mController.notifyVisible(true);
}
- private float getAnimTranslation() {
- return mContext.getResources().getDimension(
- R.dimen.volume_dialog_panel_width) / 2;
- }
-
protected void rescheduleTimeoutH() {
mHandler.removeMessages(H.DISMISS);
final int timeout = computeTimeoutH();
@@ -423,7 +451,6 @@
if (mAccessibility.mFeedbackEnabled) return 20000;
if (mHovering) return 16000;
if (mSafetyWarning != null) return 5000;
- if (mActiveStream == AudioManager.STREAM_MUSIC) return 1500;
return 3000;
}
@@ -431,16 +458,22 @@
mHandler.removeMessages(H.DISMISS);
mHandler.removeMessages(H.SHOW);
if (!mShowing) return;
+ mDialogView.animate().cancel();
mShowing = false;
- mHardwareLayout.setTranslationX(0);
- mHardwareLayout.setAlpha(1);
- mHardwareLayout.animate()
+
+ mDialogView.setTranslationY(0);
+ mDialogView.setAlpha(1);
+ mDialogView.animate()
.alpha(0)
- .translationX(getAnimTranslation())
- .setDuration(300)
- .withEndAction(() -> mDialog.dismiss())
+ .translationY(-mDialogView.getHeight())
+ .setDuration(250)
.setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
+ .withEndAction(() -> mHandler.postDelayed(() -> {
+ if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
+ mDialog.dismiss();
+ }, 50))
.start();
+
if (mAccessibilityMgr.isEnabled()) {
AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -493,6 +526,53 @@
}
}
+ protected void updateRingerH() {
+ if (mState != null) {
+ final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
+ switch (mState.ringerModeInternal) {
+ case AudioManager.RINGER_MODE_VIBRATE:
+ mRingerStatus.setText(R.string.volume_ringer_status_vibrate);
+ mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
+ mRingerIcon.setTag(Events.ICON_STATE_VIBRATE);
+ break;
+ case AudioManager.RINGER_MODE_SILENT:
+ mRingerStatus.setText(R.string.volume_ringer_status_silent);
+ mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+ mRingerIcon.setContentDescription(mContext.getString(
+ R.string.volume_stream_content_description_unmute,
+ getStreamLabelH(ss)));
+ mRingerIcon.setTag(Events.ICON_STATE_MUTE);
+ break;
+ case AudioManager.RINGER_MODE_NORMAL:
+ default:
+ boolean muted = (mAutomute && ss.level == 0) || ss.muted ? true : false;
+ if (muted) {
+ mRingerStatus.setText(R.string.volume_ringer_status_silent);
+ mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+ mRingerIcon.setContentDescription(mContext.getString(
+ R.string.volume_stream_content_description_unmute,
+ getStreamLabelH(ss)));
+ mRingerIcon.setTag(Events.ICON_STATE_MUTE);
+ } else {
+ mRingerStatus.setText(R.string.volume_ringer_status_normal);
+ mRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
+ if (mController.hasVibrator()) {
+ mRingerIcon.setContentDescription(mContext.getString(
+ mShowA11yStream
+ ? R.string.volume_stream_content_description_vibrate_a11y
+ : R.string.volume_stream_content_description_vibrate,
+ getStreamLabelH(ss)));
+
+ } else {
+ mRingerIcon.setContentDescription(getStreamLabelH(ss));
+ }
+ mRingerIcon.setTag(Events.ICON_STATE_UNMUTE);
+ }
+ break;
+ }
+ }
+ }
+
private void trimObsoleteH() {
if (D.BUG) Log.d(TAG, "trimObsoleteH");
for (int i = mRows.size() - 1; i >= 0; i--) {
@@ -529,6 +609,7 @@
for (VolumeRow row : mRows) {
updateVolumeRowH(row);
}
+ updateRingerH();
}
private void updateVolumeRowH(VolumeRow row) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
new file mode 100644
index 0000000..49ac9b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
+
+public class VolumeUiLayout extends FrameLayout {
+
+ public VolumeUiLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener);
+ }
+
+ @Override
+ public ViewOutlineProvider getOutlineProvider() {
+ return super.getOutlineProvider();
+ }
+
+ public void setOutsideTouchListener(OnClickListener onClickListener) {
+ requestLayout();
+ setOnClickListener(onClickListener);
+ setClickable(true);
+ setFocusable(true);
+ }
+
+ public static VolumeUiLayout get(View v) {
+ if (v instanceof VolumeUiLayout) return (VolumeUiLayout) v;
+ if (v.getParent() instanceof View) {
+ return get((View) v.getParent());
+ }
+ return null;
+ }
+
+ private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> {
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ };
+}