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.PreferenceManager;
24 import android.preference.SeekBarPreference;
25 import android.preference.SeekBarVolumizer;
26 import android.text.TextUtils;
27 import android.util.AttributeSet;
28 import android.util.Log;
29 import android.view.View;
30 import android.widget.ImageView;
31 import android.widget.SeekBar;
32 import android.widget.TextView;
33 
34 import com.android.settings.R;
35 
36 import java.util.Objects;
37 
38 /** A slider preference that directly controls an audio stream volume (no dialog) **/
39 public class VolumeSeekBarPreference extends SeekBarPreference
40         implements PreferenceManager.OnActivityStopListener {
41     private static final String TAG = "VolumeSeekBarPreference";
42 
43     private int mStream;
44     private SeekBar mSeekBar;
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 int mIconResId;
52     private int mMuteIconResId;
53     private boolean mStopped;
54 
VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)55     public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr,
56             int defStyleRes) {
57         super(context, attrs, defStyleAttr, defStyleRes);
58         setLayoutResource(R.layout.preference_volume_slider);
59     }
60 
VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr)61     public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
62         this(context, attrs, defStyleAttr, 0);
63     }
64 
VolumeSeekBarPreference(Context context, AttributeSet attrs)65     public VolumeSeekBarPreference(Context context, AttributeSet attrs) {
66         this(context, attrs, 0);
67     }
68 
VolumeSeekBarPreference(Context context)69     public VolumeSeekBarPreference(Context context) {
70         this(context, null);
71     }
72 
setStream(int stream)73     public void setStream(int stream) {
74         mStream = stream;
75     }
76 
setCallback(Callback callback)77     public void setCallback(Callback callback) {
78         mCallback = callback;
79     }
80 
onActivityResume()81     public void onActivityResume() {
82         if (mStopped) {
83             init();
84         }
85     }
86 
87     @Override
onActivityStop()88     public void onActivityStop() {
89         mStopped = true;
90         if (mVolumizer != null) {
91             mVolumizer.stop();
92         }
93     }
94 
95     @Override
onBindView(View view)96     protected void onBindView(View view) {
97         super.onBindView(view);
98         if (mStream == 0) {
99             Log.w(TAG, "No stream found, not binding volumizer");
100             return;
101         }
102         mSeekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
103         mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
104         mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text);
105         init();
106     }
107 
init()108     private void init() {
109         if (mSeekBar == null) return;
110         getPreferenceManager().registerOnActivityStopListener(this);
111         final SeekBarVolumizer.Callback sbvc = new SeekBarVolumizer.Callback() {
112             @Override
113             public void onSampleStarting(SeekBarVolumizer sbv) {
114                 if (mCallback != null) {
115                     mCallback.onSampleStarting(sbv);
116                 }
117             }
118             @Override
119             public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
120                 if (mCallback != null) {
121                     mCallback.onStreamValueChanged(mStream, progress);
122                 }
123             }
124             @Override
125             public void onMuted(boolean muted) {
126                 if (mMuted == muted) return;
127                 mMuted = muted;
128                 updateIconView();
129             }
130         };
131         final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null;
132         if (mVolumizer == null) {
133             mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc);
134         }
135         mVolumizer.start();
136         mVolumizer.setSeekBar(mSeekBar);
137         updateIconView();
138         mCallback.onStreamValueChanged(mStream, mSeekBar.getProgress());
139         updateSuppressionText();
140     }
141 
142     // during initialization, this preference is the SeekBar listener
143     @Override
onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch)144     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
145         super.onProgressChanged(seekBar, progress, fromTouch);
146         mCallback.onStreamValueChanged(mStream, progress);
147     }
148 
updateIconView()149     private void updateIconView() {
150         if (mIconView == null) return;
151         if (mIconResId != 0) {
152             mIconView.setImageResource(mIconResId);
153         } else if (mMuteIconResId != 0 && mMuted) {
154             mIconView.setImageResource(mMuteIconResId);
155         } else {
156             mIconView.setImageDrawable(getIcon());
157         }
158     }
159 
showIcon(int resId)160     public void showIcon(int resId) {
161         // Instead of using setIcon, which will trigger listeners, this just decorates the
162         // preference temporarily with a new icon.
163         if (mIconResId == resId) return;
164         mIconResId = resId;
165         updateIconView();
166     }
167 
setMuteIcon(int resId)168     public void setMuteIcon(int resId) {
169         if (mMuteIconResId == resId) return;
170         mMuteIconResId = resId;
171         updateIconView();
172     }
173 
getMediaVolumeUri()174     private Uri getMediaVolumeUri() {
175         return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
176                 + getContext().getPackageName()
177                 + "/" + R.raw.media_volume);
178     }
179 
setSuppressionText(String text)180     public void setSuppressionText(String text) {
181         if (Objects.equals(text, mSuppressionText)) return;
182         mSuppressionText = text;
183         updateSuppressionText();
184     }
185 
updateSuppressionText()186     private void updateSuppressionText() {
187         if (mSuppressionTextView != null && mSeekBar != null) {
188             mSuppressionTextView.setText(mSuppressionText);
189             final boolean showSuppression = !TextUtils.isEmpty(mSuppressionText);
190             mSuppressionTextView.setVisibility(showSuppression ? View.VISIBLE : View.INVISIBLE);
191             mSeekBar.setVisibility(showSuppression ? View.INVISIBLE : View.VISIBLE);
192         }
193     }
194 
195     public interface Callback {
onSampleStarting(SeekBarVolumizer sbv)196         void onSampleStarting(SeekBarVolumizer sbv);
onStreamValueChanged(int stream, int progress)197         void onStreamValueChanged(int stream, int progress);
198     }
199 }
200