ActivityMusic.java revision f3b2798428ebb3e3895eb5106a4cb69c6bbc8181
1/* 2 * Copyright (C) 2010-2011 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 17package com.android.musicfx; 18 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.app.Dialog; 22import android.bluetooth.BluetoothClass; 23import android.bluetooth.BluetoothDevice; 24import android.content.BroadcastReceiver; 25import android.content.Context; 26import android.content.DialogInterface; 27import android.content.DialogInterface.OnCancelListener; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.media.AudioManager; 31import android.media.audiofx.AudioEffect; 32import android.media.audiofx.AudioEffect.Descriptor; 33import android.os.Bundle; 34import android.util.Log; 35import android.view.Gravity; 36import android.view.LayoutInflater; 37import android.view.Menu; 38import android.view.MenuInflater; 39import android.view.MenuItem; 40import android.view.MotionEvent; 41import android.view.View; 42import android.view.View.OnClickListener; 43import android.view.View.OnTouchListener; 44import android.view.ViewGroup; 45import android.widget.AbsListView; 46import android.widget.CheckBox; 47import android.widget.CompoundButton; 48import android.widget.CompoundButton.OnCheckedChangeListener; 49import android.widget.LinearLayout; 50import android.widget.ListView; 51import android.widget.RelativeLayout; 52import com.android.musicfx.seekbar.SeekBar; 53import com.android.musicfx.seekbar.SeekBar.OnSeekBarChangeListener; 54import android.widget.TextView; 55import android.widget.Toast; 56 57import com.android.audiofx.OpenSLESConstants; 58 59import java.util.Formatter; 60import java.util.HashMap; 61import java.util.Locale; 62import java.util.Map; 63 64/** 65 * 66 */ 67public class ActivityMusic extends Activity implements OnSeekBarChangeListener { 68 private final static String TAG = "MusicFXActivityMusic"; 69 70 /** 71 * Max number of EQ bands supported 72 */ 73 private final static int EQUALIZER_MAX_BANDS = 32; 74 75 /** 76 * Dialog IDS 77 */ 78 static final int DIALOG_EQUALIZER = 0; 79 static final int DIALOG_PRESET_REVERB = 1; 80 static final int DIALOG_RESET_DEFAULTS = 2; 81 82 /** 83 * Indicates if Virtualizer effect is supported. 84 */ 85 private boolean mVirtualizerSupported; 86 /** 87 * Indicates if BassBoost effect is supported. 88 */ 89 private boolean mBassBoostSupported; 90 /** 91 * Indicates if Equalizer effect is supported. 92 */ 93 private boolean mEqualizerSupported; 94 /** 95 * Indicates if Preset Reverb effect is supported. 96 */ 97 private boolean mPresetReverbSupported; 98 99 // Equalizer fields 100 private final SeekBar[] mEqualizerSeekBar = new SeekBar[EQUALIZER_MAX_BANDS]; 101 private final TextView[] mEqualizerValueText = new TextView[EQUALIZER_MAX_BANDS]; 102 private int mNumberEqualizerBands; 103 private int mEqualizerMinBandLevel; 104 private int mEQPresetUserPos = 1; 105 private View mEqualizerView; 106 private int mEQPreset; 107 private int mEQPresetPrevious; 108 private int[] mEQPresetUserBandLevelsPrev; 109 110 private int mPRPreset; 111 112 private boolean mIsHeadsetOn = false; 113 114 private StringBuilder mFormatBuilder = new StringBuilder(); 115 private Formatter mFormatter = new Formatter(mFormatBuilder, Locale.getDefault()); 116 117 /** 118 * Mapping for the EQ widget ids per band 119 */ 120 private static final int[][] EQViewElementIds = { 121 { R.id.EQBand0TextView, R.id.EQBand0SeekBar, R.id.EQBand0Value }, 122 { R.id.EQBand1TextView, R.id.EQBand1SeekBar, R.id.EQBand1Value }, 123 { R.id.EQBand2TextView, R.id.EQBand2SeekBar, R.id.EQBand2Value }, 124 { R.id.EQBand3TextView, R.id.EQBand3SeekBar, R.id.EQBand3Value }, 125 { R.id.EQBand4TextView, R.id.EQBand4SeekBar, R.id.EQBand4Value }, 126 { R.id.EQBand5TextView, R.id.EQBand5SeekBar, R.id.EQBand5Value }, 127 { R.id.EQBand6TextView, R.id.EQBand6SeekBar, R.id.EQBand6Value }, 128 { R.id.EQBand7TextView, R.id.EQBand7SeekBar, R.id.EQBand7Value }, 129 { R.id.EQBand8TextView, R.id.EQBand8SeekBar, R.id.EQBand8Value }, 130 { R.id.EQBand9TextView, R.id.EQBand9SeekBar, R.id.EQBand9Value }, 131 { R.id.EQBand10TextView, R.id.EQBand10SeekBar, R.id.EQBand10Value }, 132 { R.id.EQBand11TextView, R.id.EQBand11SeekBar, R.id.EQBand11Value }, 133 { R.id.EQBand12TextView, R.id.EQBand12SeekBar, R.id.EQBand12Value }, 134 { R.id.EQBand13TextView, R.id.EQBand13SeekBar, R.id.EQBand13Value }, 135 { R.id.EQBand14TextView, R.id.EQBand14SeekBar, R.id.EQBand14Value }, 136 { R.id.EQBand15TextView, R.id.EQBand15SeekBar, R.id.EQBand15Value }, 137 { R.id.EQBand16TextView, R.id.EQBand16SeekBar, R.id.EQBand16Value }, 138 { R.id.EQBand17TextView, R.id.EQBand17SeekBar, R.id.EQBand17Value }, 139 { R.id.EQBand18TextView, R.id.EQBand18SeekBar, R.id.EQBand18Value }, 140 { R.id.EQBand19TextView, R.id.EQBand19SeekBar, R.id.EQBand19Value }, 141 { R.id.EQBand20TextView, R.id.EQBand20SeekBar, R.id.EQBand20Value }, 142 { R.id.EQBand21TextView, R.id.EQBand21SeekBar, R.id.EQBand21Value }, 143 { R.id.EQBand22TextView, R.id.EQBand22SeekBar, R.id.EQBand22Value }, 144 { R.id.EQBand23TextView, R.id.EQBand23SeekBar, R.id.EQBand23Value }, 145 { R.id.EQBand24TextView, R.id.EQBand24SeekBar, R.id.EQBand24Value }, 146 { R.id.EQBand25TextView, R.id.EQBand25SeekBar, R.id.EQBand25Value }, 147 { R.id.EQBand26TextView, R.id.EQBand26SeekBar, R.id.EQBand26Value }, 148 { R.id.EQBand27TextView, R.id.EQBand27SeekBar, R.id.EQBand27Value }, 149 { R.id.EQBand28TextView, R.id.EQBand28SeekBar, R.id.EQBand28Value }, 150 { R.id.EQBand29TextView, R.id.EQBand29SeekBar, R.id.EQBand29Value }, 151 { R.id.EQBand30TextView, R.id.EQBand30SeekBar, R.id.EQBand30Value }, 152 { R.id.EQBand31TextView, R.id.EQBand31SeekBar, R.id.EQBand31Value } }; 153 154 // Preset Reverb fields 155 /** 156 * Array containing the PR preset names. 157 */ 158 private static final String[] PRESETREVERBPRESETSTRINGS = { "None", "SmallRoom", "MediumRoom", 159 "LargeRoom", "MediumHall", "LargeHall", "Plate" }; 160 161 /** 162 * Context field 163 */ 164 private Context mContext; 165 166 /** 167 * Calling package name field 168 */ 169 private String mCallingPackageName = "empty"; 170 171 /** 172 * Audio session field 173 */ 174 private int mAudioSession = AudioEffect.ERROR_BAD_VALUE; 175 176 // Broadcast receiver to handle wired and Bluetooth A2dp headset events 177 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 178 @Override 179 public void onReceive(final Context context, final Intent intent) { 180 final String action = intent.getAction(); 181 final boolean isHeadsetOnPrev = mIsHeadsetOn; 182 final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 183 if (action.equals(Intent.ACTION_HEADSET_PLUG)) { 184 mIsHeadsetOn = (intent.getIntExtra("state", 0) == 1) 185 || audioManager.isBluetoothA2dpOn(); 186 } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { 187 final int deviceClass = ((BluetoothDevice) intent 188 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).getBluetoothClass() 189 .getDeviceClass(); 190 if ((deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES) 191 || (deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) { 192 mIsHeadsetOn = true; 193 } 194 } else if (action.equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) { 195 mIsHeadsetOn = audioManager.isBluetoothA2dpOn() || audioManager.isWiredHeadsetOn(); 196 } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { 197 final int deviceClass = ((BluetoothDevice) intent 198 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).getBluetoothClass() 199 .getDeviceClass(); 200 if ((deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES) 201 || (deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) { 202 mIsHeadsetOn = audioManager.isWiredHeadsetOn(); 203 } 204 } 205 if (isHeadsetOnPrev != mIsHeadsetOn) { 206 updateUIHeadset(); 207 } 208 } 209 }; 210 211 /* 212 * Declares and initializes all objects and widgets in the layouts and the CheckBox and SeekBar 213 * onchange methods on creation. 214 * 215 * (non-Javadoc) 216 * 217 * @see android.app.ActivityGroup#onCreate(android.os.Bundle) 218 */ 219 @Override 220 public void onCreate(final Bundle savedInstanceState) { 221 super.onCreate(savedInstanceState); 222 223 // Init context to be used in listeners 224 mContext = this; 225 226 // Receive intent 227 // get calling intent 228 final Intent intent = getIntent(); 229 mAudioSession = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION, 230 AudioEffect.ERROR_BAD_VALUE); 231 Log.v(TAG, "audio session: " + mAudioSession); 232 233 mCallingPackageName = getCallingPackage(); 234 235 // check for errors 236 if (mCallingPackageName == null) { 237 Log.e(TAG, "Package name is null"); 238 setResult(RESULT_CANCELED); 239 finish(); 240 return; 241 } 242 setResult(RESULT_OK); 243 244 Log.v(TAG, mCallingPackageName + " (" + mAudioSession + ")"); 245 246 ControlPanelEffect.initEffectsPreferences(mContext, mCallingPackageName, mAudioSession); 247 248 // query available effects 249 final Descriptor[] effects = AudioEffect.queryEffects(); 250 251 // Determine available/supported effects 252 Log.v(TAG, "Available effects:"); 253 for (final Descriptor effect : effects) { 254 Log.v(TAG, effect.name.toString() + ", type: " + effect.type.toString()); 255 256 if (effect.type.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) { 257 mVirtualizerSupported = true; 258 } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) { 259 mBassBoostSupported = true; 260 } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) { 261 mEqualizerSupported = true; 262 } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_PRESET_REVERB)) { 263 mPresetReverbSupported = true; 264 } 265 } 266 267 setContentView(R.layout.music_main); 268 final ViewGroup viewGroup = (ViewGroup) findViewById(R.id.contentSoundEffects); 269 final View mainToggleView = findViewById(R.id.mainToggleEffectsLayout); 270 271 // Watch for button clicks and initialization. 272 if ((mVirtualizerSupported) || (mBassBoostSupported) || (mEqualizerSupported) 273 || (mPresetReverbSupported)) { 274 // Set the listener for the main enhancements toggle button. 275 // Depending on the state enable the supported effects if they were 276 // checked in the setup tab. 277 final CompoundButton toggleEffects = (CompoundButton) findViewById(R.id.mainToggleEffectsCheckBox); 278 toggleEffects.setOnCheckedChangeListener(new OnCheckedChangeListener() { 279 @Override 280 public void onCheckedChanged(final CompoundButton buttonView, 281 final boolean isChecked) { 282 283 // set parameter and state 284 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 285 mAudioSession, ControlPanelEffect.Key.global_enabled, isChecked); 286 // Enable Linear layout (in scroll layout) view with all 287 // effect contents depending on checked state 288 setEnabledAllChilds(viewGroup, isChecked); 289 // update UI according to headset state 290 updateUIHeadset(); 291 } 292 }); 293 294 mainToggleView.setVisibility(View.VISIBLE); 295 296 // Initialize the Virtualizer elements. 297 // Set the SeekBar listener. 298 if (mVirtualizerSupported) { 299 // Show msg when disabled slider (layout) is touched 300 findViewById(R.id.vILayout).setOnTouchListener(new OnTouchListener() { 301 302 @Override 303 public boolean onTouch(final View v, final MotionEvent event) { 304 showHeadsetMsg(); 305 return false; 306 } 307 }); 308 309 final SeekBar seekbar = (SeekBar) findViewById(R.id.vIStrengthSeekBar); 310 seekbar.setMax(OpenSLESConstants.VIRTUALIZER_MAX_STRENGTH 311 - OpenSLESConstants.VIRTUALIZER_MIN_STRENGTH); 312 313 seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 314 // Update the parameters while SeekBar changes and set the 315 // effect parameter. 316 317 @Override 318 public void onProgressChanged(final SeekBar seekBar, final int progress, 319 final boolean fromUser) { 320 // set parameter and state 321 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, 322 mAudioSession, ControlPanelEffect.Key.virt_strength, progress); 323 } 324 325 // If slider pos was 0 when starting re-enable effect 326 @Override 327 public void onStartTrackingTouch(final SeekBar seekBar) { 328 if (seekBar.getProgress() == 0) { 329 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 330 mAudioSession, ControlPanelEffect.Key.virt_enabled, true); 331 } 332 } 333 334 // If slider pos = 0 when stopping disable effect 335 @Override 336 public void onStopTrackingTouch(final SeekBar seekBar) { 337 if (seekBar.getProgress() == 0) { 338 // disable 339 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 340 mAudioSession, ControlPanelEffect.Key.virt_enabled, false); 341 } 342 } 343 }); 344 } 345 346 // Initialize the Bass Boost elements. 347 // Set the SeekBar listener. 348 if (mBassBoostSupported) { 349 // Show msg when disabled slider (layout) is touched 350 findViewById(R.id.bBLayout).setOnTouchListener(new OnTouchListener() { 351 352 @Override 353 public boolean onTouch(final View v, final MotionEvent event) { 354 showHeadsetMsg(); 355 return false; 356 } 357 }); 358 359 final SeekBar seekbar = (SeekBar) findViewById(R.id.bBStrengthSeekBar); 360 seekbar.setMax(OpenSLESConstants.BASSBOOST_MAX_STRENGTH 361 - OpenSLESConstants.BASSBOOST_MIN_STRENGTH); 362 363 seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 364 // Update the parameters while SeekBar changes and set the 365 // effect parameter. 366 367 @Override 368 public void onProgressChanged(final SeekBar seekBar, final int progress, 369 final boolean fromUser) { 370 // set parameter and state 371 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, 372 mAudioSession, ControlPanelEffect.Key.bb_strength, progress); 373 } 374 375 // If slider pos was 0 when starting re-enable effect 376 @Override 377 public void onStartTrackingTouch(final SeekBar seekBar) { 378 if (seekBar.getProgress() == 0) { 379 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 380 mAudioSession, ControlPanelEffect.Key.bb_enabled, true); 381 } 382 } 383 384 // If slider pos = 0 when stopping disable effect 385 @Override 386 public void onStopTrackingTouch(final SeekBar seekBar) { 387 if (seekBar.getProgress() == 0) { 388 // disable 389 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 390 mAudioSession, ControlPanelEffect.Key.bb_enabled, false); 391 } 392 393 } 394 }); 395 } 396 397 // Initialize the Equalizer elements. 398 if (mEqualizerSupported) { 399 final View view = findViewById(R.id.eqLayout); 400 view.setOnClickListener(new OnClickListener() { 401 402 @Override 403 public void onClick(final View v) { 404 showDialog(DIALOG_EQUALIZER); 405 } 406 }); 407 } 408 409 // Initialize the Preset Reverb elements. 410 // Set Spinner listeners. 411 if (mPresetReverbSupported) { 412 final View view = findViewById(R.id.eRLayout); 413 view.setOnClickListener(new OnClickListener() { 414 415 @Override 416 public void onClick(final View v) { 417 showDialog(DIALOG_PRESET_REVERB); 418 } 419 }); 420 } 421 422 // init reset defaults 423 final View view = findViewById(R.id.resetDefaultsLayout); 424 view.setOnClickListener(new OnClickListener() { 425 426 @Override 427 public void onClick(final View v) { 428 showDialog(DIALOG_RESET_DEFAULTS); 429 } 430 }); 431 } else { 432 viewGroup.setVisibility(View.GONE); 433 mainToggleView.setVisibility(View.GONE); 434 ((TextView) findViewById(R.id.noEffectsTextView)).setVisibility(View.VISIBLE); 435 } 436 } 437 438 /* 439 * (non-Javadoc) 440 * 441 * @see android.app.Activity#onResume() 442 */ 443 @Override 444 protected void onResume() { 445 super.onResume(); 446 if ((mVirtualizerSupported) || (mBassBoostSupported) || (mEqualizerSupported) 447 || (mPresetReverbSupported)) { 448 // Listen for broadcast intents that might affect the onscreen UI for headset. 449 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); 450 intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); 451 intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); 452 intentFilter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY); 453 registerReceiver(mReceiver, intentFilter); 454 455 // Check if wired or Bluetooth headset is connected/on 456 final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 457 mIsHeadsetOn = (audioManager.isWiredHeadsetOn() || audioManager.isBluetoothA2dpOn()); 458 Log.v(TAG, "onResume: mIsHeadsetOn : " + mIsHeadsetOn); 459 460 // Update UI 461 updateUI(); 462 } 463 } 464 465 /* 466 * (non-Javadoc) 467 * 468 * @see android.app.Activity#onPause() 469 */ 470 @Override 471 protected void onPause() { 472 super.onPause(); 473 474 // Unregister for broadcast intents. (These affect the visible UI, 475 // so we only care about them while we're in the foreground.) 476 unregisterReceiver(mReceiver); 477 } 478 479 /* 480 * Create dialogs for about, EQ preset control, PR and reset to default (alert) dialogs 481 * 482 * (non-Javadoc) 483 * 484 * @see android.app.Activity#onCreateDialog(int) 485 */ 486 @Override 487 protected Dialog onCreateDialog(final int id) { 488 final AlertDialog alertDialog; 489 switch (id) { 490 case DIALOG_EQUALIZER: { 491 final AlertDialog.Builder builder = new AlertDialog.Builder(this); 492 builder.setTitle(R.string.eq_dialog_title); 493 builder.setSingleChoiceItems(getEQPresetStrings(), -1, 494 new DialogInterface.OnClickListener() { 495 @Override 496 public void onClick(final DialogInterface dialog, final int item) { 497 if (item != mEQPresetPrevious) { 498 equalizerSetPreset(item); 499 final ListView listView = ((AlertDialog) dialog).getListView(); 500 // For the user preset, where EQ sliders need to be shown at the 501 // bottom of the list when selected, the footer view of a list is 502 // used to display them in. 503 // This footer view will then be added or removed from the list 504 // depending on whether user preset is selected or not. 505 // Using transcript mode to scroll to bottom when in user 506 if (!isEqualizerUserPreset(item)) { 507 listView.removeFooterView(mEqualizerView); 508 listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_DISABLED); 509 } else { 510 listView.addFooterView(mEqualizerView); 511 listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL); 512 } 513 } 514 mEQPresetPrevious = item; 515 } 516 }); 517 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 518 @Override 519 public void onClick(final DialogInterface dialog, final int whichButton) { 520 final ListView listView = ((AlertDialog) dialog).getListView(); 521 final int newPreset = listView.getCheckedItemPosition(); 522 equalizerSetPreset(newPreset); 523 ((TextView) findViewById(R.id.eqPresetsTitleTextView)) 524 .setText(getString(R.string.eq_title) + " " 525 + listView.getItemAtPosition(newPreset).toString()); 526 } 527 }); 528 builder.setNegativeButton(android.R.string.cancel, 529 new DialogInterface.OnClickListener() { 530 @Override 531 public void onClick(final DialogInterface dialog, final int whichButton) { 532 dialog.cancel(); 533 } 534 }); 535 builder.setOnCancelListener(new OnCancelListener() { 536 @Override 537 public void onCancel(final DialogInterface dialog) { 538 if (!isEqualizerUserPreset(mEQPreset)) { 539 final ListView listView = ((AlertDialog) dialog).getListView(); 540 listView.removeFooterView(mEqualizerView); 541 } 542 equalizerSetPreset(mEQPreset); 543 final int[] presetUserBandLevels = ControlPanelEffect.getParameterIntArray( 544 mContext, mCallingPackageName, mAudioSession, 545 ControlPanelEffect.Key.eq_preset_user_band_level); 546 short band = 0; 547 for (final int bandLevel : mEQPresetUserBandLevelsPrev) { 548 if (bandLevel != presetUserBandLevels[band]) { 549 if (!isEqualizerUserPreset(mEQPreset)) { 550 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, 551 mAudioSession, 552 ControlPanelEffect.Key.eq_preset_user_band_level, 553 bandLevel, band); 554 } else { 555 equalizerBandUpdate(band, (short) bandLevel); 556 } 557 } 558 band++; 559 } 560 } 561 }); 562 563 alertDialog = builder.create(); 564 final LayoutInflater factory = LayoutInflater.from(this); 565 mEqualizerView = factory.inflate(R.layout.music_eq, null); 566 equalizerInit(); 567 final ListView listView = alertDialog.getListView(); 568 // Add empty footer view 569 listView.addFooterView(mEqualizerView); 570 571 mEQPresetUserPos = getEQPresetStrings().length - 1; 572 break; 573 } 574 case DIALOG_PRESET_REVERB: { 575 final AlertDialog.Builder builder = new AlertDialog.Builder(this); 576 builder.setTitle(R.string.pr_dialog_title); 577 builder.setSingleChoiceItems(PRESETREVERBPRESETSTRINGS, -1, 578 new DialogInterface.OnClickListener() { 579 @Override 580 public void onClick(final DialogInterface dialog, final int item) { 581 presetReverbSetPreset(item); 582 } 583 }); 584 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 585 @Override 586 public void onClick(final DialogInterface dialog, final int whichButton) { 587 final ListView listView = ((AlertDialog) dialog).getListView(); 588 final int newPreset = listView.getCheckedItemPosition(); 589 presetReverbSetPreset(newPreset); 590 ((TextView) findViewById(R.id.eRPresetsTitleTextView)) 591 .setText(getString(R.string.pr_title) + " " 592 + listView.getItemAtPosition(newPreset).toString()); 593 } 594 }); 595 builder.setNegativeButton(android.R.string.cancel, 596 new DialogInterface.OnClickListener() { 597 @Override 598 public void onClick(final DialogInterface dialog, final int whichButton) { 599 dialog.cancel(); 600 } 601 }); 602 builder.setOnCancelListener(new OnCancelListener() { 603 @Override 604 public void onCancel(final DialogInterface dialog) { 605 presetReverbSetPreset(mPRPreset); 606 } 607 }); 608 609 alertDialog = builder.create(); 610 break; 611 } 612 case DIALOG_RESET_DEFAULTS: { 613 final AlertDialog.Builder builder = new AlertDialog.Builder(this); 614 builder.setTitle(R.string.reset_defaults_dialog_title); 615 builder.setMessage(getString(R.string.reset_defaults_dialog_message)); 616 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 617 @Override 618 public void onClick(final DialogInterface dialog, final int whichButton) { 619 ControlPanelEffect.setEffectDefaults(mContext, mCallingPackageName, 620 mAudioSession); 621 updateUI(); 622 } 623 }); 624 builder.setNegativeButton(android.R.string.cancel, null); 625 alertDialog = builder.create(); 626 break; 627 } 628 default: 629 Log.e(TAG, "onCreateDialog invalid Dialog id: " + id); 630 alertDialog = null; 631 break; 632 } 633 return alertDialog; 634 } 635 636 /* 637 * Updates dialog (selections) before they are shown if necessary 638 * 639 * (non-Javadoc) 640 * 641 * @see android.app.Activity#onPrepareDialog(int, android.app.Dialog, android.os.Bundle) 642 */ 643 @Override 644 protected void onPrepareDialog(final int id, final Dialog dialog, final Bundle args) { 645 switch (id) { 646 case DIALOG_EQUALIZER: { 647 mEQPreset = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 648 mAudioSession, ControlPanelEffect.Key.eq_current_preset); 649 mEQPresetPrevious = mEQPreset; 650 mEQPresetUserBandLevelsPrev = ControlPanelEffect.getParameterIntArray(mContext, 651 mCallingPackageName, mAudioSession, 652 ControlPanelEffect.Key.eq_preset_user_band_level); 653 final ListView listView = ((AlertDialog) dialog).getListView(); 654 listView.setItemChecked(mEQPreset, true); 655 listView.setSelection(mEQPreset); 656 657 if (isEqualizerUserPreset(mEQPreset)) { 658 if (listView.getFooterViewsCount() == 0) { 659 listView.addFooterView(mEqualizerView); 660 listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL); 661 } 662 equalizerUpdateDisplay(); 663 } else { 664 // FIXME: because of a probable bug in Android removeFooterView, we need to catch 665 // NPE which is sometimes thrown from inside removeFooterView (encountered only in 666 // Honeycomb). 667 // Should ideally be be avoided otherwise. 668 try { 669 listView.removeFooterView(mEqualizerView); 670 listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_DISABLED); 671 } catch (final NullPointerException e) { 672 Log.w(TAG, "onPrepareDialog: DIALOG_EQUALIZER: removeFooterView: " + e); 673 } 674 } 675 676 break; 677 } 678 case DIALOG_PRESET_REVERB: { 679 mPRPreset = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 680 mAudioSession, ControlPanelEffect.Key.pr_current_preset); 681 final ListView listView = ((AlertDialog) dialog).getListView(); 682 listView.setItemChecked(mPRPreset, true); 683 listView.setSelection(mPRPreset); 684 break; 685 } 686 case DIALOG_RESET_DEFAULTS: { 687 break; 688 } 689 default: 690 Log.e(TAG, "onPrepareDialog invalid Dialog id: " + id); 691 break; 692 } 693 } 694 695 /** 696 * En/disables all childs for a given view. For linear and relative layout childs do this 697 * recursively 698 * 699 * @param view 700 * @param enabled 701 */ 702 private void setEnabledAllChilds(final ViewGroup viewGroup, final boolean enabled) { 703 final int count = viewGroup.getChildCount(); 704 for (int i = 0; i < count; i++) { 705 final View view = viewGroup.getChildAt(i); 706 if ((view instanceof LinearLayout) || (view instanceof RelativeLayout)) { 707 final ViewGroup vg = (ViewGroup) view; 708 setEnabledAllChilds(vg, enabled); 709 } 710 view.setEnabled(enabled); 711 } 712 } 713 714 /** 715 * Updates UI (checkbox, seekbars, enabled states) according to the current stored preferences. 716 */ 717 private void updateUI() { 718 final boolean isEnabled = ControlPanelEffect.getParameterBoolean(mContext, 719 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.global_enabled); 720 ((CompoundButton) findViewById(R.id.mainToggleEffectsCheckBox)).setChecked(isEnabled); 721 setEnabledAllChilds((ViewGroup) findViewById(R.id.contentSoundEffects), isEnabled); 722 updateUIHeadset(); 723 724 if (mVirtualizerSupported) { 725 ((SeekBar) findViewById(R.id.vIStrengthSeekBar)).setProgress(ControlPanelEffect 726 .getParameterInt(mContext, mCallingPackageName, mAudioSession, 727 ControlPanelEffect.Key.virt_strength)); 728 } 729 if (mBassBoostSupported) { 730 ((SeekBar) findViewById(R.id.bBStrengthSeekBar)).setProgress(ControlPanelEffect 731 .getParameterInt(mContext, mCallingPackageName, mAudioSession, 732 ControlPanelEffect.Key.bb_strength)); 733 } 734 if (mEqualizerSupported) { 735 String [] presets = getEQPresetStrings(); 736 int idx = ControlPanelEffect.getParameterInt(mContext, 737 mCallingPackageName, mAudioSession, 738 ControlPanelEffect.Key.eq_current_preset); 739 if (idx >= presets.length) { 740 idx = 0; 741 } 742 ((TextView) findViewById(R.id.eqPresetsTitleTextView)) 743 .setText(getString(R.string.eq_title) 744 + " " 745 + presets[idx]); 746 } 747 if (mPresetReverbSupported) { 748 ((TextView) findViewById(R.id.eRPresetsTitleTextView)) 749 .setText(getString(R.string.pr_title) 750 + " " 751 + PRESETREVERBPRESETSTRINGS[ControlPanelEffect.getParameterInt( 752 mContext, mCallingPackageName, mAudioSession, 753 ControlPanelEffect.Key.pr_current_preset)]); 754 } 755 } 756 757 /** 758 * Updates UI for headset mode. En/disable VI and BB controls depending on headset state 759 * (on/off) if effects are on. Do the inverse for their layouts so they can take over 760 * control/events. 761 */ 762 private void updateUIHeadset() { 763 if (((CompoundButton) findViewById(R.id.mainToggleEffectsCheckBox)).isChecked()) { 764 ((TextView) findViewById(R.id.vIStrengthText)).setEnabled(mIsHeadsetOn); 765 ((SeekBar) findViewById(R.id.vIStrengthSeekBar)).setEnabled(mIsHeadsetOn); 766 findViewById(R.id.vILayout).setEnabled(!mIsHeadsetOn); 767 ((TextView) findViewById(R.id.bBStrengthText)).setEnabled(mIsHeadsetOn); 768 ((SeekBar) findViewById(R.id.bBStrengthSeekBar)).setEnabled(mIsHeadsetOn); 769 findViewById(R.id.bBLayout).setEnabled(!mIsHeadsetOn); 770 } 771 } 772 773 /** 774 * Initializes the equalizer elements. Set the SeekBars and Spinner listeners. 775 */ 776 private void equalizerInit() { 777 // Initialize the N-Band Equalizer elements. 778 mNumberEqualizerBands = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 779 mAudioSession, ControlPanelEffect.Key.eq_num_bands); 780 mEQPresetUserBandLevelsPrev = ControlPanelEffect.getParameterIntArray(mContext, 781 mCallingPackageName, mAudioSession, 782 ControlPanelEffect.Key.eq_preset_user_band_level); 783 final int[] centerFreqs = ControlPanelEffect.getParameterIntArray(mContext, 784 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_center_freq); 785 final int[] bandLevelRange = ControlPanelEffect.getParameterIntArray(mContext, 786 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_level_range); 787 mEqualizerMinBandLevel = bandLevelRange[0]; 788 final int mEqualizerMaxBandLevel = bandLevelRange[1]; 789 790 for (int band = 0; band < mNumberEqualizerBands; band++) { 791 // Unit conversion from mHz to Hz and use k prefix if necessary to display 792 final int centerFreq = centerFreqs[band] / 1000; 793 float centerFreqHz = centerFreq; 794 String unitPrefix = ""; 795 if (centerFreqHz >= 1000) { 796 centerFreqHz = centerFreqHz / 1000; 797 unitPrefix = "k"; 798 } 799 ((TextView) mEqualizerView.findViewById(EQViewElementIds[band][0])).setText( 800 format("%.0f ", centerFreqHz) + unitPrefix + "Hz"); 801 mEqualizerSeekBar[band] = (SeekBar) mEqualizerView 802 .findViewById(EQViewElementIds[band][1]); 803 mEqualizerSeekBar[band].setMax(mEqualizerMaxBandLevel - mEqualizerMinBandLevel); 804 mEqualizerValueText[band] = (TextView) mEqualizerView 805 .findViewById(EQViewElementIds[band][2]); 806 mEqualizerSeekBar[band].setOnSeekBarChangeListener(this); 807 } 808 809 // Hide the inactive Equalizer bands. 810 for (int band = mNumberEqualizerBands; band < EQUALIZER_MAX_BANDS; band++) { 811 // CenterFreq text 812 mEqualizerView.findViewById(EQViewElementIds[band][0]).setVisibility(View.GONE); 813 // SeekBar 814 mEqualizerView.findViewById(EQViewElementIds[band][1]).setVisibility(View.GONE); 815 // Value text 816 mEqualizerView.findViewById(EQViewElementIds[band][2]).setVisibility(View.GONE); 817 } 818 } 819 820 private String format(String format, Object... args) { 821 mFormatBuilder.setLength(0); 822 mFormatter.format(format, args); 823 return mFormatBuilder.toString(); 824 } 825 826 /* 827 * For the EQ Band SeekBars 828 * 829 * (non-Javadoc) 830 * 831 * @see android.widget.SeekBar.OnSeekBarChangeListener#onProgressChanged(android 832 * .widget.SeekBar, int, boolean) 833 */ 834 835 @Override 836 public void onProgressChanged(final SeekBar seekbar, final int progress, final boolean fromUser) { 837 final int id = seekbar.getId(); 838 839 for (short band = 0; band < mNumberEqualizerBands; band++) { 840 if (id == EQViewElementIds[band][1]) { 841 final short level = (short) (progress + mEqualizerMinBandLevel); 842 equalizerBandUpdateDisplay(band, level); 843 if (fromUser) { 844 equalizerBandUpdate(band, level); 845 } 846 break; 847 } 848 } 849 } 850 851 /* 852 * (non-Javadoc) 853 * 854 * @see android.widget.SeekBar.OnSeekBarChangeListener#onStartTrackingTouch(android 855 * .widget.SeekBar) 856 */ 857 858 @Override 859 public void onStartTrackingTouch(final SeekBar seekbar) { 860 // Do nothing 861 } 862 863 /* 864 * Updates the EQ display when the user stops changing. 865 * 866 * (non-Javadoc) 867 * 868 * @see android.widget.SeekBar.OnSeekBarChangeListener#onStopTrackingTouch(android 869 * .widget.SeekBar) 870 */ 871 872 @Override 873 public void onStopTrackingTouch(final SeekBar seekbar) { 874 equalizerUpdateDisplay(); 875 } 876 877 /** 878 * Updates the EQ by getting the parameters. 879 */ 880 private void equalizerUpdateDisplay() { 881 // Update and show the active N-Band Equalizer bands. 882 final int[] bandLevels = ControlPanelEffect.getParameterIntArray(mContext, 883 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_band_level); 884 for (short band = 0; band < mNumberEqualizerBands; band++) { 885 final int level = bandLevels[band]; 886 equalizerBandUpdateDisplay(band, (short) level); 887 final int progress = level - mEqualizerMinBandLevel; 888 mEqualizerSeekBar[band].setProgress(progress); 889 } 890 } 891 892 /** 893 * Updates/sets a given EQ band level. 894 * 895 * @param band 896 * Band id 897 * @param level 898 * EQ band level 899 */ 900 private void equalizerBandUpdate(final short band, final short level) { 901 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession, 902 ControlPanelEffect.Key.eq_band_level, level, band); 903 } 904 905 /** 906 * Updates the EQ band level display. 907 * 908 * @param band 909 * Band id 910 * @param level 911 * EQ band level 912 */ 913 private void equalizerBandUpdateDisplay(final short band, final short level) { 914 final int dBValue = level / 100; 915 mEqualizerValueText[band].setText(format("%d dB", dBValue)); 916 } 917 918 /** 919 * Sets the given EQ preset. 920 * 921 * @param preset 922 * EQ preset id. 923 */ 924 private void equalizerSetPreset(final short preset) { 925 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession, 926 ControlPanelEffect.Key.eq_current_preset, preset); 927 if (isEqualizerUserPreset(preset)) { 928 equalizerUpdateDisplay(); 929 } 930 } 931 932 /** 933 * Sets the given EQ preset. 934 * 935 * @param preset 936 * EQ preset id. 937 */ 938 private void equalizerSetPreset(final int preset) { 939 equalizerSetPreset((short) preset); 940 } 941 942 /** 943 * Checks if an User EQ preset is set. 944 */ 945 private boolean isEqualizerUserPreset(final int preset) { 946 return (preset == mEQPresetUserPos); 947 } 948 949 /** 950 * Gets the EQ preset names 951 * 952 * @return 953 */ 954 private final String[] getEQPresetStrings() { 955 final int numPresets = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 956 mAudioSession, ControlPanelEffect.Key.eq_num_presets); 957 // Fill array with presets from AudioEffects call. 958 // allocate a space for 2 extra strings (CI Extreme & User) 959 final String[] eQViewPresetStrings = new String[numPresets + 2]; 960 for (short i = 0; i < numPresets; i++) { 961 eQViewPresetStrings[i] = ControlPanelEffect.getParameterString(mContext, 962 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_preset_name, i); 963 } 964 965 eQViewPresetStrings[numPresets] = getString(R.string.ci_extreme); 966 eQViewPresetStrings[numPresets + 1] = getString(R.string.user); 967 return eQViewPresetStrings; 968 } 969 970 /** 971 * Sets the given PR preset. 972 * 973 * @param preset 974 * PR preset id. 975 */ 976 private void presetReverbSetPreset(final short preset) { 977 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession, 978 ControlPanelEffect.Key.pr_current_preset, preset); 979 } 980 981 /** 982 * Sets the given PR preset. 983 * 984 * @param preset 985 * PR preset id. 986 */ 987 private void presetReverbSetPreset(final int preset) { 988 presetReverbSetPreset((short) preset); 989 } 990 991 /** 992 * Show msg that headset needs to be plugged. 993 */ 994 private void showHeadsetMsg() { 995 final Context context = getApplicationContext(); 996 final int duration = Toast.LENGTH_SHORT; 997 998 final Toast toast = Toast.makeText(context, getString(R.string.headset_plug), duration); 999 toast.setGravity(Gravity.CENTER, toast.getXOffset() / 2, toast.getYOffset() / 2); 1000 toast.show(); 1001 } 1002} 1003