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 17 package com.android.musicfx; 18 19 import com.android.audiofx.OpenSLESConstants; 20 21 import android.app.ActionBar; 22 import android.app.Activity; 23 import android.app.AlertDialog; 24 import android.app.Dialog; 25 import android.bluetooth.BluetoothClass; 26 import android.bluetooth.BluetoothDevice; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.DialogInterface; 30 import android.content.DialogInterface.OnCancelListener; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.media.AudioFormat; 34 import android.media.AudioManager; 35 import android.media.audiofx.AudioEffect; 36 import android.media.audiofx.AudioEffect.Descriptor; 37 import android.media.audiofx.Virtualizer; 38 import android.os.Bundle; 39 import android.util.Log; 40 import android.view.Gravity; 41 import android.view.LayoutInflater; 42 import android.view.MotionEvent; 43 import android.view.View; 44 import android.view.View.OnClickListener; 45 import android.view.View.OnTouchListener; 46 import android.view.ViewGroup; 47 import android.widget.AdapterView; 48 import android.widget.AdapterView.OnItemSelectedListener; 49 import android.widget.ArrayAdapter; 50 import android.widget.CompoundButton; 51 import android.widget.CompoundButton.OnCheckedChangeListener; 52 import android.widget.LinearLayout; 53 import android.widget.ListView; 54 import android.widget.RelativeLayout; 55 import android.widget.SeekBar; 56 import android.widget.SeekBar.OnSeekBarChangeListener; 57 import android.widget.Spinner; 58 import android.widget.Switch; 59 import android.widget.TextView; 60 import android.widget.Toast; 61 62 import java.util.Formatter; 63 import java.util.Locale; 64 import java.util.UUID; 65 66 /** 67 * 68 */ 69 public class ActivityMusic extends Activity implements OnSeekBarChangeListener { 70 private final static String TAG = "MusicFXActivityMusic"; 71 72 /** 73 * Max number of EQ bands supported 74 */ 75 private final static int EQUALIZER_MAX_BANDS = 32; 76 77 /** 78 * Max levels per EQ band in millibels (1 dB = 100 mB) 79 */ 80 private final static int EQUALIZER_MAX_LEVEL = 1000; 81 82 /** 83 * Min levels per EQ band in millibels (1 dB = 100 mB) 84 */ 85 private final static int EQUALIZER_MIN_LEVEL = -1000; 86 87 /** 88 * Indicates if Virtualizer effect is supported. 89 */ 90 private boolean mVirtualizerSupported; 91 private boolean mVirtualizerIsHeadphoneOnly; 92 /** 93 * Indicates if BassBoost effect is supported. 94 */ 95 private boolean mBassBoostSupported; 96 /** 97 * Indicates if Equalizer effect is supported. 98 */ 99 private boolean mEqualizerSupported; 100 /** 101 * Indicates if Preset Reverb effect is supported. 102 */ 103 private boolean mPresetReverbSupported; 104 105 // Equalizer fields 106 private final SeekBar[] mEqualizerSeekBar = new SeekBar[EQUALIZER_MAX_BANDS]; 107 private int mNumberEqualizerBands; 108 private int mEqualizerMinBandLevel; 109 private int mEQPresetUserPos = 1; 110 private int mEQPreset; 111 private int mEQPresetPrevious; 112 private int[] mEQPresetUserBandLevelsPrev; 113 private String[] mEQPresetNames; 114 115 private int mPRPreset; 116 private int mPRPresetPrevious; 117 118 private boolean mIsHeadsetOn = false; 119 private CompoundButton mToggleSwitch; 120 121 private StringBuilder mFormatBuilder = new StringBuilder(); 122 private Formatter mFormatter = new Formatter(mFormatBuilder, Locale.getDefault()); 123 124 /** 125 * Mapping for the EQ widget ids per band 126 */ 127 private static final int[][] EQViewElementIds = { 128 { R.id.EQBand0TextView, R.id.EQBand0SeekBar }, 129 { R.id.EQBand1TextView, R.id.EQBand1SeekBar }, 130 { R.id.EQBand2TextView, R.id.EQBand2SeekBar }, 131 { R.id.EQBand3TextView, R.id.EQBand3SeekBar }, 132 { R.id.EQBand4TextView, R.id.EQBand4SeekBar }, 133 { R.id.EQBand5TextView, R.id.EQBand5SeekBar }, 134 { R.id.EQBand6TextView, R.id.EQBand6SeekBar }, 135 { R.id.EQBand7TextView, R.id.EQBand7SeekBar }, 136 { R.id.EQBand8TextView, R.id.EQBand8SeekBar }, 137 { R.id.EQBand9TextView, R.id.EQBand9SeekBar }, 138 { R.id.EQBand10TextView, R.id.EQBand10SeekBar }, 139 { R.id.EQBand11TextView, R.id.EQBand11SeekBar }, 140 { R.id.EQBand12TextView, R.id.EQBand12SeekBar }, 141 { R.id.EQBand13TextView, R.id.EQBand13SeekBar }, 142 { R.id.EQBand14TextView, R.id.EQBand14SeekBar }, 143 { R.id.EQBand15TextView, R.id.EQBand15SeekBar }, 144 { R.id.EQBand16TextView, R.id.EQBand16SeekBar }, 145 { R.id.EQBand17TextView, R.id.EQBand17SeekBar }, 146 { R.id.EQBand18TextView, R.id.EQBand18SeekBar }, 147 { R.id.EQBand19TextView, R.id.EQBand19SeekBar }, 148 { R.id.EQBand20TextView, R.id.EQBand20SeekBar }, 149 { R.id.EQBand21TextView, R.id.EQBand21SeekBar }, 150 { R.id.EQBand22TextView, R.id.EQBand22SeekBar }, 151 { R.id.EQBand23TextView, R.id.EQBand23SeekBar }, 152 { R.id.EQBand24TextView, R.id.EQBand24SeekBar }, 153 { R.id.EQBand25TextView, R.id.EQBand25SeekBar }, 154 { R.id.EQBand26TextView, R.id.EQBand26SeekBar }, 155 { R.id.EQBand27TextView, R.id.EQBand27SeekBar }, 156 { R.id.EQBand28TextView, R.id.EQBand28SeekBar }, 157 { R.id.EQBand29TextView, R.id.EQBand29SeekBar }, 158 { R.id.EQBand30TextView, R.id.EQBand30SeekBar }, 159 { R.id.EQBand31TextView, R.id.EQBand31SeekBar } }; 160 161 // Preset Reverb fields 162 /** 163 * Array containing the PR preset names. 164 */ 165 private static final String[] PRESETREVERBPRESETSTRINGS = { "None", "SmallRoom", "MediumRoom", 166 "LargeRoom", "MediumHall", "LargeHall", "Plate" }; 167 168 /** 169 * Context field 170 */ 171 private Context mContext; 172 173 /** 174 * Calling package name field 175 */ 176 private String mCallingPackageName = "empty"; 177 178 /** 179 * Audio session field 180 */ 181 private int mAudioSession = AudioEffect.ERROR_BAD_VALUE; 182 183 // Broadcast receiver to handle wired and Bluetooth A2dp headset events 184 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 185 @Override 186 public void onReceive(final Context context, final Intent intent) { 187 final String action = intent.getAction(); 188 final boolean isHeadsetOnPrev = mIsHeadsetOn; 189 final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 190 if (action.equals(Intent.ACTION_HEADSET_PLUG)) { 191 mIsHeadsetOn = (intent.getIntExtra("state", 0) == 1) 192 || audioManager.isBluetoothA2dpOn(); 193 } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { 194 final int deviceClass = ((BluetoothDevice) intent 195 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).getBluetoothClass() 196 .getDeviceClass(); 197 if ((deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES) 198 || (deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) { 199 mIsHeadsetOn = true; 200 } 201 } else if (action.equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) { 202 mIsHeadsetOn = audioManager.isBluetoothA2dpOn() || audioManager.isWiredHeadsetOn(); 203 } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { 204 final int deviceClass = ((BluetoothDevice) intent 205 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).getBluetoothClass() 206 .getDeviceClass(); 207 if ((deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES) 208 || (deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) { 209 mIsHeadsetOn = audioManager.isWiredHeadsetOn(); 210 } 211 } 212 if (isHeadsetOnPrev != mIsHeadsetOn) { 213 updateUIHeadset(); 214 } 215 } 216 }; 217 218 /* 219 * Declares and initializes all objects and widgets in the layouts and the CheckBox and SeekBar 220 * onchange methods on creation. 221 * 222 * (non-Javadoc) 223 * 224 * @see android.app.ActivityGroup#onCreate(android.os.Bundle) 225 */ 226 @Override onCreate(final Bundle savedInstanceState)227 public void onCreate(final Bundle savedInstanceState) { 228 super.onCreate(savedInstanceState); 229 230 // Init context to be used in listeners 231 mContext = this; 232 233 // Receive intent 234 // get calling intent 235 final Intent intent = getIntent(); 236 mAudioSession = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION, 237 AudioEffect.ERROR_BAD_VALUE); 238 Log.v(TAG, "audio session: " + mAudioSession); 239 240 mCallingPackageName = getCallingPackage(); 241 242 // check for errors 243 if (mCallingPackageName == null) { 244 Log.e(TAG, "Package name is null"); 245 setResult(RESULT_CANCELED); 246 finish(); 247 return; 248 } 249 setResult(RESULT_OK); 250 251 Log.v(TAG, mCallingPackageName + " (" + mAudioSession + ")"); 252 253 ControlPanelEffect.initEffectsPreferences(mContext, mCallingPackageName, mAudioSession); 254 255 // query available effects 256 final Descriptor[] effects = AudioEffect.queryEffects(); 257 258 // Determine available/supported effects 259 Log.v(TAG, "Available effects:"); 260 for (final Descriptor effect : effects) { 261 Log.v(TAG, effect.name.toString() + ", type: " + effect.type.toString()); 262 263 if (effect.type.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) { 264 mVirtualizerSupported = true; 265 mVirtualizerIsHeadphoneOnly = !isVirtualizerTransauralSupported(); 266 } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) { 267 mBassBoostSupported = true; 268 } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) { 269 mEqualizerSupported = true; 270 } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_PRESET_REVERB)) { 271 mPresetReverbSupported = true; 272 } 273 } 274 275 setContentView(R.layout.music_main); 276 final ViewGroup viewGroup = (ViewGroup) findViewById(R.id.contentSoundEffects); 277 278 // Set accessibility label for bass boost and virtualizer strength seekbars. 279 findViewById(R.id.bBStrengthText).setLabelFor(R.id.bBStrengthSeekBar); 280 findViewById(R.id.vIStrengthText).setLabelFor(R.id.vIStrengthSeekBar); 281 282 // Fill array with presets from AudioEffects call. 283 // allocate a space for 2 extra strings (CI Extreme & User) 284 final int numPresets = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 285 mAudioSession, ControlPanelEffect.Key.eq_num_presets); 286 mEQPresetNames = new String[numPresets + 2]; 287 for (short i = 0; i < numPresets; i++) { 288 mEQPresetNames[i] = ControlPanelEffect.getParameterString(mContext, 289 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_preset_name, i); 290 } 291 mEQPresetNames[numPresets] = getString(R.string.ci_extreme); 292 mEQPresetNames[numPresets + 1] = getString(R.string.user); 293 mEQPresetUserPos = numPresets + 1; 294 295 // Watch for button clicks and initialization. 296 if ((mVirtualizerSupported) || (mBassBoostSupported) || (mEqualizerSupported) 297 || (mPresetReverbSupported)) { 298 // Set the listener for the main enhancements toggle button. 299 // Depending on the state enable the supported effects if they were 300 // checked in the setup tab. 301 mToggleSwitch = new Switch(this); 302 mToggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() { 303 @Override 304 public void onCheckedChanged(final CompoundButton buttonView, 305 final boolean isChecked) { 306 307 // set parameter and state 308 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 309 mAudioSession, ControlPanelEffect.Key.global_enabled, isChecked); 310 // Enable Linear layout (in scroll layout) view with all 311 // effect contents depending on checked state 312 setEnabledAllChildren(viewGroup, isChecked); 313 // update UI according to headset state 314 updateUIHeadset(); 315 } 316 }); 317 318 // Initialize the Virtualizer elements. 319 // Set the SeekBar listener. 320 if (mVirtualizerSupported) { 321 // Show msg when disabled slider (layout) is touched 322 findViewById(R.id.vILayout).setOnTouchListener(new OnTouchListener() { 323 324 @Override 325 public boolean onTouch(final View v, final MotionEvent event) { 326 if (event.getAction() == MotionEvent.ACTION_UP) { 327 showHeadsetMsg(); 328 } 329 return false; 330 } 331 }); 332 333 final SeekBar seekbar = (SeekBar) findViewById(R.id.vIStrengthSeekBar); 334 seekbar.setMax(OpenSLESConstants.VIRTUALIZER_MAX_STRENGTH 335 - OpenSLESConstants.VIRTUALIZER_MIN_STRENGTH); 336 337 seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 338 // Update the parameters while SeekBar changes and set the 339 // effect parameter. 340 341 @Override 342 public void onProgressChanged(final SeekBar seekBar, final int progress, 343 final boolean fromUser) { 344 // set parameter and state 345 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, 346 mAudioSession, ControlPanelEffect.Key.virt_strength, progress); 347 } 348 349 // If slider pos was 0 when starting re-enable effect 350 @Override 351 public void onStartTrackingTouch(final SeekBar seekBar) { 352 if (seekBar.getProgress() == 0) { 353 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 354 mAudioSession, ControlPanelEffect.Key.virt_enabled, true); 355 } 356 } 357 358 // If slider pos = 0 when stopping disable effect 359 @Override 360 public void onStopTrackingTouch(final SeekBar seekBar) { 361 if (seekBar.getProgress() == 0) { 362 // disable 363 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 364 mAudioSession, ControlPanelEffect.Key.virt_enabled, false); 365 } 366 } 367 }); 368 369 final Switch sw = (Switch) findViewById(R.id.vIStrengthToggle); 370 sw.setOnCheckedChangeListener(new OnCheckedChangeListener() { 371 @Override 372 public void onCheckedChanged(final CompoundButton buttonView, 373 final boolean isChecked) { 374 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 375 mAudioSession, ControlPanelEffect.Key.virt_enabled, isChecked); 376 } 377 }); 378 } 379 380 // Initialize the Bass Boost elements. 381 // Set the SeekBar listener. 382 if (mBassBoostSupported) { 383 // Show msg when disabled slider (layout) is touched 384 findViewById(R.id.bBLayout).setOnTouchListener(new OnTouchListener() { 385 386 @Override 387 public boolean onTouch(final View v, final MotionEvent event) { 388 if (event.getAction() == MotionEvent.ACTION_UP) { 389 showHeadsetMsg(); 390 } 391 return false; 392 } 393 }); 394 395 final SeekBar seekbar = (SeekBar) findViewById(R.id.bBStrengthSeekBar); 396 seekbar.setMax(OpenSLESConstants.BASSBOOST_MAX_STRENGTH 397 - OpenSLESConstants.BASSBOOST_MIN_STRENGTH); 398 399 seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 400 // Update the parameters while SeekBar changes and set the 401 // effect parameter. 402 403 @Override 404 public void onProgressChanged(final SeekBar seekBar, final int progress, 405 final boolean fromUser) { 406 // set parameter and state 407 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, 408 mAudioSession, ControlPanelEffect.Key.bb_strength, progress); 409 } 410 411 // If slider pos was 0 when starting re-enable effect 412 @Override 413 public void onStartTrackingTouch(final SeekBar seekBar) { 414 if (seekBar.getProgress() == 0) { 415 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 416 mAudioSession, ControlPanelEffect.Key.bb_enabled, true); 417 } 418 } 419 420 // If slider pos = 0 when stopping disable effect 421 @Override 422 public void onStopTrackingTouch(final SeekBar seekBar) { 423 if (seekBar.getProgress() == 0) { 424 // disable 425 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 426 mAudioSession, ControlPanelEffect.Key.bb_enabled, false); 427 } 428 429 } 430 }); 431 } 432 433 // Initialize the Equalizer elements. 434 if (mEqualizerSupported) { 435 mEQPreset = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 436 mAudioSession, ControlPanelEffect.Key.eq_current_preset); 437 if (mEQPreset >= mEQPresetNames.length) { 438 mEQPreset = 0; 439 } 440 mEQPresetPrevious = mEQPreset; 441 equalizerSpinnerInit((Spinner)findViewById(R.id.eqSpinner)); 442 equalizerBandsInit(findViewById(R.id.eqcontainer)); 443 } 444 445 // Initialize the Preset Reverb elements. 446 // Set Spinner listeners. 447 if (mPresetReverbSupported) { 448 mPRPreset = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 449 mAudioSession, ControlPanelEffect.Key.pr_current_preset); 450 mPRPresetPrevious = mPRPreset; 451 reverbSpinnerInit((Spinner)findViewById(R.id.prSpinner)); 452 } 453 454 ActionBar ab = getActionBar(); 455 final int padding = getResources().getDimensionPixelSize( 456 R.dimen.action_bar_switch_padding); 457 mToggleSwitch.setPadding(0,0, padding, 0); 458 ab.setCustomView(mToggleSwitch, new ActionBar.LayoutParams( 459 ActionBar.LayoutParams.WRAP_CONTENT, 460 ActionBar.LayoutParams.WRAP_CONTENT, 461 Gravity.CENTER_VERTICAL | Gravity.RIGHT)); 462 ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM); 463 464 } else { 465 viewGroup.setVisibility(View.GONE); 466 ((TextView) findViewById(R.id.noEffectsTextView)).setVisibility(View.VISIBLE); 467 } 468 469 } 470 471 /* 472 * (non-Javadoc) 473 * 474 * @see android.app.Activity#onResume() 475 */ 476 @Override onResume()477 protected void onResume() { 478 super.onResume(); 479 if ((mVirtualizerSupported) || (mBassBoostSupported) || (mEqualizerSupported) 480 || (mPresetReverbSupported)) { 481 // Listen for broadcast intents that might affect the onscreen UI for headset. 482 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); 483 intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); 484 intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); 485 intentFilter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY); 486 registerReceiver(mReceiver, intentFilter); 487 488 // Check if wired or Bluetooth headset is connected/on 489 final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 490 mIsHeadsetOn = (audioManager.isWiredHeadsetOn() || audioManager.isBluetoothA2dpOn()); 491 Log.v(TAG, "onResume: mIsHeadsetOn : " + mIsHeadsetOn); 492 493 // Update UI 494 updateUI(); 495 } 496 } 497 498 /* 499 * (non-Javadoc) 500 * 501 * @see android.app.Activity#onPause() 502 */ 503 @Override onPause()504 protected void onPause() { 505 super.onPause(); 506 507 // Unregister for broadcast intents. (These affect the visible UI, 508 // so we only care about them while we're in the foreground.) 509 if ((mVirtualizerSupported) || (mBassBoostSupported) || (mEqualizerSupported) 510 || (mPresetReverbSupported)) { 511 unregisterReceiver(mReceiver); 512 } 513 } 514 reverbSpinnerInit(Spinner spinner)515 private void reverbSpinnerInit(Spinner spinner) { 516 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, 517 android.R.layout.simple_spinner_item, PRESETREVERBPRESETSTRINGS); 518 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 519 spinner.setAdapter(adapter); 520 spinner.setOnItemSelectedListener(new OnItemSelectedListener() { 521 522 @Override 523 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 524 if (position != mPRPresetPrevious) { 525 presetReverbSetPreset(position); 526 } 527 mPRPresetPrevious = position; 528 } 529 530 @Override 531 public void onNothingSelected(AdapterView<?> parent) { 532 } 533 }); 534 spinner.setSelection(mPRPreset); 535 } 536 equalizerSpinnerInit(Spinner spinner)537 private void equalizerSpinnerInit(Spinner spinner) { 538 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, 539 android.R.layout.simple_spinner_item, mEQPresetNames); 540 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 541 spinner.setAdapter(adapter); 542 spinner.setOnItemSelectedListener(new OnItemSelectedListener() { 543 544 @Override 545 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 546 if (position != mEQPresetPrevious) { 547 equalizerSetPreset(position); 548 } 549 mEQPresetPrevious = position; 550 } 551 552 @Override 553 public void onNothingSelected(AdapterView<?> parent) { 554 } 555 }); 556 spinner.setSelection(mEQPreset); 557 } 558 559 560 /** 561 * En/disables all children for a given view. For linear and relative layout children do this 562 * recursively 563 * 564 * @param viewGroup 565 * @param enabled 566 */ setEnabledAllChildren(final ViewGroup viewGroup, final boolean enabled)567 private void setEnabledAllChildren(final ViewGroup viewGroup, final boolean enabled) { 568 final int count = viewGroup.getChildCount(); 569 for (int i = 0; i < count; i++) { 570 final View view = viewGroup.getChildAt(i); 571 if ((view instanceof ViewGroup)) { 572 final ViewGroup vg = (ViewGroup) view; 573 setEnabledAllChildren(vg, enabled); 574 } 575 view.setEnabled(enabled); 576 } 577 } 578 579 /** 580 * Updates UI (checkbox, seekbars, enabled states) according to the current stored preferences. 581 */ updateUI()582 private void updateUI() { 583 final boolean isEnabled = ControlPanelEffect.getParameterBoolean(mContext, 584 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.global_enabled); 585 mToggleSwitch.setChecked(isEnabled); 586 setEnabledAllChildren((ViewGroup) findViewById(R.id.contentSoundEffects), isEnabled); 587 updateUIHeadset(); 588 589 if (mVirtualizerSupported) { 590 SeekBar bar = (SeekBar) findViewById(R.id.vIStrengthSeekBar); 591 Switch sw = (Switch) findViewById(R.id.vIStrengthToggle); 592 int strength = ControlPanelEffect 593 .getParameterInt(mContext, mCallingPackageName, mAudioSession, 594 ControlPanelEffect.Key.virt_strength); 595 bar.setProgress(strength); 596 boolean hasStrength = ControlPanelEffect.getParameterBoolean(mContext, 597 mCallingPackageName, mAudioSession, 598 ControlPanelEffect.Key.virt_strength_supported); 599 if (hasStrength) { 600 sw.setVisibility(View.GONE); 601 } else { 602 bar.setVisibility(View.GONE); 603 sw.setChecked(sw.isEnabled() && strength != 0); 604 } 605 } 606 if (mBassBoostSupported) { 607 ((SeekBar) findViewById(R.id.bBStrengthSeekBar)).setProgress(ControlPanelEffect 608 .getParameterInt(mContext, mCallingPackageName, mAudioSession, 609 ControlPanelEffect.Key.bb_strength)); 610 } 611 if (mEqualizerSupported) { 612 equalizerUpdateDisplay(); 613 } 614 if (mPresetReverbSupported) { 615 int reverb = ControlPanelEffect.getParameterInt( 616 mContext, mCallingPackageName, mAudioSession, 617 ControlPanelEffect.Key.pr_current_preset); 618 ((Spinner)findViewById(R.id.prSpinner)).setSelection(reverb); 619 } 620 } 621 622 /** 623 * Updates UI for headset mode. En/disable VI and BB controls depending on headset state 624 * (on/off) if effects are on. Do the inverse for their layouts so they can take over 625 * control/events. 626 */ updateUIHeadset()627 private void updateUIHeadset() { 628 if (mToggleSwitch.isChecked()) { 629 ((TextView) findViewById(R.id.vIStrengthText)).setEnabled( 630 mIsHeadsetOn || !mVirtualizerIsHeadphoneOnly); 631 ((SeekBar) findViewById(R.id.vIStrengthSeekBar)).setEnabled( 632 mIsHeadsetOn || !mVirtualizerIsHeadphoneOnly); 633 findViewById(R.id.vILayout).setEnabled(!mIsHeadsetOn || !mVirtualizerIsHeadphoneOnly); 634 ((TextView) findViewById(R.id.bBStrengthText)).setEnabled(mIsHeadsetOn); 635 ((SeekBar) findViewById(R.id.bBStrengthSeekBar)).setEnabled(mIsHeadsetOn); 636 findViewById(R.id.bBLayout).setEnabled(!mIsHeadsetOn); 637 } 638 } 639 640 /** 641 * Initializes the equalizer elements. Set the SeekBars and Spinner listeners. 642 */ equalizerBandsInit(View eqcontainer)643 private void equalizerBandsInit(View eqcontainer) { 644 // Initialize the N-Band Equalizer elements. 645 mNumberEqualizerBands = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 646 mAudioSession, ControlPanelEffect.Key.eq_num_bands); 647 mEQPresetUserBandLevelsPrev = ControlPanelEffect.getParameterIntArray(mContext, 648 mCallingPackageName, mAudioSession, 649 ControlPanelEffect.Key.eq_preset_user_band_level); 650 final int[] centerFreqs = ControlPanelEffect.getParameterIntArray(mContext, 651 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_center_freq); 652 final int[] bandLevelRange = ControlPanelEffect.getParameterIntArray(mContext, 653 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_level_range); 654 mEqualizerMinBandLevel = (int) Math.max(EQUALIZER_MIN_LEVEL, bandLevelRange[0]); 655 final int mEqualizerMaxBandLevel = (int) Math.min(EQUALIZER_MAX_LEVEL, bandLevelRange[1]); 656 657 for (int band = 0; band < mNumberEqualizerBands; band++) { 658 // Unit conversion from mHz to Hz and use k prefix if necessary to display 659 final int centerFreq = centerFreqs[band] / 1000; 660 float centerFreqHz = centerFreq; 661 String unitPrefix = ""; 662 if (centerFreqHz >= 1000) { 663 centerFreqHz = centerFreqHz / 1000; 664 unitPrefix = "k"; 665 } 666 ((TextView) eqcontainer.findViewById(EQViewElementIds[band][0])).setText( 667 format("%.0f ", centerFreqHz) + unitPrefix + "Hz"); 668 mEqualizerSeekBar[band] = (SeekBar) eqcontainer 669 .findViewById(EQViewElementIds[band][1]); 670 eqcontainer.findViewById(EQViewElementIds[band][0]) 671 .setLabelFor(EQViewElementIds[band][1]); 672 mEqualizerSeekBar[band].setMax(mEqualizerMaxBandLevel - mEqualizerMinBandLevel); 673 mEqualizerSeekBar[band].setOnSeekBarChangeListener(this); 674 } 675 676 // Hide the inactive Equalizer bands. 677 for (int band = mNumberEqualizerBands; band < EQUALIZER_MAX_BANDS; band++) { 678 // CenterFreq text 679 eqcontainer.findViewById(EQViewElementIds[band][0]).setVisibility(View.GONE); 680 // SeekBar 681 eqcontainer.findViewById(EQViewElementIds[band][1]).setVisibility(View.GONE); 682 } 683 684 TextView tv = (TextView) findViewById(R.id.maxLevelText); 685 tv.setText(String.format("+%d dB", (int) Math.ceil(mEqualizerMaxBandLevel / 100))); 686 tv = (TextView) findViewById(R.id.centerLevelText); 687 tv.setText("0 dB"); 688 tv = (TextView) findViewById(R.id.minLevelText); 689 tv.setText(String.format("%d dB", (int) Math.floor(mEqualizerMinBandLevel / 100))); 690 equalizerUpdateDisplay(); 691 } 692 format(String format, Object... args)693 private String format(String format, Object... args) { 694 mFormatBuilder.setLength(0); 695 mFormatter.format(format, args); 696 return mFormatBuilder.toString(); 697 } 698 699 /* 700 * For the EQ Band SeekBars 701 * 702 * (non-Javadoc) 703 * 704 * @see android.widget.SeekBar.OnSeekBarChangeListener#onProgressChanged(android 705 * .widget.SeekBar, int, boolean) 706 */ 707 708 @Override onProgressChanged(final SeekBar seekbar, final int progress, final boolean fromUser)709 public void onProgressChanged(final SeekBar seekbar, final int progress, final boolean fromUser) { 710 final int id = seekbar.getId(); 711 712 for (short band = 0; band < mNumberEqualizerBands; band++) { 713 if (id == EQViewElementIds[band][1]) { 714 final short level = (short) (progress + mEqualizerMinBandLevel); 715 if (fromUser) { 716 equalizerBandUpdate(band, level); 717 } 718 break; 719 } 720 } 721 } 722 723 /* 724 * (non-Javadoc) 725 * 726 * @see android.widget.SeekBar.OnSeekBarChangeListener#onStartTrackingTouch(android 727 * .widget.SeekBar) 728 */ 729 730 @Override onStartTrackingTouch(final SeekBar seekbar)731 public void onStartTrackingTouch(final SeekBar seekbar) { 732 // get current levels 733 final int[] bandLevels = ControlPanelEffect.getParameterIntArray(mContext, 734 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_band_level); 735 // copy current levels to user preset 736 for (short band = 0; band < mNumberEqualizerBands; band++) { 737 equalizerBandUpdate(band, bandLevels[band]); 738 } 739 equalizerSetPreset(mEQPresetUserPos); 740 ((Spinner)findViewById(R.id.eqSpinner)).setSelection(mEQPresetUserPos); 741 } 742 743 /* 744 * Updates the EQ display when the user stops changing. 745 * 746 * (non-Javadoc) 747 * 748 * @see android.widget.SeekBar.OnSeekBarChangeListener#onStopTrackingTouch(android 749 * .widget.SeekBar) 750 */ 751 752 @Override onStopTrackingTouch(final SeekBar seekbar)753 public void onStopTrackingTouch(final SeekBar seekbar) { 754 equalizerUpdateDisplay(); 755 } 756 757 /** 758 * Updates the EQ by getting the parameters. 759 */ equalizerUpdateDisplay()760 private void equalizerUpdateDisplay() { 761 // Update and show the active N-Band Equalizer bands. 762 final int[] bandLevels = ControlPanelEffect.getParameterIntArray(mContext, 763 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_band_level); 764 for (short band = 0; band < mNumberEqualizerBands; band++) { 765 final int level = bandLevels[band]; 766 final int progress = level - mEqualizerMinBandLevel; 767 mEqualizerSeekBar[band].setProgress(progress); 768 } 769 } 770 771 /** 772 * Updates/sets a given EQ band level. 773 * 774 * @param band 775 * Band id 776 * @param level 777 * EQ band level 778 */ equalizerBandUpdate(final int band, final int level)779 private void equalizerBandUpdate(final int band, final int level) { 780 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession, 781 ControlPanelEffect.Key.eq_band_level, level, band); 782 } 783 784 /** 785 * Sets the given EQ preset. 786 * 787 * @param preset 788 * EQ preset id. 789 */ equalizerSetPreset(final int preset)790 private void equalizerSetPreset(final int preset) { 791 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession, 792 ControlPanelEffect.Key.eq_current_preset, preset); 793 equalizerUpdateDisplay(); 794 } 795 796 /** 797 * Sets the given PR preset. 798 * 799 * @param preset 800 * PR preset id. 801 */ presetReverbSetPreset(final int preset)802 private void presetReverbSetPreset(final int preset) { 803 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession, 804 ControlPanelEffect.Key.pr_current_preset, preset); 805 } 806 807 /** 808 * Show msg that headset needs to be plugged. 809 */ showHeadsetMsg()810 private void showHeadsetMsg() { 811 final Context context = getApplicationContext(); 812 final int duration = Toast.LENGTH_SHORT; 813 814 final Toast toast = Toast.makeText(context, getString(R.string.headset_plug), duration); 815 toast.setGravity(Gravity.CENTER, toast.getXOffset() / 2, toast.getYOffset() / 2); 816 toast.show(); 817 } 818 isVirtualizerTransauralSupported()819 private static boolean isVirtualizerTransauralSupported() { 820 Virtualizer virt = null; 821 boolean transauralSupported = false; 822 try { 823 virt = new Virtualizer(0, android.media.AudioSystem.newAudioSessionId()); 824 transauralSupported = virt.canVirtualize(AudioFormat.CHANNEL_OUT_STEREO, 825 Virtualizer.VIRTUALIZATION_MODE_TRANSAURAL); 826 } catch (Exception e) { 827 } finally { 828 if (virt != null) { 829 virt.release(); 830 } 831 } 832 return transauralSupported; 833 } 834 } 835