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 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 // Fill array with presets from AudioEffects call. 279 // allocate a space for 2 extra strings (CI Extreme & User) 280 final int numPresets = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 281 mAudioSession, ControlPanelEffect.Key.eq_num_presets); 282 mEQPresetNames = new String[numPresets + 2]; 283 for (short i = 0; i < numPresets; i++) { 284 mEQPresetNames[i] = ControlPanelEffect.getParameterString(mContext, 285 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_preset_name, i); 286 } 287 mEQPresetNames[numPresets] = getString(R.string.ci_extreme); 288 mEQPresetNames[numPresets + 1] = getString(R.string.user); 289 mEQPresetUserPos = numPresets + 1; 290 291 // Watch for button clicks and initialization. 292 if ((mVirtualizerSupported) || (mBassBoostSupported) || (mEqualizerSupported) 293 || (mPresetReverbSupported)) { 294 // Set the listener for the main enhancements toggle button. 295 // Depending on the state enable the supported effects if they were 296 // checked in the setup tab. 297 mToggleSwitch = new Switch(this); 298 mToggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() { 299 @Override 300 public void onCheckedChanged(final CompoundButton buttonView, 301 final boolean isChecked) { 302 303 // set parameter and state 304 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 305 mAudioSession, ControlPanelEffect.Key.global_enabled, isChecked); 306 // Enable Linear layout (in scroll layout) view with all 307 // effect contents depending on checked state 308 setEnabledAllChildren(viewGroup, isChecked); 309 // update UI according to headset state 310 updateUIHeadset(); 311 } 312 }); 313 314 // Initialize the Virtualizer elements. 315 // Set the SeekBar listener. 316 if (mVirtualizerSupported) { 317 // Show msg when disabled slider (layout) is touched 318 findViewById(R.id.vILayout).setOnTouchListener(new OnTouchListener() { 319 320 @Override 321 public boolean onTouch(final View v, final MotionEvent event) { 322 if (event.getAction() == MotionEvent.ACTION_UP) { 323 showHeadsetMsg(); 324 } 325 return false; 326 } 327 }); 328 329 final SeekBar seekbar = (SeekBar) findViewById(R.id.vIStrengthSeekBar); 330 seekbar.setMax(OpenSLESConstants.VIRTUALIZER_MAX_STRENGTH 331 - OpenSLESConstants.VIRTUALIZER_MIN_STRENGTH); 332 333 seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 334 // Update the parameters while SeekBar changes and set the 335 // effect parameter. 336 337 @Override 338 public void onProgressChanged(final SeekBar seekBar, final int progress, 339 final boolean fromUser) { 340 // set parameter and state 341 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, 342 mAudioSession, ControlPanelEffect.Key.virt_strength, progress); 343 } 344 345 // If slider pos was 0 when starting re-enable effect 346 @Override 347 public void onStartTrackingTouch(final SeekBar seekBar) { 348 if (seekBar.getProgress() == 0) { 349 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 350 mAudioSession, ControlPanelEffect.Key.virt_enabled, true); 351 } 352 } 353 354 // If slider pos = 0 when stopping disable effect 355 @Override 356 public void onStopTrackingTouch(final SeekBar seekBar) { 357 if (seekBar.getProgress() == 0) { 358 // disable 359 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 360 mAudioSession, ControlPanelEffect.Key.virt_enabled, false); 361 } 362 } 363 }); 364 365 final Switch sw = (Switch) findViewById(R.id.vIStrengthToggle); 366 sw.setOnCheckedChangeListener(new OnCheckedChangeListener() { 367 @Override 368 public void onCheckedChanged(final CompoundButton buttonView, 369 final boolean isChecked) { 370 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 371 mAudioSession, ControlPanelEffect.Key.virt_enabled, isChecked); 372 } 373 }); 374 } 375 376 // Initialize the Bass Boost elements. 377 // Set the SeekBar listener. 378 if (mBassBoostSupported) { 379 // Show msg when disabled slider (layout) is touched 380 findViewById(R.id.bBLayout).setOnTouchListener(new OnTouchListener() { 381 382 @Override 383 public boolean onTouch(final View v, final MotionEvent event) { 384 if (event.getAction() == MotionEvent.ACTION_UP) { 385 showHeadsetMsg(); 386 } 387 return false; 388 } 389 }); 390 391 final SeekBar seekbar = (SeekBar) findViewById(R.id.bBStrengthSeekBar); 392 seekbar.setMax(OpenSLESConstants.BASSBOOST_MAX_STRENGTH 393 - OpenSLESConstants.BASSBOOST_MIN_STRENGTH); 394 395 seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 396 // Update the parameters while SeekBar changes and set the 397 // effect parameter. 398 399 @Override 400 public void onProgressChanged(final SeekBar seekBar, final int progress, 401 final boolean fromUser) { 402 // set parameter and state 403 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, 404 mAudioSession, ControlPanelEffect.Key.bb_strength, progress); 405 } 406 407 // If slider pos was 0 when starting re-enable effect 408 @Override 409 public void onStartTrackingTouch(final SeekBar seekBar) { 410 if (seekBar.getProgress() == 0) { 411 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 412 mAudioSession, ControlPanelEffect.Key.bb_enabled, true); 413 } 414 } 415 416 // If slider pos = 0 when stopping disable effect 417 @Override 418 public void onStopTrackingTouch(final SeekBar seekBar) { 419 if (seekBar.getProgress() == 0) { 420 // disable 421 ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName, 422 mAudioSession, ControlPanelEffect.Key.bb_enabled, false); 423 } 424 425 } 426 }); 427 } 428 429 // Initialize the Equalizer elements. 430 if (mEqualizerSupported) { 431 mEQPreset = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 432 mAudioSession, ControlPanelEffect.Key.eq_current_preset); 433 if (mEQPreset >= mEQPresetNames.length) { 434 mEQPreset = 0; 435 } 436 mEQPresetPrevious = mEQPreset; 437 equalizerSpinnerInit((Spinner)findViewById(R.id.eqSpinner)); 438 equalizerBandsInit(findViewById(R.id.eqcontainer)); 439 } 440 441 // Initialize the Preset Reverb elements. 442 // Set Spinner listeners. 443 if (mPresetReverbSupported) { 444 mPRPreset = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 445 mAudioSession, ControlPanelEffect.Key.pr_current_preset); 446 mPRPresetPrevious = mPRPreset; 447 reverbSpinnerInit((Spinner)findViewById(R.id.prSpinner)); 448 } 449 450 } else { 451 viewGroup.setVisibility(View.GONE); 452 ((TextView) findViewById(R.id.noEffectsTextView)).setVisibility(View.VISIBLE); 453 } 454 455 ActionBar ab = getActionBar(); 456 final int padding = getResources().getDimensionPixelSize( 457 R.dimen.action_bar_switch_padding); 458 mToggleSwitch.setPadding(0,0, padding, 0); 459 ab.setCustomView(mToggleSwitch, new ActionBar.LayoutParams( 460 ActionBar.LayoutParams.WRAP_CONTENT, 461 ActionBar.LayoutParams.WRAP_CONTENT, 462 Gravity.CENTER_VERTICAL | Gravity.RIGHT)); 463 ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM); 464 } 465 466 /* 467 * (non-Javadoc) 468 * 469 * @see android.app.Activity#onResume() 470 */ 471 @Override 472 protected void onResume() { 473 super.onResume(); 474 if ((mVirtualizerSupported) || (mBassBoostSupported) || (mEqualizerSupported) 475 || (mPresetReverbSupported)) { 476 // Listen for broadcast intents that might affect the onscreen UI for headset. 477 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); 478 intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); 479 intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); 480 intentFilter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY); 481 registerReceiver(mReceiver, intentFilter); 482 483 // Check if wired or Bluetooth headset is connected/on 484 final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 485 mIsHeadsetOn = (audioManager.isWiredHeadsetOn() || audioManager.isBluetoothA2dpOn()); 486 Log.v(TAG, "onResume: mIsHeadsetOn : " + mIsHeadsetOn); 487 488 // Update UI 489 updateUI(); 490 } 491 } 492 493 /* 494 * (non-Javadoc) 495 * 496 * @see android.app.Activity#onPause() 497 */ 498 @Override 499 protected void onPause() { 500 super.onPause(); 501 502 // Unregister for broadcast intents. (These affect the visible UI, 503 // so we only care about them while we're in the foreground.) 504 unregisterReceiver(mReceiver); 505 } 506 507 private void reverbSpinnerInit(Spinner spinner) { 508 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, 509 android.R.layout.simple_spinner_item, PRESETREVERBPRESETSTRINGS); 510 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 511 spinner.setAdapter(adapter); 512 spinner.setOnItemSelectedListener(new OnItemSelectedListener() { 513 514 @Override 515 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 516 if (position != mPRPresetPrevious) { 517 presetReverbSetPreset(position); 518 } 519 mPRPresetPrevious = position; 520 } 521 522 @Override 523 public void onNothingSelected(AdapterView<?> parent) { 524 } 525 }); 526 spinner.setSelection(mPRPreset); 527 } 528 529 private void equalizerSpinnerInit(Spinner spinner) { 530 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, 531 android.R.layout.simple_spinner_item, mEQPresetNames); 532 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 533 spinner.setAdapter(adapter); 534 spinner.setOnItemSelectedListener(new OnItemSelectedListener() { 535 536 @Override 537 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 538 if (position != mEQPresetPrevious) { 539 equalizerSetPreset(position); 540 } 541 mEQPresetPrevious = position; 542 } 543 544 @Override 545 public void onNothingSelected(AdapterView<?> parent) { 546 } 547 }); 548 spinner.setSelection(mEQPreset); 549 } 550 551 552 /** 553 * En/disables all children for a given view. For linear and relative layout children do this 554 * recursively 555 * 556 * @param viewGroup 557 * @param enabled 558 */ 559 private void setEnabledAllChildren(final ViewGroup viewGroup, final boolean enabled) { 560 final int count = viewGroup.getChildCount(); 561 for (int i = 0; i < count; i++) { 562 final View view = viewGroup.getChildAt(i); 563 if ((view instanceof ViewGroup)) { 564 final ViewGroup vg = (ViewGroup) view; 565 setEnabledAllChildren(vg, enabled); 566 } 567 view.setEnabled(enabled); 568 } 569 } 570 571 /** 572 * Updates UI (checkbox, seekbars, enabled states) according to the current stored preferences. 573 */ 574 private void updateUI() { 575 final boolean isEnabled = ControlPanelEffect.getParameterBoolean(mContext, 576 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.global_enabled); 577 mToggleSwitch.setChecked(isEnabled); 578 setEnabledAllChildren((ViewGroup) findViewById(R.id.contentSoundEffects), isEnabled); 579 updateUIHeadset(); 580 581 if (mVirtualizerSupported) { 582 SeekBar bar = (SeekBar) findViewById(R.id.vIStrengthSeekBar); 583 Switch sw = (Switch) findViewById(R.id.vIStrengthToggle); 584 int strength = ControlPanelEffect 585 .getParameterInt(mContext, mCallingPackageName, mAudioSession, 586 ControlPanelEffect.Key.virt_strength); 587 bar.setProgress(strength); 588 boolean hasStrength = ControlPanelEffect.getParameterBoolean(mContext, 589 mCallingPackageName, mAudioSession, 590 ControlPanelEffect.Key.virt_strength_supported); 591 if (hasStrength) { 592 sw.setVisibility(View.GONE); 593 } else { 594 bar.setVisibility(View.GONE); 595 sw.setChecked(sw.isEnabled() && strength != 0); 596 } 597 } 598 if (mBassBoostSupported) { 599 ((SeekBar) findViewById(R.id.bBStrengthSeekBar)).setProgress(ControlPanelEffect 600 .getParameterInt(mContext, mCallingPackageName, mAudioSession, 601 ControlPanelEffect.Key.bb_strength)); 602 } 603 if (mEqualizerSupported) { 604 equalizerUpdateDisplay(); 605 } 606 if (mPresetReverbSupported) { 607 int reverb = ControlPanelEffect.getParameterInt( 608 mContext, mCallingPackageName, mAudioSession, 609 ControlPanelEffect.Key.pr_current_preset); 610 ((Spinner)findViewById(R.id.prSpinner)).setSelection(reverb); 611 } 612 } 613 614 /** 615 * Updates UI for headset mode. En/disable VI and BB controls depending on headset state 616 * (on/off) if effects are on. Do the inverse for their layouts so they can take over 617 * control/events. 618 */ 619 private void updateUIHeadset() { 620 if (mToggleSwitch.isChecked()) { 621 ((TextView) findViewById(R.id.vIStrengthText)).setEnabled( 622 mIsHeadsetOn || !mVirtualizerIsHeadphoneOnly); 623 ((SeekBar) findViewById(R.id.vIStrengthSeekBar)).setEnabled( 624 mIsHeadsetOn || !mVirtualizerIsHeadphoneOnly); 625 findViewById(R.id.vILayout).setEnabled(!mIsHeadsetOn || !mVirtualizerIsHeadphoneOnly); 626 ((TextView) findViewById(R.id.bBStrengthText)).setEnabled(mIsHeadsetOn); 627 ((SeekBar) findViewById(R.id.bBStrengthSeekBar)).setEnabled(mIsHeadsetOn); 628 findViewById(R.id.bBLayout).setEnabled(!mIsHeadsetOn); 629 } 630 } 631 632 /** 633 * Initializes the equalizer elements. Set the SeekBars and Spinner listeners. 634 */ 635 private void equalizerBandsInit(View eqcontainer) { 636 // Initialize the N-Band Equalizer elements. 637 mNumberEqualizerBands = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName, 638 mAudioSession, ControlPanelEffect.Key.eq_num_bands); 639 mEQPresetUserBandLevelsPrev = ControlPanelEffect.getParameterIntArray(mContext, 640 mCallingPackageName, mAudioSession, 641 ControlPanelEffect.Key.eq_preset_user_band_level); 642 final int[] centerFreqs = ControlPanelEffect.getParameterIntArray(mContext, 643 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_center_freq); 644 final int[] bandLevelRange = ControlPanelEffect.getParameterIntArray(mContext, 645 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_level_range); 646 mEqualizerMinBandLevel = (int) Math.max(EQUALIZER_MIN_LEVEL, bandLevelRange[0]); 647 final int mEqualizerMaxBandLevel = (int) Math.min(EQUALIZER_MAX_LEVEL, bandLevelRange[1]); 648 649 for (int band = 0; band < mNumberEqualizerBands; band++) { 650 // Unit conversion from mHz to Hz and use k prefix if necessary to display 651 final int centerFreq = centerFreqs[band] / 1000; 652 float centerFreqHz = centerFreq; 653 String unitPrefix = ""; 654 if (centerFreqHz >= 1000) { 655 centerFreqHz = centerFreqHz / 1000; 656 unitPrefix = "k"; 657 } 658 ((TextView) eqcontainer.findViewById(EQViewElementIds[band][0])).setText( 659 format("%.0f ", centerFreqHz) + unitPrefix + "Hz"); 660 mEqualizerSeekBar[band] = (SeekBar) eqcontainer 661 .findViewById(EQViewElementIds[band][1]); 662 mEqualizerSeekBar[band].setMax(mEqualizerMaxBandLevel - mEqualizerMinBandLevel); 663 mEqualizerSeekBar[band].setOnSeekBarChangeListener(this); 664 } 665 666 // Hide the inactive Equalizer bands. 667 for (int band = mNumberEqualizerBands; band < EQUALIZER_MAX_BANDS; band++) { 668 // CenterFreq text 669 eqcontainer.findViewById(EQViewElementIds[band][0]).setVisibility(View.GONE); 670 // SeekBar 671 eqcontainer.findViewById(EQViewElementIds[band][1]).setVisibility(View.GONE); 672 } 673 674 TextView tv = (TextView) findViewById(R.id.maxLevelText); 675 tv.setText(String.format("+%d dB", (int) Math.ceil(mEqualizerMaxBandLevel / 100))); 676 tv = (TextView) findViewById(R.id.centerLevelText); 677 tv.setText("0 dB"); 678 tv = (TextView) findViewById(R.id.minLevelText); 679 tv.setText(String.format("%d dB", (int) Math.floor(mEqualizerMinBandLevel / 100))); 680 equalizerUpdateDisplay(); 681 } 682 683 private String format(String format, Object... args) { 684 mFormatBuilder.setLength(0); 685 mFormatter.format(format, args); 686 return mFormatBuilder.toString(); 687 } 688 689 /* 690 * For the EQ Band SeekBars 691 * 692 * (non-Javadoc) 693 * 694 * @see android.widget.SeekBar.OnSeekBarChangeListener#onProgressChanged(android 695 * .widget.SeekBar, int, boolean) 696 */ 697 698 @Override 699 public void onProgressChanged(final SeekBar seekbar, final int progress, final boolean fromUser) { 700 final int id = seekbar.getId(); 701 702 for (short band = 0; band < mNumberEqualizerBands; band++) { 703 if (id == EQViewElementIds[band][1]) { 704 final short level = (short) (progress + mEqualizerMinBandLevel); 705 if (fromUser) { 706 equalizerBandUpdate(band, level); 707 } 708 break; 709 } 710 } 711 } 712 713 /* 714 * (non-Javadoc) 715 * 716 * @see android.widget.SeekBar.OnSeekBarChangeListener#onStartTrackingTouch(android 717 * .widget.SeekBar) 718 */ 719 720 @Override 721 public void onStartTrackingTouch(final SeekBar seekbar) { 722 // get current levels 723 final int[] bandLevels = ControlPanelEffect.getParameterIntArray(mContext, 724 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_band_level); 725 // copy current levels to user preset 726 for (short band = 0; band < mNumberEqualizerBands; band++) { 727 equalizerBandUpdate(band, bandLevels[band]); 728 } 729 equalizerSetPreset(mEQPresetUserPos); 730 ((Spinner)findViewById(R.id.eqSpinner)).setSelection(mEQPresetUserPos); 731 } 732 733 /* 734 * Updates the EQ display when the user stops changing. 735 * 736 * (non-Javadoc) 737 * 738 * @see android.widget.SeekBar.OnSeekBarChangeListener#onStopTrackingTouch(android 739 * .widget.SeekBar) 740 */ 741 742 @Override 743 public void onStopTrackingTouch(final SeekBar seekbar) { 744 equalizerUpdateDisplay(); 745 } 746 747 /** 748 * Updates the EQ by getting the parameters. 749 */ 750 private void equalizerUpdateDisplay() { 751 // Update and show the active N-Band Equalizer bands. 752 final int[] bandLevels = ControlPanelEffect.getParameterIntArray(mContext, 753 mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_band_level); 754 for (short band = 0; band < mNumberEqualizerBands; band++) { 755 final int level = bandLevels[band]; 756 final int progress = level - mEqualizerMinBandLevel; 757 mEqualizerSeekBar[band].setProgress(progress); 758 } 759 } 760 761 /** 762 * Updates/sets a given EQ band level. 763 * 764 * @param band 765 * Band id 766 * @param level 767 * EQ band level 768 */ 769 private void equalizerBandUpdate(final int band, final int level) { 770 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession, 771 ControlPanelEffect.Key.eq_band_level, level, band); 772 } 773 774 /** 775 * Sets the given EQ preset. 776 * 777 * @param preset 778 * EQ preset id. 779 */ 780 private void equalizerSetPreset(final int preset) { 781 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession, 782 ControlPanelEffect.Key.eq_current_preset, preset); 783 equalizerUpdateDisplay(); 784 } 785 786 /** 787 * Sets the given PR preset. 788 * 789 * @param preset 790 * PR preset id. 791 */ 792 private void presetReverbSetPreset(final int preset) { 793 ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession, 794 ControlPanelEffect.Key.pr_current_preset, preset); 795 } 796 797 /** 798 * Show msg that headset needs to be plugged. 799 */ 800 private void showHeadsetMsg() { 801 final Context context = getApplicationContext(); 802 final int duration = Toast.LENGTH_SHORT; 803 804 final Toast toast = Toast.makeText(context, getString(R.string.headset_plug), duration); 805 toast.setGravity(Gravity.CENTER, toast.getXOffset() / 2, toast.getYOffset() / 2); 806 toast.show(); 807 } 808 809 private static boolean isVirtualizerTransauralSupported() { 810 Virtualizer virt = null; 811 boolean transauralSupported = false; 812 try { 813 virt = new Virtualizer(0, android.media.AudioSystem.newAudioSessionId()); 814 transauralSupported = virt.canVirtualize(AudioFormat.CHANNEL_OUT_STEREO, 815 Virtualizer.VIRTUALIZATION_MODE_TRANSAURAL); 816 } catch (Exception e) { 817 } finally { 818 if (virt != null) { 819 virt.release(); 820 } 821 } 822 return transauralSupported; 823 } 824 } 825