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