1 /* 2 * Copyright (C) 2007 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.view; 18 19 import com.android.internal.R; 20 21 import android.app.Dialog; 22 import android.content.DialogInterface.OnDismissListener; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.res.Resources; 29 import android.media.AudioManager; 30 import android.media.AudioService; 31 import android.media.AudioSystem; 32 import android.media.RingtoneManager; 33 import android.media.ToneGenerator; 34 import android.net.Uri; 35 import android.os.Handler; 36 import android.os.Message; 37 import android.os.RemoteException; 38 import android.os.Vibrator; 39 import android.provider.Settings; 40 import android.provider.Settings.System; 41 import android.util.Log; 42 import android.view.WindowManager.LayoutParams; 43 import android.widget.ImageView; 44 import android.widget.SeekBar; 45 import android.widget.SeekBar.OnSeekBarChangeListener; 46 47 import java.util.HashMap; 48 49 /** 50 * Handle the volume up and down keys. 51 * 52 * This code really should be moved elsewhere. 53 * 54 * Seriously, it really really should be moved elsewhere. This is used by 55 * android.media.AudioService, which actually runs in the system process, to 56 * show the volume dialog when the user changes the volume. What a mess. 57 * 58 * @hide 59 */ 60 public class VolumePanel extends Handler implements OnSeekBarChangeListener, View.OnClickListener 61 { 62 private static final String TAG = "VolumePanel"; 63 private static boolean LOGD = false; 64 65 /** 66 * The delay before playing a sound. This small period exists so the user 67 * can press another key (non-volume keys, too) to have it NOT be audible. 68 * <p> 69 * PhoneWindow will implement this part. 70 */ 71 public static final int PLAY_SOUND_DELAY = 300; 72 73 /** 74 * The delay before vibrating. This small period exists so if the user is 75 * moving to silent mode, it will not emit a short vibrate (it normally 76 * would since vibrate is between normal mode and silent mode using hardware 77 * keys). 78 */ 79 public static final int VIBRATE_DELAY = 300; 80 81 private static final int VIBRATE_DURATION = 300; 82 private static final int BEEP_DURATION = 150; 83 private static final int MAX_VOLUME = 100; 84 private static final int FREE_DELAY = 10000; 85 private static final int TIMEOUT_DELAY = 3000; 86 87 private static final int MSG_VOLUME_CHANGED = 0; 88 private static final int MSG_FREE_RESOURCES = 1; 89 private static final int MSG_PLAY_SOUND = 2; 90 private static final int MSG_STOP_SOUNDS = 3; 91 private static final int MSG_VIBRATE = 4; 92 private static final int MSG_TIMEOUT = 5; 93 private static final int MSG_RINGER_MODE_CHANGED = 6; 94 95 protected Context mContext; 96 private AudioManager mAudioManager; 97 protected AudioService mAudioService; 98 private boolean mRingIsSilent; 99 private boolean mShowCombinedVolumes; 100 private boolean mVoiceCapable; 101 102 /** Dialog containing all the sliders */ 103 private final Dialog mDialog; 104 /** Dialog's content view */ 105 private final View mView; 106 107 /** The visible portion of the volume overlay */ 108 private final ViewGroup mPanel; 109 /** Contains the sliders and their touchable icons */ 110 private final ViewGroup mSliderGroup; 111 /** The button that expands the dialog to show all sliders */ 112 private final View mMoreButton; 113 /** Dummy divider icon that needs to vanish with the more button */ 114 private final View mDivider; 115 116 /** Currently active stream that shows up at the top of the list of sliders */ 117 private int mActiveStreamType = -1; 118 /** All the slider controls mapped by stream type */ 119 private HashMap<Integer,StreamControl> mStreamControls; 120 121 private enum StreamResources { 122 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO, 123 R.string.volume_icon_description_bluetooth, 124 R.drawable.ic_audio_bt, 125 R.drawable.ic_audio_bt, 126 false), 127 RingerStream(AudioManager.STREAM_RING, 128 R.string.volume_icon_description_ringer, 129 R.drawable.ic_audio_ring_notif, 130 R.drawable.ic_audio_ring_notif_mute, 131 false), 132 VoiceStream(AudioManager.STREAM_VOICE_CALL, 133 R.string.volume_icon_description_incall, 134 R.drawable.ic_audio_phone, 135 R.drawable.ic_audio_phone, 136 false), 137 AlarmStream(AudioManager.STREAM_ALARM, 138 R.string.volume_alarm, 139 R.drawable.ic_audio_alarm, 140 R.drawable.ic_audio_alarm_mute, 141 false), 142 MediaStream(AudioManager.STREAM_MUSIC, 143 R.string.volume_icon_description_media, 144 R.drawable.ic_audio_vol, 145 R.drawable.ic_audio_vol_mute, 146 true), 147 NotificationStream(AudioManager.STREAM_NOTIFICATION, 148 R.string.volume_icon_description_notification, 149 R.drawable.ic_audio_notification, 150 R.drawable.ic_audio_notification_mute, 151 true); 152 153 int streamType; 154 int descRes; 155 int iconRes; 156 int iconMuteRes; 157 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested 158 boolean show; 159 160 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) { 161 this.streamType = streamType; 162 this.descRes = descRes; 163 this.iconRes = iconRes; 164 this.iconMuteRes = iconMuteRes; 165 this.show = show; 166 } 167 }; 168 169 // List of stream types and their order 170 private static final StreamResources[] STREAMS = { 171 StreamResources.BluetoothSCOStream, 172 StreamResources.RingerStream, 173 StreamResources.VoiceStream, 174 StreamResources.MediaStream, 175 StreamResources.NotificationStream, 176 StreamResources.AlarmStream 177 }; 178 179 /** Object that contains data for each slider */ 180 private class StreamControl { 181 int streamType; 182 ViewGroup group; 183 ImageView icon; 184 SeekBar seekbarView; 185 int iconRes; 186 int iconMuteRes; 187 } 188 189 // Synchronize when accessing this 190 private ToneGenerator mToneGenerators[]; 191 private Vibrator mVibrator; 192 193 public VolumePanel(final Context context, AudioService volumeService) { 194 mContext = context; 195 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 196 mAudioService = volumeService; 197 198 LayoutInflater inflater = (LayoutInflater) context 199 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 200 View view = mView = inflater.inflate(R.layout.volume_adjust, null); 201 mView.setOnTouchListener(new View.OnTouchListener() { 202 public boolean onTouch(View v, MotionEvent event) { 203 resetTimeout(); 204 return false; 205 } 206 }); 207 mPanel = (ViewGroup) mView.findViewById(R.id.visible_panel); 208 mSliderGroup = (ViewGroup) mView.findViewById(R.id.slider_group); 209 mMoreButton = (ImageView) mView.findViewById(R.id.expand_button); 210 mDivider = (ImageView) mView.findViewById(R.id.expand_button_divider); 211 212 mDialog = new Dialog(context, R.style.Theme_Panel_Volume) { 213 public boolean onTouchEvent(MotionEvent event) { 214 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE) { 215 forceTimeout(); 216 return true; 217 } 218 return false; 219 } 220 }; 221 mDialog.setTitle("Volume control"); // No need to localize 222 mDialog.setContentView(mView); 223 mDialog.setOnDismissListener(new OnDismissListener() { 224 public void onDismiss(DialogInterface dialog) { 225 mActiveStreamType = -1; 226 mAudioManager.forceVolumeControlStream(mActiveStreamType); 227 } 228 }); 229 // Change some window properties 230 Window window = mDialog.getWindow(); 231 window.setGravity(Gravity.TOP); 232 LayoutParams lp = window.getAttributes(); 233 lp.token = null; 234 // Offset from the top 235 lp.y = mContext.getResources().getDimensionPixelOffset( 236 com.android.internal.R.dimen.volume_panel_top); 237 lp.type = LayoutParams.TYPE_VOLUME_OVERLAY; 238 lp.width = LayoutParams.WRAP_CONTENT; 239 lp.height = LayoutParams.WRAP_CONTENT; 240 window.setAttributes(lp); 241 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCH_MODAL 242 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); 243 244 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()]; 245 mVibrator = new Vibrator(); 246 247 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable); 248 mShowCombinedVolumes = !mVoiceCapable; 249 // If we don't want to show multiple volumes, hide the settings button and divider 250 if (!mShowCombinedVolumes) { 251 mMoreButton.setVisibility(View.GONE); 252 mDivider.setVisibility(View.GONE); 253 } else { 254 mMoreButton.setOnClickListener(this); 255 } 256 257 listenToRingerMode(); 258 } 259 260 private void listenToRingerMode() { 261 final IntentFilter filter = new IntentFilter(); 262 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); 263 mContext.registerReceiver(new BroadcastReceiver() { 264 265 public void onReceive(Context context, Intent intent) { 266 final String action = intent.getAction(); 267 268 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) { 269 removeMessages(MSG_RINGER_MODE_CHANGED); 270 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED)); 271 } 272 } 273 }, filter); 274 } 275 276 private boolean isMuted(int streamType) { 277 return mAudioManager.isStreamMute(streamType); 278 } 279 280 private void createSliders() { 281 final int silentableStreams = System.getInt(mContext.getContentResolver(), 282 System.MODE_RINGER_STREAMS_AFFECTED, 283 ((1 << AudioSystem.STREAM_NOTIFICATION) | (1 << AudioSystem.STREAM_RING))); 284 285 LayoutInflater inflater = (LayoutInflater) mContext 286 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 287 mStreamControls = new HashMap<Integer, StreamControl>(STREAMS.length); 288 Resources res = mContext.getResources(); 289 for (int i = 0; i < STREAMS.length; i++) { 290 StreamResources streamRes = STREAMS[i]; 291 int streamType = streamRes.streamType; 292 if (mVoiceCapable && streamRes == StreamResources.NotificationStream) { 293 streamRes = StreamResources.RingerStream; 294 } 295 StreamControl sc = new StreamControl(); 296 sc.streamType = streamType; 297 sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null); 298 sc.group.setTag(sc); 299 sc.icon = (ImageView) sc.group.findViewById(R.id.stream_icon); 300 if ((silentableStreams & (1 << sc.streamType)) != 0) { 301 sc.icon.setOnClickListener(this); 302 } 303 sc.icon.setTag(sc); 304 sc.icon.setContentDescription(res.getString(streamRes.descRes)); 305 sc.iconRes = streamRes.iconRes; 306 sc.iconMuteRes = streamRes.iconMuteRes; 307 sc.icon.setImageResource(sc.iconRes); 308 sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar); 309 int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO || 310 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0; 311 sc.seekbarView.setMax(mAudioManager.getStreamMaxVolume(streamType) + plusOne); 312 sc.seekbarView.setOnSeekBarChangeListener(this); 313 sc.seekbarView.setTag(sc); 314 mStreamControls.put(streamType, sc); 315 } 316 } 317 318 private void reorderSliders(int activeStreamType) { 319 mSliderGroup.removeAllViews(); 320 321 StreamControl active = mStreamControls.get(activeStreamType); 322 if (active == null) { 323 Log.e("VolumePanel", "Missing stream type! - " + activeStreamType); 324 mActiveStreamType = -1; 325 } else { 326 mSliderGroup.addView(active.group); 327 mActiveStreamType = activeStreamType; 328 active.group.setVisibility(View.VISIBLE); 329 updateSlider(active); 330 } 331 332 addOtherVolumes(); 333 } 334 335 private void addOtherVolumes() { 336 if (!mShowCombinedVolumes) return; 337 338 for (int i = 0; i < STREAMS.length; i++) { 339 // Skip the phone specific ones and the active one 340 final int streamType = STREAMS[i].streamType; 341 if (!STREAMS[i].show || streamType == mActiveStreamType) { 342 continue; 343 } 344 StreamControl sc = mStreamControls.get(streamType); 345 mSliderGroup.addView(sc.group); 346 updateSlider(sc); 347 } 348 } 349 350 /** Update the mute and progress state of a slider */ 351 private void updateSlider(StreamControl sc) { 352 sc.seekbarView.setProgress(mAudioManager.getLastAudibleStreamVolume(sc.streamType)); 353 final boolean muted = isMuted(sc.streamType); 354 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes); 355 if (sc.streamType == AudioManager.STREAM_RING && muted 356 && mAudioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) { 357 sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate); 358 } 359 sc.seekbarView.setEnabled(!muted); 360 } 361 362 private boolean isExpanded() { 363 return mMoreButton.getVisibility() != View.VISIBLE; 364 } 365 366 private void expand() { 367 final int count = mSliderGroup.getChildCount(); 368 for (int i = 0; i < count; i++) { 369 mSliderGroup.getChildAt(i).setVisibility(View.VISIBLE); 370 } 371 mMoreButton.setVisibility(View.INVISIBLE); 372 mDivider.setVisibility(View.INVISIBLE); 373 } 374 375 private void collapse() { 376 mMoreButton.setVisibility(View.VISIBLE); 377 mDivider.setVisibility(View.VISIBLE); 378 final int count = mSliderGroup.getChildCount(); 379 for (int i = 1; i < count; i++) { 380 mSliderGroup.getChildAt(i).setVisibility(View.GONE); 381 } 382 } 383 384 private void updateStates() { 385 final int count = mSliderGroup.getChildCount(); 386 for (int i = 0; i < count; i++) { 387 StreamControl sc = (StreamControl) mSliderGroup.getChildAt(i).getTag(); 388 updateSlider(sc); 389 } 390 } 391 392 public void postVolumeChanged(int streamType, int flags) { 393 if (hasMessages(MSG_VOLUME_CHANGED)) return; 394 if (mStreamControls == null) { 395 createSliders(); 396 } 397 removeMessages(MSG_FREE_RESOURCES); 398 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget(); 399 } 400 401 /** 402 * Override this if you have other work to do when the volume changes (for 403 * example, vibrating, playing a sound, etc.). Make sure to call through to 404 * the superclass implementation. 405 */ 406 protected void onVolumeChanged(int streamType, int flags) { 407 408 if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")"); 409 410 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { 411 if (mActiveStreamType == -1) { 412 reorderSliders(streamType); 413 } 414 onShowVolumeChanged(streamType, flags); 415 } 416 417 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) { 418 removeMessages(MSG_PLAY_SOUND); 419 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY); 420 } 421 422 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) { 423 removeMessages(MSG_PLAY_SOUND); 424 removeMessages(MSG_VIBRATE); 425 onStopSounds(); 426 } 427 428 removeMessages(MSG_FREE_RESOURCES); 429 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY); 430 431 resetTimeout(); 432 } 433 434 protected void onShowVolumeChanged(int streamType, int flags) { 435 int index = mAudioService.isStreamMute(streamType) ? 436 mAudioService.getLastAudibleStreamVolume(streamType) 437 : mAudioService.getStreamVolume(streamType); 438 439 // int message = UNKNOWN_VOLUME_TEXT; 440 // int additionalMessage = 0; 441 mRingIsSilent = false; 442 443 if (LOGD) { 444 Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType 445 + ", flags: " + flags + "), index: " + index); 446 } 447 448 // get max volume for progress bar 449 450 int max = mAudioService.getStreamMaxVolume(streamType); 451 452 switch (streamType) { 453 454 case AudioManager.STREAM_RING: { 455 // setRingerIcon(); 456 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri( 457 mContext, RingtoneManager.TYPE_RINGTONE); 458 if (ringuri == null) { 459 mRingIsSilent = true; 460 } 461 break; 462 } 463 464 case AudioManager.STREAM_MUSIC: { 465 // Special case for when Bluetooth is active for music 466 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) & 467 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP | 468 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | 469 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) { 470 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute); 471 } else { 472 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute); 473 } 474 break; 475 } 476 477 case AudioManager.STREAM_VOICE_CALL: { 478 /* 479 * For in-call voice call volume, there is no inaudible volume. 480 * Rescale the UI control so the progress bar doesn't go all 481 * the way to zero and don't show the mute icon. 482 */ 483 index++; 484 max++; 485 break; 486 } 487 488 case AudioManager.STREAM_ALARM: { 489 break; 490 } 491 492 case AudioManager.STREAM_NOTIFICATION: { 493 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri( 494 mContext, RingtoneManager.TYPE_NOTIFICATION); 495 if (ringuri == null) { 496 mRingIsSilent = true; 497 } 498 break; 499 } 500 501 case AudioManager.STREAM_BLUETOOTH_SCO: { 502 /* 503 * For in-call voice call volume, there is no inaudible volume. 504 * Rescale the UI control so the progress bar doesn't go all 505 * the way to zero and don't show the mute icon. 506 */ 507 index++; 508 max++; 509 break; 510 } 511 } 512 513 StreamControl sc = mStreamControls.get(streamType); 514 if (sc != null) { 515 if (sc.seekbarView.getMax() != max) { 516 sc.seekbarView.setMax(max); 517 } 518 sc.seekbarView.setProgress(index); 519 } 520 521 if (!mDialog.isShowing()) { 522 mAudioManager.forceVolumeControlStream(streamType); 523 mDialog.setContentView(mView); 524 // Showing dialog - use collapsed state 525 if (mShowCombinedVolumes) { 526 collapse(); 527 } 528 mDialog.show(); 529 } 530 531 // Do a little vibrate if applicable (only when going into vibrate mode) 532 if ((flags & AudioManager.FLAG_VIBRATE) != 0 && 533 mAudioService.isStreamAffectedByRingerMode(streamType) && 534 mAudioService.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE && 535 mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) { 536 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY); 537 } 538 } 539 540 protected void onPlaySound(int streamType, int flags) { 541 542 if (hasMessages(MSG_STOP_SOUNDS)) { 543 removeMessages(MSG_STOP_SOUNDS); 544 // Force stop right now 545 onStopSounds(); 546 } 547 548 synchronized (this) { 549 ToneGenerator toneGen = getOrCreateToneGenerator(streamType); 550 if (toneGen != null) { 551 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP); 552 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION); 553 } 554 } 555 } 556 557 protected void onStopSounds() { 558 559 synchronized (this) { 560 int numStreamTypes = AudioSystem.getNumStreamTypes(); 561 for (int i = numStreamTypes - 1; i >= 0; i--) { 562 ToneGenerator toneGen = mToneGenerators[i]; 563 if (toneGen != null) { 564 toneGen.stopTone(); 565 } 566 } 567 } 568 } 569 570 protected void onVibrate() { 571 572 // Make sure we ended up in vibrate ringer mode 573 if (mAudioService.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) { 574 return; 575 } 576 577 mVibrator.vibrate(VIBRATE_DURATION); 578 } 579 580 /** 581 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator. 582 */ 583 private ToneGenerator getOrCreateToneGenerator(int streamType) { 584 synchronized (this) { 585 if (mToneGenerators[streamType] == null) { 586 try { 587 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME); 588 } catch (RuntimeException e) { 589 if (LOGD) { 590 Log.d(TAG, "ToneGenerator constructor failed with " 591 + "RuntimeException: " + e); 592 } 593 } 594 } 595 return mToneGenerators[streamType]; 596 } 597 } 598 599 600 /** 601 * Switch between icons because Bluetooth music is same as music volume, but with 602 * different icons. 603 */ 604 private void setMusicIcon(int resId, int resMuteId) { 605 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC); 606 if (sc != null) { 607 sc.iconRes = resId; 608 sc.iconMuteRes = resMuteId; 609 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes); 610 } 611 } 612 613 protected void onFreeResources() { 614 synchronized (this) { 615 for (int i = mToneGenerators.length - 1; i >= 0; i--) { 616 if (mToneGenerators[i] != null) { 617 mToneGenerators[i].release(); 618 } 619 mToneGenerators[i] = null; 620 } 621 } 622 } 623 624 @Override 625 public void handleMessage(Message msg) { 626 switch (msg.what) { 627 628 case MSG_VOLUME_CHANGED: { 629 onVolumeChanged(msg.arg1, msg.arg2); 630 break; 631 } 632 633 case MSG_FREE_RESOURCES: { 634 onFreeResources(); 635 break; 636 } 637 638 case MSG_STOP_SOUNDS: { 639 onStopSounds(); 640 break; 641 } 642 643 case MSG_PLAY_SOUND: { 644 onPlaySound(msg.arg1, msg.arg2); 645 break; 646 } 647 648 case MSG_VIBRATE: { 649 onVibrate(); 650 break; 651 } 652 653 case MSG_TIMEOUT: { 654 if (mDialog.isShowing()) { 655 mDialog.dismiss(); 656 mActiveStreamType = -1; 657 } 658 break; 659 } 660 case MSG_RINGER_MODE_CHANGED: { 661 if (mDialog.isShowing()) { 662 updateStates(); 663 } 664 break; 665 } 666 } 667 } 668 669 private void resetTimeout() { 670 removeMessages(MSG_TIMEOUT); 671 sendMessageDelayed(obtainMessage(MSG_TIMEOUT), TIMEOUT_DELAY); 672 } 673 674 private void forceTimeout() { 675 removeMessages(MSG_TIMEOUT); 676 sendMessage(obtainMessage(MSG_TIMEOUT)); 677 } 678 679 public void onProgressChanged(SeekBar seekBar, int progress, 680 boolean fromUser) { 681 final Object tag = seekBar.getTag(); 682 if (fromUser && tag instanceof StreamControl) { 683 StreamControl sc = (StreamControl) tag; 684 if (mAudioManager.getStreamVolume(sc.streamType) != progress) { 685 mAudioManager.setStreamVolume(sc.streamType, progress, 0); 686 } 687 } 688 resetTimeout(); 689 } 690 691 public void onStartTrackingTouch(SeekBar seekBar) { 692 } 693 694 public void onStopTrackingTouch(SeekBar seekBar) { 695 } 696 697 public void onClick(View v) { 698 if (v == mMoreButton) { 699 expand(); 700 } else if (v.getTag() instanceof StreamControl) { 701 StreamControl sc = (StreamControl) v.getTag(); 702 boolean vibeInSilent = Settings.System.getInt(mContext.getContentResolver(), 703 System.VIBRATE_IN_SILENT, 1) == 1; 704 int newMode = mAudioManager.isSilentMode() 705 ? AudioManager.RINGER_MODE_NORMAL 706 : (vibeInSilent 707 ? AudioManager.RINGER_MODE_VIBRATE 708 : AudioManager.RINGER_MODE_SILENT); 709 mAudioManager.setRingerMode(newMode); 710 // Expand the dialog if it hasn't been expanded yet. 711 if (mShowCombinedVolumes && !isExpanded()) expand(); 712 } 713 resetTimeout(); 714 } 715 } 716