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 android.preference;
18 
19 import android.Manifest;
20 import android.annotation.NonNull;
21 import android.annotation.RequiresPermission;
22 import android.app.NotificationManager;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.database.ContentObserver;
29 import android.media.AudioAttributes;
30 import android.media.AudioManager;
31 import android.media.Ringtone;
32 import android.media.RingtoneManager;
33 import android.media.audiopolicy.AudioProductStrategy;
34 import android.media.audiopolicy.AudioVolumeGroup;
35 import android.net.Uri;
36 import android.os.Handler;
37 import android.os.HandlerThread;
38 import android.os.Message;
39 import android.preference.VolumePreference.VolumeStore;
40 import android.provider.Settings;
41 import android.provider.Settings.Global;
42 import android.provider.Settings.System;
43 import android.service.notification.ZenModeConfig;
44 import android.util.Log;
45 import android.widget.SeekBar;
46 import android.widget.SeekBar.OnSeekBarChangeListener;
47 
48 import com.android.internal.annotations.GuardedBy;
49 import com.android.internal.os.SomeArgs;
50 
51 import java.util.concurrent.TimeUnit;
52 
53 /**
54  * Turns a {@link SeekBar} into a volume control.
55  * @hide
56  *
57  * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
58  *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
59  *      Preference Library</a> for consistent behavior across all devices. For more information on
60  *      using the AndroidX Preference Library see
61  *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
62  */
63 @Deprecated
64 public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callback {
65     private static final String TAG = "SeekBarVolumizer";
66 
67     public interface Callback {
onSampleStarting(SeekBarVolumizer sbv)68         void onSampleStarting(SeekBarVolumizer sbv);
onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch)69         void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch);
onMuted(boolean muted, boolean zenMuted)70         void onMuted(boolean muted, boolean zenMuted);
71         /**
72          * Callback reporting that the seek bar is start tracking.
73          *
74          * @param sbv - The seek bar that start tracking
75          */
onStartTrackingTouch(SeekBarVolumizer sbv)76         void onStartTrackingTouch(SeekBarVolumizer sbv);
77 
78         /**
79          * Callback reporting that the seek bar is stop tracking.
80          *
81          * @param sbv - The seek bar that stop tracking
82          */
onStopTrackingTouch(SeekBarVolumizer sbv)83         default void onStopTrackingTouch(SeekBarVolumizer sbv) {
84         }
85     }
86 
87     private static final int MSG_GROUP_VOLUME_CHANGED = 1;
88     private static long sStopVolumeTime = 0L;
89     private final Handler mVolumeHandler = new VolumeHandler();
90     private AudioAttributes mAttributes;
91     private int mVolumeGroupId;
92 
93     private final AudioManager.VolumeGroupCallback mVolumeGroupCallback =
94             new AudioManager.VolumeGroupCallback() {
95         @Override
96         public void onAudioVolumeGroupChanged(int group, int flags) {
97             if (mHandler == null) {
98                 return;
99             }
100             SomeArgs args = SomeArgs.obtain();
101             args.arg1 = group;
102             args.arg2 = flags;
103             mVolumeHandler.sendMessage(mHandler.obtainMessage(MSG_GROUP_VOLUME_CHANGED, args));
104         }
105     };
106 
107     @UnsupportedAppUsage
108     private final Context mContext;
109     private final H mUiHandler = new H();
110     private final Callback mCallback;
111     private final Uri mDefaultUri;
112     @UnsupportedAppUsage
113     private final AudioManager mAudioManager;
114     private final NotificationManager mNotificationManager;
115     @UnsupportedAppUsage
116     private final int mStreamType;
117     private final int mMaxStreamVolume;
118     private boolean mAffectedByRingerMode;
119     private boolean mNotificationOrRing;
120     private final Receiver mReceiver = new Receiver();
121 
122     private Handler mHandler;
123     private Observer mVolumeObserver;
124     @UnsupportedAppUsage
125     private int mOriginalStreamVolume;
126     private int mLastAudibleStreamVolume;
127     // When the old handler is destroyed and a new one is created, there could be a situation where
128     // this is accessed at the same time in different handlers. So, access to this field needs to be
129     // synchronized.
130     @GuardedBy("this")
131     @UnsupportedAppUsage
132     private Ringtone mRingtone;
133     @UnsupportedAppUsage
134     private int mLastProgress = -1;
135     private boolean mMuted;
136     @UnsupportedAppUsage
137     private SeekBar mSeekBar;
138     private int mVolumeBeforeMute = -1;
139     private int mRingerMode;
140     private int mZenMode;
141     private boolean mPlaySample;
142     private final boolean mDeviceHasProductStrategies;
143 
144     private static final int MSG_SET_STREAM_VOLUME = 0;
145     private static final int MSG_START_SAMPLE = 1;
146     private static final int MSG_STOP_SAMPLE = 2;
147     private static final int MSG_INIT_SAMPLE = 3;
148     private static final int MSG_UPDATE_SLIDER_MAYBE_LATER = 4;
149     private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
150     private static final int CHECK_UPDATE_SLIDER_LATER_MS = 500;
151     private static final long SET_STREAM_VOLUME_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500);
152     private static final long START_SAMPLE_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500);
153     private static final long DURATION_TO_START_DELAYING = TimeUnit.MILLISECONDS.toMillis(2000);
154 
155     private NotificationManager.Policy mNotificationPolicy;
156     private boolean mAllowAlarms;
157     private boolean mAllowMedia;
158     private boolean mAllowRinger;
159 
160     @UnsupportedAppUsage
SeekBarVolumizer(Context context, int streamType, Uri defaultUri, Callback callback)161     public SeekBarVolumizer(Context context, int streamType, Uri defaultUri, Callback callback) {
162         this(context, streamType, defaultUri, callback, true /* playSample */);
163     }
164 
165     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
SeekBarVolumizer( Context context, int streamType, Uri defaultUri, Callback callback, boolean playSample)166     public SeekBarVolumizer(
167             Context context,
168             int streamType,
169             Uri defaultUri,
170             Callback callback,
171             boolean playSample) {
172         mContext = context;
173         mAudioManager = context.getSystemService(AudioManager.class);
174         mDeviceHasProductStrategies = hasAudioProductStrategies();
175         mNotificationManager = context.getSystemService(NotificationManager.class);
176         mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy();
177         mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
178                 .PRIORITY_CATEGORY_ALARMS) != 0;
179         mAllowMedia = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
180                 .PRIORITY_CATEGORY_MEDIA) != 0;
181         mAllowRinger = !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
182                 mNotificationPolicy);
183         mStreamType = streamType;
184         mAffectedByRingerMode = mAudioManager.isStreamAffectedByRingerMode(mStreamType);
185         mNotificationOrRing = isNotificationOrRing(mStreamType);
186         if (mNotificationOrRing) {
187             mRingerMode = mAudioManager.getRingerModeInternal();
188         }
189         mZenMode = mNotificationManager.getZenMode();
190 
191         if (mDeviceHasProductStrategies) {
192             mVolumeGroupId = getVolumeGroupIdForLegacyStreamType(mStreamType);
193             mAttributes = getAudioAttributesForLegacyStreamType(
194                     mStreamType);
195         }
196 
197         mMaxStreamVolume = mAudioManager.getStreamMaxVolume(mStreamType);
198         mCallback = callback;
199         mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
200         mLastAudibleStreamVolume = mAudioManager.getLastAudibleStreamVolume(mStreamType);
201         mMuted = mAudioManager.isStreamMute(mStreamType);
202         mPlaySample = playSample;
203         if (mCallback != null) {
204             mCallback.onMuted(mMuted, isZenMuted());
205         }
206         if (defaultUri == null) {
207             if (mStreamType == AudioManager.STREAM_RING) {
208                 defaultUri = Settings.System.DEFAULT_RINGTONE_URI;
209             } else if (mStreamType == AudioManager.STREAM_NOTIFICATION) {
210                 defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI;
211             } else {
212                 defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
213             }
214         }
215         mDefaultUri = defaultUri;
216     }
217 
218     /**
219      * DO NOT CALL every time this is needed, use once in constructor,
220      * read mDeviceHasProductStrategies instead
221      * @return true if stream types are used for volume management, false if volume groups are
222      *     used for volume management
223      */
hasAudioProductStrategies()224     private boolean hasAudioProductStrategies() {
225         return AudioManager.getAudioProductStrategies().size() > 0;
226     }
227 
getVolumeGroupIdForLegacyStreamType(int streamType)228     private int getVolumeGroupIdForLegacyStreamType(int streamType) {
229         for (final AudioProductStrategy productStrategy :
230                 AudioManager.getAudioProductStrategies()) {
231             int volumeGroupId = productStrategy.getVolumeGroupIdForLegacyStreamType(streamType);
232             if (volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
233                 return volumeGroupId;
234             }
235         }
236 
237         return AudioManager.getAudioProductStrategies().stream()
238                 .map(strategy -> strategy.getVolumeGroupIdForAudioAttributes(
239                         AudioProductStrategy.getDefaultAttributes()))
240                 .filter(volumeGroupId -> volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP)
241                 .findFirst()
242                 .orElse(AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
243     }
244 
getAudioAttributesForLegacyStreamType(int streamType)245     private @NonNull AudioAttributes getAudioAttributesForLegacyStreamType(int streamType) {
246         for (final AudioProductStrategy productStrategy :
247                 AudioManager.getAudioProductStrategies()) {
248             AudioAttributes aa = productStrategy.getAudioAttributesForLegacyStreamType(streamType);
249             if (aa != null) {
250                 return aa;
251             }
252         }
253         return new AudioAttributes.Builder()
254                 .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
255                 .setUsage(AudioAttributes.USAGE_UNKNOWN).build();
256     }
257 
isNotificationOrRing(int stream)258     private static boolean isNotificationOrRing(int stream) {
259         return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
260     }
261 
isAlarmsStream(int stream)262     private static boolean isAlarmsStream(int stream) {
263         return stream == AudioManager.STREAM_ALARM;
264     }
265 
isMediaStream(int stream)266     private static boolean isMediaStream(int stream) {
267         return stream == AudioManager.STREAM_MUSIC;
268     }
269 
setSeekBar(SeekBar seekBar)270     public void setSeekBar(SeekBar seekBar) {
271         if (mSeekBar != null) {
272             mSeekBar.setOnSeekBarChangeListener(null);
273         }
274         mSeekBar = seekBar;
275         mSeekBar.setOnSeekBarChangeListener(null);
276         mSeekBar.setMax(mMaxStreamVolume);
277         updateSeekBar();
278         mSeekBar.setOnSeekBarChangeListener(this);
279     }
280 
isZenMuted()281     private boolean isZenMuted() {
282         return mNotificationOrRing && mZenMode == Global.ZEN_MODE_ALARMS
283                 || mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
284                 || (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
285                     && ((!mAllowAlarms && isAlarmsStream(mStreamType))
286                         || (!mAllowMedia && isMediaStream(mStreamType))
287                         || (!mAllowRinger && isNotificationOrRing(mStreamType))));
288     }
289 
updateSeekBar()290     protected void updateSeekBar() {
291         final boolean zenMuted = isZenMuted();
292         mSeekBar.setEnabled(!zenMuted);
293         if (zenMuted) {
294             mSeekBar.setProgress(mLastAudibleStreamVolume, true);
295         } else if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
296             // For ringer-mode affected streams, show volume as zero when ringermode is vibrate
297             if (mStreamType == AudioManager.STREAM_RING
298                     || (mStreamType == AudioManager.STREAM_NOTIFICATION && mMuted)) {
299                 mSeekBar.setProgress(0, true);
300             }
301         } else if (mMuted) {
302             mSeekBar.setProgress(0, true);
303         } else {
304             mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume, true);
305         }
306     }
307 
308     @Override
handleMessage(Message msg)309     public boolean handleMessage(Message msg) {
310         switch (msg.what) {
311             case MSG_SET_STREAM_VOLUME:
312                 if (mMuted && mLastProgress > 0) {
313                     mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_UNMUTE, 0);
314                 } else if (!mMuted && mLastProgress == 0) {
315                     mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_MUTE, 0);
316                 }
317                 mAudioManager.setStreamVolume(mStreamType, mLastProgress,
318                         AudioManager.FLAG_SHOW_UI_WARNINGS);
319                 break;
320             case MSG_START_SAMPLE:
321                 if (mPlaySample) {
322                     onStartSample();
323                 }
324                 break;
325             case MSG_STOP_SAMPLE:
326                 if (mPlaySample) {
327                     onStopSample();
328                 }
329                 break;
330             case MSG_INIT_SAMPLE:
331                 if (mPlaySample) {
332                     onInitSample();
333                 }
334                 break;
335             case MSG_UPDATE_SLIDER_MAYBE_LATER:
336                 onUpdateSliderMaybeLater();
337                 break;
338             default:
339                 Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what);
340         }
341         return true;
342     }
343 
onInitSample()344     private void onInitSample() {
345         synchronized (this) {
346             mRingtone = RingtoneManager.getRingtone(mContext, mDefaultUri);
347             if (mRingtone != null) {
348                 mRingtone.setStreamType(mStreamType);
349             }
350         }
351     }
352 
postStartSample()353     private void postStartSample() {
354         if (mHandler == null) return;
355         mHandler.removeMessages(MSG_START_SAMPLE);
356         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),
357                 isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS
358                         : isDelay() ? START_SAMPLE_DELAY_MS : 0);
359     }
360 
onUpdateSliderMaybeLater()361     private void onUpdateSliderMaybeLater() {
362         if (isDelay()) {
363             postUpdateSliderMaybeLater();
364             return;
365         }
366         updateSlider();
367     }
368 
postUpdateSliderMaybeLater()369     private void postUpdateSliderMaybeLater() {
370         if (mHandler == null) return;
371         mHandler.removeMessages(MSG_UPDATE_SLIDER_MAYBE_LATER);
372         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SLIDER_MAYBE_LATER),
373                 CHECK_UPDATE_SLIDER_LATER_MS);
374     }
375 
376     // After stop volume it needs to add a small delay when playing volume or set stream.
377     // It is because the call volume is from the earpiece and the alarm/ring/media
378     // is from the speaker. If play the alarm volume or set alarm stream right after stop
379     // call volume, the alarm volume on earpiece is returned then cause the volume value incorrect.
380     // It needs a small delay after stop call volume to get alarm volume on speaker.
381     // e.g. : If the ring volume has adjusted right after call volume stopped in 2 second
382     // then delay 0.5 second to set stream or play volume ringtone.
isDelay()383     private boolean isDelay() {
384         final long durationTime = java.lang.System.currentTimeMillis() - sStopVolumeTime;
385         return durationTime >= 0 && durationTime < DURATION_TO_START_DELAYING;
386     }
387 
setStopVolumeTime()388     private void setStopVolumeTime() {
389         // set the time of stop volume
390         if ((mStreamType == AudioManager.STREAM_VOICE_CALL
391                 || mStreamType == AudioManager.STREAM_RING
392                 || mStreamType == AudioManager.STREAM_NOTIFICATION
393                 || mStreamType == AudioManager.STREAM_ALARM)) {
394             sStopVolumeTime = java.lang.System.currentTimeMillis();
395         }
396     }
397 
onStartSample()398     private void onStartSample() {
399         if (!isSamplePlaying()) {
400             if (mCallback != null) {
401                 mCallback.onSampleStarting(this);
402             }
403 
404             synchronized (this) {
405                 if (mRingtone != null) {
406                     try {
407                         mRingtone.setAudioAttributes(new AudioAttributes.Builder(mRingtone
408                                 .getAudioAttributes())
409                                 .setFlags(AudioAttributes.FLAG_BYPASS_MUTE)
410                                 .addTag("VX_AOSP_SAMPLESOUND")
411                                 .build());
412                         mRingtone.play();
413                     } catch (Throwable e) {
414                         Log.w(TAG, "Error playing ringtone, stream " + mStreamType, e);
415                     }
416                 }
417             }
418         }
419     }
420 
postStopSample()421     private void postStopSample() {
422         if (mHandler == null) return;
423         setStopVolumeTime();
424         // remove pending delayed start messages
425         mHandler.removeMessages(MSG_START_SAMPLE);
426         mHandler.removeMessages(MSG_STOP_SAMPLE);
427         mHandler.sendMessage(mHandler.obtainMessage(MSG_STOP_SAMPLE));
428     }
429 
onStopSample()430     private void onStopSample() {
431         synchronized (this) {
432             if (mRingtone != null) {
433                 mRingtone.stop();
434             }
435         }
436     }
437 
438     @UnsupportedAppUsage
stop()439     public void stop() {
440         if (mHandler == null) return;  // already stopped
441         postStopSample();
442         mContext.getContentResolver().unregisterContentObserver(mVolumeObserver);
443         mReceiver.setListening(false);
444         if (mDeviceHasProductStrategies) {
445             unregisterVolumeGroupCb();
446         }
447         mSeekBar.setOnSeekBarChangeListener(null);
448         mHandler.getLooper().quitSafely();
449         mHandler = null;
450         mVolumeObserver = null;
451     }
452 
start()453     public void start() {
454         if (mHandler != null) return;  // already started
455         HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
456         thread.start();
457         mHandler = new Handler(thread.getLooper(), this);
458         mHandler.sendEmptyMessage(MSG_INIT_SAMPLE);
459         mVolumeObserver = new Observer(mHandler);
460         mContext.getContentResolver().registerContentObserver(
461                 System.getUriFor(System.VOLUME_SETTINGS_INT[mStreamType]),
462                 false, mVolumeObserver);
463         mReceiver.setListening(true);
464         if (mDeviceHasProductStrategies) {
465             registerVolumeGroupCb();
466         }
467     }
468 
revertVolume()469     public void revertVolume() {
470         mAudioManager.setStreamVolume(mStreamType, mOriginalStreamVolume, 0);
471     }
472 
onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch)473     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
474         if (fromTouch) {
475             postSetVolume(progress);
476         }
477         if (mCallback != null) {
478             mCallback.onProgressChanged(seekBar, progress, fromTouch);
479         }
480     }
481 
postSetVolume(int progress)482     private void postSetVolume(int progress) {
483         if (mHandler == null) return;
484         // Do the volume changing separately to give responsive UI
485         mLastProgress = progress;
486         mHandler.removeMessages(MSG_SET_STREAM_VOLUME);
487         mHandler.removeMessages(MSG_START_SAMPLE);
488         mHandler.removeMessages(MSG_UPDATE_SLIDER_MAYBE_LATER);
489         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME),
490                 isDelay() ? SET_STREAM_VOLUME_DELAY_MS : 0);
491     }
492 
onStartTrackingTouch(SeekBar seekBar)493     public void onStartTrackingTouch(SeekBar seekBar) {
494         if (mCallback != null) {
495             mCallback.onStartTrackingTouch(this);
496         }
497     }
498 
onStopTrackingTouch(SeekBar seekBar)499     public void onStopTrackingTouch(SeekBar seekBar) {
500         postStartSample();
501         if (mCallback != null) {
502             mCallback.onStopTrackingTouch(this);
503         }
504     }
505 
isSamplePlaying()506     public boolean isSamplePlaying() {
507         synchronized (this) {
508             return mRingtone != null && mRingtone.isPlaying();
509         }
510     }
511 
startSample()512     public void startSample() {
513         postStartSample();
514     }
515 
stopSample()516     public void stopSample() {
517         postStopSample();
518     }
519 
getSeekBar()520     public SeekBar getSeekBar() {
521         return mSeekBar;
522     }
523 
changeVolumeBy(int amount)524     public void changeVolumeBy(int amount) {
525         mSeekBar.incrementProgressBy(amount);
526         postSetVolume(mSeekBar.getProgress());
527         postStartSample();
528         mVolumeBeforeMute = -1;
529     }
530 
muteVolume()531     public void muteVolume() {
532         if (mVolumeBeforeMute != -1) {
533             mSeekBar.setProgress(mVolumeBeforeMute, true);
534             postSetVolume(mVolumeBeforeMute);
535             postStartSample();
536             mVolumeBeforeMute = -1;
537         } else {
538             mVolumeBeforeMute = mSeekBar.getProgress();
539             mSeekBar.setProgress(0, true);
540             postStopSample();
541             postSetVolume(0);
542         }
543     }
544 
onSaveInstanceState(VolumeStore volumeStore)545     public void onSaveInstanceState(VolumeStore volumeStore) {
546         if (mLastProgress >= 0) {
547             volumeStore.volume = mLastProgress;
548             volumeStore.originalVolume = mOriginalStreamVolume;
549         }
550     }
551 
onRestoreInstanceState(VolumeStore volumeStore)552     public void onRestoreInstanceState(VolumeStore volumeStore) {
553         if (volumeStore.volume != -1) {
554             mOriginalStreamVolume = volumeStore.originalVolume;
555             mLastProgress = volumeStore.volume;
556             postSetVolume(mLastProgress);
557         }
558     }
559 
560     private final class H extends Handler {
561         private static final int UPDATE_SLIDER = 1;
562 
563         @Override
handleMessage(Message msg)564         public void handleMessage(Message msg) {
565             if (msg.what == UPDATE_SLIDER) {
566                 if (mSeekBar != null) {
567                     mLastProgress = msg.arg1;
568                     mLastAudibleStreamVolume = msg.arg2;
569                     final boolean muted = ((Boolean)msg.obj).booleanValue();
570                     if (muted != mMuted) {
571                         mMuted = muted;
572                         if (mCallback != null) {
573                             mCallback.onMuted(mMuted, isZenMuted());
574                         }
575                     }
576                     updateSeekBar();
577                 }
578             }
579         }
580 
postUpdateSlider(int volume, int lastAudibleVolume, boolean mute)581         public void postUpdateSlider(int volume, int lastAudibleVolume, boolean mute) {
582             obtainMessage(UPDATE_SLIDER, volume, lastAudibleVolume, Boolean.valueOf(mute))
583                     .sendToTarget();
584         }
585     }
586 
updateSlider()587     private void updateSlider() {
588         if (mSeekBar != null && mAudioManager != null) {
589             final int volume = mAudioManager.getStreamVolume(mStreamType);
590             final int lastAudibleVolume = mAudioManager.getLastAudibleStreamVolume(mStreamType);
591             final boolean mute = mAudioManager.isStreamMute(mStreamType);
592             mUiHandler.postUpdateSlider(volume, lastAudibleVolume, mute);
593         }
594     }
595 
596     private final class Observer extends ContentObserver {
Observer(Handler handler)597         public Observer(Handler handler) {
598             super(handler);
599         }
600 
601         @Override
onChange(boolean selfChange)602         public void onChange(boolean selfChange) {
603             super.onChange(selfChange);
604             updateSlider();
605         }
606     }
607 
608     private final class Receiver extends BroadcastReceiver {
609         private boolean mListening;
610 
setListening(boolean listening)611         public void setListening(boolean listening) {
612             if (mListening == listening) return;
613             mListening = listening;
614             if (listening) {
615                 final IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
616                 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
617                 filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
618                 filter.addAction(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
619                 filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
620                 mContext.registerReceiver(this, filter);
621             } else {
622                 mContext.unregisterReceiver(this);
623             }
624         }
625 
626         @Override
onReceive(Context context, Intent intent)627         public void onReceive(Context context, Intent intent) {
628             final String action = intent.getAction();
629             if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
630                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
631                 int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
632                 if (mDeviceHasProductStrategies && !isDelay()) {
633                     updateVolumeSlider(streamType, streamValue);
634                 }
635             } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
636                 if (mNotificationOrRing) {
637                     mRingerMode = mAudioManager.getRingerModeInternal();
638                 }
639                 if (mAffectedByRingerMode) {
640                     updateSlider();
641                 }
642             } else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) {
643                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
644 
645                 if (mDeviceHasProductStrategies) {
646                     if (isDelay()) {
647                         // not the right time to update the sliders, try again later
648                         postUpdateSliderMaybeLater();
649                     } else {
650                         int streamVolume = mAudioManager.getStreamVolume(streamType);
651                         updateVolumeSlider(streamType, streamVolume);
652                     }
653 
654                 } else {
655                     int volumeGroup = getVolumeGroupIdForLegacyStreamType(streamType);
656                     if (volumeGroup != AudioVolumeGroup.DEFAULT_VOLUME_GROUP
657                             && volumeGroup == mVolumeGroupId) {
658                         int streamVolume = mAudioManager.getStreamVolume(streamType);
659                         if (!isDelay()) {
660                             updateVolumeSlider(streamType, streamVolume);
661                         }
662                     }
663                 }
664             } else if (NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED.equals(action)) {
665                 mZenMode = mNotificationManager.getZenMode();
666                 updateSlider();
667             } else if (NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED.equals(action)) {
668                 mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy();
669                 mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
670                         .PRIORITY_CATEGORY_ALARMS) != 0;
671                 mAllowMedia = (mNotificationPolicy.priorityCategories
672                         & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) != 0;
673                 mAllowRinger = !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
674                         mNotificationPolicy);
675                 updateSlider();
676             }
677         }
678 
updateVolumeSlider(int streamType, int streamValue)679         private void updateVolumeSlider(int streamType, int streamValue) {
680             final boolean streamMatch = (streamType == mStreamType);
681             if (mSeekBar != null && streamMatch && streamValue != -1) {
682                 final boolean muted = mAudioManager.isStreamMute(mStreamType)
683                         || streamValue == 0;
684                 mUiHandler.postUpdateSlider(streamValue, mLastAudibleStreamVolume, muted);
685             }
686         }
687     }
688 
registerVolumeGroupCb()689     private void registerVolumeGroupCb() {
690         if (mVolumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
691             mAudioManager.registerVolumeGroupCallback(Runnable::run, mVolumeGroupCallback);
692             updateSlider();
693         }
694     }
695 
unregisterVolumeGroupCb()696     private void unregisterVolumeGroupCb() {
697         if (mVolumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
698             mAudioManager.unregisterVolumeGroupCallback(mVolumeGroupCallback);
699         }
700     }
701 
702     private class VolumeHandler extends Handler {
703         @Override
handleMessage(Message msg)704         public void handleMessage(Message msg) {
705             SomeArgs args = (SomeArgs) msg.obj;
706             switch (msg.what) {
707                 case MSG_GROUP_VOLUME_CHANGED:
708                     int group = (int) args.arg1;
709                     if (mVolumeGroupId != group
710                             || mVolumeGroupId == AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
711                         return;
712                     }
713                     updateSlider();
714                     break;
715             }
716         }
717     }
718 }
719