Volume dialog: Add zen mode icons and notification access.

- Add icon above text to all three zen mode states, update text style.
- Remove zentoast.
- Update shared borderless rect background, masks now support shapes.
- Update size of volume stream icons.
- Ensure all volume icons are expressions of white.
- Make volume icons testable via new demo mode command.
- Add a divider + secondary icon to access the notification slider.
- Animate the transition when accessing notification slider.

Bug: 18206097
Bug: 16303068
Bug: 18102850
Change-Id: I5eb6f820dc317e89be272cc78f6c80ed969ad5e9
This commit is contained in:
John Spurlock
2014-11-08 12:40:19 -05:00
parent aed8e76172
commit bb4a702e6f
34 changed files with 318 additions and 345 deletions

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2014 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.
-->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/decelerate_quad"
android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@integer/zen_toast_animation_duration" />

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2014 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.
-->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/accelerate_quad"
android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="@integer/zen_toast_animation_duration" />

View File

@@ -17,7 +17,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="@android:color/white"/>
<item android:color="@color/segmented_button_text_inactive"/>
<item android:state_selected="true" android:color="@color/segmented_button_selected"/>
<item android:color="@color/segmented_button_unselected"/>
</selector>

View File

@@ -16,6 +16,10 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask"
android:drawable="@android:color/white" />
<item android:id="@android:id/mask">
<shape>
<corners android:radius="@dimen/borderless_button_radius" />
<solid android:color="@android:color/white" />
</shape>
</item>
</ripple>

View File

@@ -14,8 +14,8 @@ Copyright (C) 2014 The Android Open Source Project
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32.0dp"
android:height="32.0dp"
android:width="28.0dp"
android:height="28.0dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path

View File

@@ -14,8 +14,8 @@ Copyright (C) 2014 The Android Open Source Project
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32.0dp"
android:height="32.0dp"
android:width="28.0dp"
android:height="28.0dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2014 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M35.4,15.4L24.0,4.0l-2.0,0.0l0.0,15.2L12.8,10.0L10.0,12.8L21.2,24.0L10.0,35.2l2.8,2.8l9.2,-9.2L22.0,44.0l2.0,0.0l11.4,-11.4L26.8,24.0L35.4,15.4zM26.0,11.7l3.8,3.8L26.0,19.2L26.0,11.7zM29.8,32.6L26.0,36.3l0.0,-7.5L29.8,32.6z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2014 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M26.0,11.8l3.8,3.8l-3.2,3.2l2.8,2.8l6.0,-6.0L24.0,4.2l-2.0,0.0l0.0,10.1l4.0,4.0L26.0,11.8zM10.8,8.2L8.0,11.0l13.2,13.2L10.0,35.3l2.8,2.8L22.0,29.0l0.0,15.2l2.0,0.0l8.6,-8.6l4.6,4.6l2.8,-2.8L10.8,8.2zM26.0,36.5L26.0,29.0l3.8,3.8L26.0,36.5z"/>
</vector>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright 2014, 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.
*/
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@*android:drawable/ic_audio_phone_am_alpha"
android:autoMirrored="true"
android:tint="#ffffffff" />

View File

@@ -14,8 +14,8 @@ Copyright (C) 2014 The Android Open Source Project
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32.0dp"
android:height="32.0dp"
android:width="28.0dp"
android:height="28.0dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path

View File

@@ -14,8 +14,8 @@ Copyright (C) 2014 The Android Open Source Project
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32.0dp"
android:height="32.0dp"
android:width="28.0dp"
android:height="28.0dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path

View File

@@ -14,8 +14,8 @@ Copyright (C) 2014 The Android Open Source Project
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:width="28dp"
android:height="28dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">

View File

@@ -14,8 +14,8 @@ Copyright (C) 2014 The Android Open Source Project
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:width="28dp"
android:height="28dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path

View File

@@ -14,8 +14,8 @@ Copyright (C) 2014 The Android Open Source Project
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:width="28dp"
android:height="28dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">

View File

@@ -0,0 +1,24 @@
<!--
Copyright (C) 2014 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6.6,3.6L5.2,2.2C2.8,4.0 1.2,6.8 1.0,10.0l2.0,0.0C3.2,7.3 4.5,5.0 6.6,3.6zM20.0,10.0l2.0,0.0c-0.2,-3.2 -1.7,-6.0 -4.1,-7.8l-1.4,1.4C18.5,5.0 19.8,7.3 20.0,10.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0l-2.0,-2.0L18.0,10.5zM11.5,22.0c0.1,0.0 0.3,0.0 0.4,0.0c0.7,-0.1 1.2,-0.6 1.4,-1.2c0.1,-0.2 0.2,-0.5 0.2,-0.8l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0z"/>
</vector>

View File

@@ -14,8 +14,8 @@ Copyright (C) 2014 The Android Open Source Project
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:width="18dp"
android:height="18dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">

View File

@@ -14,8 +14,8 @@ Copyright (C) 2014 The Android Open Source Project
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:width="18dp"
android:height="18dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/system_primary_color" />
<corners android:radius="@dimen/notification_material_rounded_rect_radius" />
</shape>

View File

@@ -19,8 +19,10 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/segmented_button_spacing"
android:layout_weight="1"
android:gravity="center_horizontal|top"
android:textColor="@color/segmented_button_text_selector"
android:background="@drawable/btn_borderless_rect"
android:textAppearance="@style/TextAppearance.QS.SegmentedButton"
android:minHeight="36dp"
android:padding="4dp" />
android:minHeight="64dp"
android:paddingTop="11dp"
android:drawablePadding="6dp" />

View File

@@ -55,4 +55,20 @@
android:paddingTop="0dp" />
</FrameLayout>
<View
android:id="@+id/divider"
android:layout_width="1dp"
android:layout_height="32dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="@color/volume_panel_divider" />
<ImageView
android:id="@+id/secondary_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="center"
android:background="@drawable/btn_borderless_rect"
android:contentDescription="@null" />
</LinearLayout>

View File

@@ -25,7 +25,7 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="12dp"
android:minHeight="8dp"
android:elevation="4dp"
android:background="@drawable/qs_background_secondary" >
@@ -33,9 +33,8 @@
android:id="@+id/zen_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/qs_panel_padding"
android:layout_marginRight="@dimen/qs_panel_padding"
android:layout_marginTop="4dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:clipChildren="false" />
</FrameLayout>

View File

@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2014 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/zen_toast_background"
android:translationZ="@dimen/volume_panel_z"
android:padding="18dp"
android:gravity="center_horizontal"
android:orientation="vertical" >
<ImageView
android:id="@android:id/icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:scaleType="center" />
<TextView
android:id="@android:id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:lineSpacingExtra="4dp"
android:gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.QS.ZenToast" />
</LinearLayout>

View File

@@ -101,8 +101,6 @@
<!-- The color of the circle around the primary user in the user switcher -->
<color name="current_user_border_color">@color/system_accent_color</color>
<color name="segmented_button_text_inactive">#99afbdc4</color><!-- 60% -->
<!-- The "inside" of a notification, reached via longpress -->
<color name="notification_guts_bg_color">@color/system_secondary_color</color>
<color name="notification_guts_title_color">#FFFFFFFF</color>
@@ -124,9 +122,11 @@
<!-- Shadow color for the furthest pixels around the fake shadow for recents. -->
<color name="fake_shadow_end_color">#03000000</color>
<!-- 25% deep teal 200 -->
<color name="screen_pinning_nav_icon_highlight_outer">#4080cbc4</color>
<!-- deep teal 500 -->
<color name="screen_pinning_request_bg">#ff009688</color>
<color name="screen_pinning_nav_icon_highlight_outer">#4080cbc4</color><!-- 25% deep teal 200 -->
<color name="screen_pinning_request_bg">#ff009688</color><!-- deep teal 500 -->
<color name="screen_pinning_request_window_bg">#80000000</color>
<color name="segmented_button_selected">#FFFFFFFF</color>
<color name="segmented_button_unselected">#B3B0BEC5</color><!-- 70% blue grey 200 -->
<color name="volume_panel_divider">#1FFFFFFF</color><!-- 12% white -->
</resources>

View File

@@ -250,12 +250,6 @@
<!-- Number of times to show the strong alarm warning text in the volume dialog -->
<integer name="zen_mode_alarm_warning_threshold">5</integer>
<!-- Zen toast fade in/out duration -->
<integer name="zen_toast_animation_duration">500</integer>
<!-- Zen toast visibility duration -->
<integer name="zen_toast_visible_duration">500</integer>
<!-- Enable the default volume dialog -->
<bool name="enable_volume_ui">true</bool>
</resources>

View File

@@ -191,17 +191,14 @@
<dimen name="qs_data_usage_text_size">14sp</dimen>
<dimen name="qs_data_usage_usage_text_size">36sp</dimen>
<dimen name="segmented_button_spacing">4dp</dimen>
<dimen name="segmented_button_radius">2dp</dimen>
<dimen name="segmented_button_spacing">8dp</dimen>
<dimen name="borderless_button_radius">2dp</dimen>
<!-- How far the expanded QS panel peeks from the header in collapsed state. -->
<dimen name="qs_peek_height">8dp</dimen>
<dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
<!-- Explicit width of the zen toast window -->
<dimen name="zen_toast_width">224dp</dimen>
<!-- used by DessertCase -->
<dimen name="dessert_case_cell_size">192dp</dimen>

View File

@@ -167,14 +167,8 @@
<item name="android:textColor">@color/qs_subhead</item>
</style>
<style name="TextAppearance.QS.ZenToast">
<item name="android:textSize">14sp</item>
</style>
<style name="TextAppearance.QS.SegmentedButton">
<item name="android:textSize">14sp</item>
<item name="android:textAllCaps">true</item>
<item name="android:fontFamily">sans-serif-medium</item>
</style>
<style name="TextAppearance.QS.DataUsage">
@@ -266,9 +260,4 @@
<style name="UserDetailView">
<item name="numColumns">3</item>
</style>
<style name="ZenToastAnimations">
<item name="android:windowEnterAnimation">@anim/zen_toast_enter</item>
<item name="android:windowExitAnimation">@anim/zen_toast_exit</item>
</style>
</resources>

View File

@@ -32,4 +32,5 @@ public interface DemoMode {
public static final String COMMAND_BARS = "bars";
public static final String COMMAND_STATUS = "status";
public static final String COMMAND_NOTIFICATIONS = "notifications";
public static final String COMMAND_VOLUME = "volume";
}

View File

@@ -3444,6 +3444,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
dispatchDemoCommand(COMMAND_ENTER, new Bundle());
}
boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT);
if ((modeChange || command.equals(COMMAND_VOLUME)) && mVolumeComponent != null) {
mVolumeComponent.dispatchDemoCommand(command, args);
}
if (modeChange || command.equals(COMMAND_CLOCK)) {
dispatchDemoCommandToView(command, args, R.id.clock);
}

View File

@@ -17,7 +17,6 @@
package com.android.systemui.volume;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -30,8 +29,6 @@ import com.android.systemui.R;
import java.util.Objects;
public class SegmentedButtons extends LinearLayout {
private static final Typeface MEDIUM = Typeface.create("sans-serif-medium", Typeface.NORMAL);
private static final Typeface BOLD = Typeface.create("sans-serif", Typeface.BOLD);
private static final int LABEL_RES_KEY = R.id.label;
private final Context mContext;
@@ -63,15 +60,17 @@ public class SegmentedButtons extends LinearLayout {
final Object tag = c.getTag();
final boolean selected = Objects.equals(mSelectedValue, tag);
c.setSelected(selected);
c.setTypeface(selected ? BOLD : MEDIUM);
c.getCompoundDrawables()[1].setTint(mContext.getResources().getColor(selected
? R.color.segmented_button_selected : R.color.segmented_button_unselected));
}
fireOnSelected();
}
public void addButton(int labelResId, Object value) {
public void addButton(int labelResId, int iconResId, Object value) {
final Button b = (Button) mInflater.inflate(R.layout.segmented_button, this, false);
b.setTag(LABEL_RES_KEY, labelResId);
b.setText(labelResId);
b.setCompoundDrawablesWithIntrinsicBounds(0, iconResId, 0, 0);
final LayoutParams lp = (LayoutParams) b.getLayoutParams();
if (getChildCount() == 0) {
lp.leftMargin = lp.rightMargin = 0; // first button has no margin

View File

@@ -16,8 +16,9 @@
package com.android.systemui.volume;
import com.android.systemui.DemoMode;
import com.android.systemui.statusbar.policy.ZenModeController;
public interface VolumeComponent {
public interface VolumeComponent extends DemoMode {
ZenModeController getZenController();
}

View File

@@ -16,6 +16,9 @@
package com.android.systemui.volume;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
@@ -42,6 +45,8 @@ import android.media.VolumeProvider;
import android.media.session.MediaController;
import android.media.session.MediaController.PlaybackInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
@@ -59,12 +64,15 @@ import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import com.android.internal.R;
import com.android.systemui.DemoMode;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -76,7 +84,7 @@ import java.io.PrintWriter;
*
* @hide
*/
public class VolumePanel extends Handler {
public class VolumePanel extends Handler implements DemoMode {
private static final String TAG = "VolumePanel";
private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
@@ -129,6 +137,8 @@ public class VolumePanel extends Handler {
private static final int IC_AUDIO_VOL = com.android.systemui.R.drawable.ic_audio_vol;
private static final int IC_AUDIO_VOL_MUTE = com.android.systemui.R.drawable.ic_audio_vol_mute;
private static final int IC_AUDIO_BT = com.android.systemui.R.drawable.ic_audio_bt;
private static final int IC_AUDIO_BT_MUTE = com.android.systemui.R.drawable.ic_audio_bt_mute;
private final String mTag;
protected final Context mContext;
@@ -142,6 +152,7 @@ public class VolumePanel extends Handler {
private float mDisabledAlpha;
private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
private int mLastRingerProgress = 0;
private int mDemoIcon;
// True if we want to play tones on the system stream when the master stream is specified.
private final boolean mPlayMasterStreamTones;
@@ -166,12 +177,13 @@ public class VolumePanel extends Handler {
/** All the slider controls mapped by stream type */
private SparseArray<StreamControl> mStreamControls;
private final AccessibilityManager mAccessibilityManager;
private final SecondaryIconTransition mSecondaryIconTransition;
private enum StreamResources {
BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
R.string.volume_icon_description_bluetooth,
R.drawable.ic_audio_bt,
R.drawable.ic_audio_bt,
IC_AUDIO_BT,
IC_AUDIO_BT_MUTE,
false),
RingerStream(AudioManager.STREAM_RING,
R.string.volume_icon_description_ringer,
@@ -180,8 +192,8 @@ public class VolumePanel extends Handler {
false),
VoiceStream(AudioManager.STREAM_VOICE_CALL,
R.string.volume_icon_description_incall,
R.drawable.ic_audio_phone,
R.drawable.ic_audio_phone,
com.android.systemui.R.drawable.ic_audio_phone,
com.android.systemui.R.drawable.ic_audio_phone,
false),
AlarmStream(AudioManager.STREAM_ALARM,
R.string.volume_alarm,
@@ -246,6 +258,8 @@ public class VolumePanel extends Handler {
ImageView icon;
SeekBar seekbarView;
TextView suppressorView;
View divider;
ImageView secondaryIcon;
int iconRes;
int iconMuteRes;
int iconSuppressedRes;
@@ -339,6 +353,7 @@ public class VolumePanel extends Handler {
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mAccessibilityManager = (AccessibilityManager) context.getSystemService(
Context.ACCESSIBILITY_SERVICE);
mSecondaryIconTransition = new SecondaryIconTransition();
// For now, only show master volume if master volume is supported
final Resources res = context.getResources();
@@ -381,6 +396,8 @@ public class VolumePanel extends Handler {
mActiveStreamType = -1;
mAudioManager.forceVolumeControlStream(mActiveStreamType);
setZenPanelVisible(false);
mDemoIcon = 0;
mSecondaryIconTransition.cancel();
}
});
@@ -604,10 +621,12 @@ public class VolumePanel extends Handler {
mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
final StreamResources notificationStream = StreamResources.NotificationStream;
for (int i = 0; i < STREAMS.length; i++) {
StreamResources streamRes = STREAMS[i];
final int streamType = streamRes.streamType;
final boolean isNotification = isNotificationOrRing(streamType);
final StreamControl sc = new StreamControl();
sc.streamType = streamType;
@@ -620,8 +639,8 @@ public class VolumePanel extends Handler {
sc.iconRes = streamRes.iconRes;
sc.iconMuteRes = streamRes.iconMuteRes;
sc.icon.setImageResource(sc.iconRes);
sc.icon.setClickable(isNotificationOrRing(streamType));
if (sc.icon.isClickable()) {
sc.icon.setClickable(isNotification);
if (isNotification) {
sc.icon.setSoundEffectsEnabled(false);
sc.icon.setOnClickListener(new OnClickListener() {
@Override
@@ -636,6 +655,23 @@ public class VolumePanel extends Handler {
sc.suppressorView =
(TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
sc.suppressorView.setVisibility(View.GONE);
final boolean showSecondary = !isNotification && notificationStream.show;
sc.divider = sc.group.findViewById(com.android.systemui.R.id.divider);
sc.secondaryIcon = (ImageView) sc.group
.findViewById(com.android.systemui.R.id.secondary_icon);
sc.secondaryIcon.setImageResource(com.android.systemui.R.drawable.ic_ringer_audible);
sc.secondaryIcon.setContentDescription(res.getString(notificationStream.descRes));
sc.secondaryIcon.setClickable(showSecondary);
sc.divider.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
sc.secondaryIcon.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
if (showSecondary) {
sc.secondaryIcon.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mSecondaryIconTransition.start(sc);
}
});
}
final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
@@ -696,7 +732,7 @@ public class VolumePanel extends Handler {
}
muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
}
sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
sc.icon.setImageResource(mDemoIcon != 0 ? mDemoIcon : muted ? sc.iconMuteRes : sc.iconRes);
}
private void updateSliderSupressor(StreamControl sc) {
@@ -800,7 +836,8 @@ public class VolumePanel extends Handler {
}
private void updateTimeoutDelay() {
mTimeoutDelay = sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
mTimeoutDelay = mDemoIcon != 0 ? TIMEOUT_DELAY_EXPANDED
: sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
: mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
: mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
: isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
@@ -995,7 +1032,7 @@ public class VolumePanel extends Handler {
(AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
setMusicIcon(IC_AUDIO_BT, IC_AUDIO_BT_MUTE);
} else {
setMusicIcon(IC_AUDIO_VOL, IC_AUDIO_VOL_MUTE);
}
@@ -1075,6 +1112,12 @@ public class VolumePanel extends Handler {
updateSliderProgress(sc, index);
updateSliderEnabled(sc, isMuted(streamType),
(flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
// check for secondary-icon transition completion
if (isNotificationOrRing(streamType) && mSecondaryIconTransition.isRunning()) {
mSecondaryIconTransition.cancel(); // safe to reset
sc.seekbarView.setAlpha(0); sc.seekbarView.animate().alpha(1);
mZenPanel.setAlpha(0); mZenPanel.animate().alpha(1);
}
}
if (!isShowing()) {
@@ -1406,6 +1449,22 @@ public class VolumePanel extends Handler {
return mZenController;
}
@Override
public void dispatchDemoCommand(String command, Bundle args) {
if (!COMMAND_VOLUME.equals(command)) return;
String icon = args.getString("icon");
final String iconMute = args.getString("iconmute");
final boolean mute = iconMute != null;
icon = mute ? iconMute : icon;
icon = icon.endsWith("Stream") ? icon : (icon + "Stream");
final StreamResources sr = StreamResources.valueOf(icon);
mDemoIcon = mute ? sr.iconMuteRes : sr.iconRes;
final int forcedStreamType = StreamResources.MediaStream.streamType;
mAudioManager.forceVolumeControlStream(forcedStreamType);
mAudioManager.adjustStreamVolume(forcedStreamType, AudioManager.ADJUST_SAME,
AudioManager.FLAG_SHOW_UI);
}
private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
@@ -1445,6 +1504,80 @@ public class VolumePanel extends Handler {
}
};
private final class SecondaryIconTransition extends AnimatorListenerAdapter
implements Runnable {
private static final int ANIMATION_TIME = 400;
private static final int WAIT_FOR_SWITCH_TIME = 1000;
private final int mAnimationTime = (int)(ANIMATION_TIME * ValueAnimator.getDurationScale());
private final int mFadeOutTime = mAnimationTime / 2;
private final int mDelayTime = mAnimationTime / 3;
private final Interpolator mIconInterpolator =
AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
private StreamControl mTarget;
public void start(StreamControl sc) {
if (sc == null) throw new IllegalArgumentException();
if (mTarget != null) {
cancel();
}
mTarget = sc;
mTimeoutDelay = mAnimationTime + WAIT_FOR_SWITCH_TIME;
resetTimeout();
mTarget.secondaryIcon.setClickable(false);
final int N = mTarget.group.getChildCount();
for (int i = 0; i < N; i++) {
final View child = mTarget.group.getChildAt(i);
if (child != mTarget.secondaryIcon) {
child.animate().alpha(0).setDuration(mFadeOutTime).start();
}
}
mTarget.secondaryIcon.animate()
.translationXBy(mTarget.icon.getX() - mTarget.secondaryIcon.getX())
.setInterpolator(mIconInterpolator)
.setStartDelay(mDelayTime)
.setDuration(mAnimationTime - mDelayTime)
.setListener(this)
.start();
}
public boolean isRunning() {
return mTarget != null;
}
public void cancel() {
if (mTarget == null) return;
mTarget.secondaryIcon.setClickable(true);
final int N = mTarget.group.getChildCount();
for (int i = 0; i < N; i++) {
final View child = mTarget.group.getChildAt(i);
if (child != mTarget.secondaryIcon) {
child.animate().cancel();
child.setAlpha(1);
}
}
mTarget.secondaryIcon.animate().cancel();
mTarget.secondaryIcon.setTranslationX(0);
mTarget = null;
}
@Override
public void onAnimationEnd(Animator animation) {
if (mTarget == null) return;
AsyncTask.execute(this);
}
@Override
public void run() {
if (mTarget == null) return;
mAudioManager.forceVolumeControlStream(StreamResources.NotificationStream.streamType);
mAudioManager.adjustStreamVolume(StreamResources.NotificationStream.streamType,
AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
}
}
public interface Callback {
void onZenSettings();
void onInteraction();

View File

@@ -10,6 +10,7 @@ import android.media.session.ISessionController;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.provider.Settings;
@@ -184,6 +185,11 @@ public class VolumeUI extends SystemUI {
public ZenModeController getZenController() {
return mPanel.getZenController();
}
@Override
public void dispatchDemoCommand(String command, Bundle args) {
mPanel.dispatchDemoCommand(command, args);
}
}
private final class RemoteVolumeController extends IRemoteVolumeController.Stub {

View File

@@ -80,7 +80,6 @@ public class ZenModePanel extends LinearLayout {
private final Interpolator mFastOutSlowInInterpolator;
private final int mSubheadWarningColor;
private final int mSubheadColor;
private final ZenToast mZenToast;
private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));
@@ -115,7 +114,6 @@ public class ZenModePanel extends LinearLayout {
final Resources res = mContext.getResources();
mSubheadWarningColor = res.getColor(R.color.system_warning_color);
mSubheadColor = res.getColor(R.color.qs_subhead);
mZenToast = new ZenToast(mContext);
if (DEBUG) Log.d(mTag, "new ZenModePanel");
}
@@ -124,10 +122,12 @@ public class ZenModePanel extends LinearLayout {
super.onFinishInflate();
mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
mZenButtons.addButton(R.string.interruption_level_none, Global.ZEN_MODE_NO_INTERRUPTIONS);
mZenButtons.addButton(R.string.interruption_level_priority,
mZenButtons.addButton(R.string.interruption_level_none, R.drawable.ic_zen_none,
Global.ZEN_MODE_NO_INTERRUPTIONS);
mZenButtons.addButton(R.string.interruption_level_priority, R.drawable.ic_zen_important,
Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
mZenButtons.addButton(R.string.interruption_level_all, Global.ZEN_MODE_OFF);
mZenButtons.addButton(R.string.interruption_level_all, R.drawable.ic_zen_all,
Global.ZEN_MODE_OFF);
mZenButtons.setCallback(mZenButtonsCallback);
mZenSubhead = findViewById(R.id.zen_subhead);
@@ -160,7 +160,6 @@ public class ZenModePanel extends LinearLayout {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (DEBUG) Log.d(mTag, "onAttachedToWindow");
mZenToast.hide();
mAttachedZen = getSelectedZen(-1);
mSessionZen = mAttachedZen;
mSessionExitCondition = copy(mExitCondition);
@@ -193,10 +192,6 @@ public class ZenModePanel extends LinearLayout {
if (selectedZen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
mPrefs.trackNoneSelected();
}
if (selectedZen == Global.ZEN_MODE_NO_INTERRUPTIONS
|| selectedZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
mZenToast.show(selectedZen);
}
}
}

View File

@@ -1,163 +0,0 @@
/*
* Copyright (C) 2014 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 static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.systemui.R;
public class ZenToast {
private static final String ACTION_SHOW = ZenToast.class.getName() + ".SHOW";
private static final String ACTION_HIDE = ZenToast.class.getName() + ".HIDE";
private static final String EXTRA_ZEN = "zen";
private static final String EXTRA_TEXT = "text";
private static final int MSG_SHOW = 1;
private static final int MSG_HIDE = 2;
private final Context mContext;
private final WindowManager mWindowManager;
private View mZenToast;
public ZenToast(Context context) {
mContext = context;
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_SHOW);
filter.addAction(ACTION_HIDE);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
}
public void show(int zen) {
mHandler.removeMessages(MSG_HIDE);
mHandler.removeMessages(MSG_SHOW);
mHandler.obtainMessage(MSG_SHOW, zen, 0).sendToTarget();
}
public void hide() {
mHandler.removeMessages(MSG_HIDE);
mHandler.removeMessages(MSG_SHOW);
mHandler.obtainMessage(MSG_HIDE).sendToTarget();
}
private void handleShow(int zen, String overrideText) {
handleHide();
String text;
final int iconRes;
switch (zen) {
case ZEN_MODE_NO_INTERRUPTIONS:
text = mContext.getString(R.string.zen_no_interruptions);
iconRes = R.drawable.ic_zen_none;
break;
case ZEN_MODE_IMPORTANT_INTERRUPTIONS:
text = mContext.getString(R.string.zen_important_interruptions);
iconRes = R.drawable.ic_zen_important;
break;
default:
return;
}
if (overrideText != null) {
text = overrideText;
}
final Resources res = mContext.getResources();
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = res.getDimensionPixelSize(R.dimen.zen_toast_width);
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = R.style.ZenToastAnimations;
params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
params.setTitle(getClass().getSimpleName());
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
params.gravity = Gravity.CENTER;
params.packageName = mContext.getPackageName();
mZenToast = LayoutInflater.from(mContext).inflate(R.layout.zen_toast, null);
final TextView message = (TextView) mZenToast.findViewById(android.R.id.message);
message.setText(text);
final ImageView icon = (ImageView) mZenToast.findViewById(android.R.id.icon);
icon.setImageResource(iconRes);
mZenToast.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
@Override
public void onViewDetachedFromWindow(View v) {
// noop
}
@Override
public void onViewAttachedToWindow(View v) {
mZenToast.announceForAccessibility(message.getText());
}
});
mWindowManager.addView(mZenToast, params);
final int animDuration = res.getInteger(R.integer.zen_toast_animation_duration);
final int visibleDuration = res.getInteger(R.integer.zen_toast_visible_duration);
mHandler.sendEmptyMessageDelayed(MSG_HIDE, animDuration + visibleDuration);
}
private void handleHide() {
if (mZenToast != null) {
mWindowManager.removeView(mZenToast);
mZenToast = null;
}
}
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SHOW:
handleShow(msg.arg1, null);
break;
case MSG_HIDE:
handleHide();
break;
}
}
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_SHOW.equals(intent.getAction())) {
final int zen = intent.getIntExtra(EXTRA_ZEN, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
final String text = intent.getStringExtra(EXTRA_TEXT);
handleShow(zen, text);
} else if (ACTION_HIDE.equals(intent.getAction())) {
handleHide();
}
}
};
}