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