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