1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.notification; 18 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.media.AudioManager; 22 import android.net.Uri; 23 import android.preference.SeekBarVolumizer; 24 import android.text.TextUtils; 25 import android.util.AttributeSet; 26 import android.view.View; 27 import android.widget.ImageView; 28 import android.widget.SeekBar; 29 import android.widget.TextView; 30 31 import androidx.annotation.VisibleForTesting; 32 import androidx.preference.PreferenceViewHolder; 33 34 import com.android.settings.R; 35 import com.android.settings.widget.SeekBarPreference; 36 37 import java.util.Objects; 38 39 /** A slider preference that directly controls an audio stream volume (no dialog) **/ 40 public class VolumeSeekBarPreference extends SeekBarPreference { 41 private static final String TAG = "VolumeSeekBarPreference"; 42 43 protected SeekBar mSeekBar; 44 private int mStream; 45 private SeekBarVolumizer mVolumizer; 46 private Callback mCallback; 47 private ImageView mIconView; 48 private TextView mSuppressionTextView; 49 private String mSuppressionText; 50 private boolean mMuted; 51 private boolean mZenMuted; 52 private int mIconResId; 53 private int mMuteIconResId; 54 private boolean mStopped; 55 @VisibleForTesting 56 AudioManager mAudioManager; 57 VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)58 public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, 59 int defStyleRes) { 60 super(context, attrs, defStyleAttr, defStyleRes); 61 setLayoutResource(R.layout.preference_volume_slider); 62 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 63 } 64 VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr)65 public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) { 66 super(context, attrs, defStyleAttr); 67 setLayoutResource(R.layout.preference_volume_slider); 68 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 69 } 70 VolumeSeekBarPreference(Context context, AttributeSet attrs)71 public VolumeSeekBarPreference(Context context, AttributeSet attrs) { 72 super(context, attrs); 73 setLayoutResource(R.layout.preference_volume_slider); 74 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 75 } 76 VolumeSeekBarPreference(Context context)77 public VolumeSeekBarPreference(Context context) { 78 super(context); 79 setLayoutResource(R.layout.preference_volume_slider); 80 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 81 } 82 setStream(int stream)83 public void setStream(int stream) { 84 mStream = stream; 85 setMax(mAudioManager.getStreamMaxVolume(mStream)); 86 // Use getStreamMinVolumeInt for non-public stream type 87 // eg: AudioManager.STREAM_BLUETOOTH_SCO 88 setMin(mAudioManager.getStreamMinVolumeInt(mStream)); 89 setProgress(mAudioManager.getStreamVolume(mStream)); 90 } 91 setCallback(Callback callback)92 public void setCallback(Callback callback) { 93 mCallback = callback; 94 } 95 onActivityResume()96 public void onActivityResume() { 97 if (mStopped) { 98 init(); 99 } 100 } 101 onActivityPause()102 public void onActivityPause() { 103 mStopped = true; 104 if (mVolumizer != null) { 105 mVolumizer.stop(); 106 mVolumizer = null; 107 } 108 } 109 110 @Override onBindViewHolder(PreferenceViewHolder view)111 public void onBindViewHolder(PreferenceViewHolder view) { 112 super.onBindViewHolder(view); 113 mSeekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar); 114 mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon); 115 mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text); 116 init(); 117 } 118 init()119 protected void init() { 120 if (mSeekBar == null) return; 121 final SeekBarVolumizer.Callback sbvc = new SeekBarVolumizer.Callback() { 122 @Override 123 public void onSampleStarting(SeekBarVolumizer sbv) { 124 if (mCallback != null) { 125 mCallback.onSampleStarting(sbv); 126 } 127 } 128 @Override 129 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { 130 if (mCallback != null) { 131 mCallback.onStreamValueChanged(mStream, progress); 132 } 133 } 134 @Override 135 public void onMuted(boolean muted, boolean zenMuted) { 136 if (mMuted == muted && mZenMuted == zenMuted) return; 137 mMuted = muted; 138 mZenMuted = zenMuted; 139 updateIconView(); 140 } 141 }; 142 final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null; 143 if (mVolumizer == null) { 144 mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc); 145 } 146 mVolumizer.start(); 147 mVolumizer.setSeekBar(mSeekBar); 148 updateIconView(); 149 updateSuppressionText(); 150 if (!isEnabled()) { 151 mSeekBar.setEnabled(false); 152 mVolumizer.stop(); 153 } 154 } 155 updateIconView()156 protected void updateIconView() { 157 if (mIconView == null) return; 158 if (mIconResId != 0) { 159 mIconView.setImageResource(mIconResId); 160 } else if (mMuteIconResId != 0 && mMuted && !mZenMuted) { 161 mIconView.setImageResource(mMuteIconResId); 162 } else { 163 mIconView.setImageDrawable(getIcon()); 164 } 165 } 166 showIcon(int resId)167 public void showIcon(int resId) { 168 // Instead of using setIcon, which will trigger listeners, this just decorates the 169 // preference temporarily with a new icon. 170 if (mIconResId == resId) return; 171 mIconResId = resId; 172 updateIconView(); 173 } 174 setMuteIcon(int resId)175 public void setMuteIcon(int resId) { 176 if (mMuteIconResId == resId) return; 177 mMuteIconResId = resId; 178 updateIconView(); 179 } 180 getMediaVolumeUri()181 private Uri getMediaVolumeUri() { 182 return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" 183 + getContext().getPackageName() 184 + "/" + R.raw.media_volume); 185 } 186 setSuppressionText(String text)187 public void setSuppressionText(String text) { 188 if (Objects.equals(text, mSuppressionText)) return; 189 mSuppressionText = text; 190 updateSuppressionText(); 191 } 192 updateSuppressionText()193 protected void updateSuppressionText() { 194 if (mSuppressionTextView != null && mSeekBar != null) { 195 mSuppressionTextView.setText(mSuppressionText); 196 final boolean showSuppression = !TextUtils.isEmpty(mSuppressionText); 197 mSuppressionTextView.setVisibility(showSuppression ? View.VISIBLE : View.GONE); 198 } 199 } 200 201 public interface Callback { onSampleStarting(SeekBarVolumizer sbv)202 void onSampleStarting(SeekBarVolumizer sbv); onStreamValueChanged(int stream, int progress)203 void onStreamValueChanged(int stream, int progress); 204 } 205 } 206