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