Home | History | Annotate | Download | only in musicfx
      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 import com.android.musicfx.seekbar.SeekBar;
     21 import com.android.musicfx.seekbar.SeekBar.OnSeekBarChangeListener;
     22 
     23 import android.app.ActionBar;
     24 import android.app.Activity;
     25 import android.app.AlertDialog;
     26 import android.app.Dialog;
     27 import android.bluetooth.BluetoothClass;
     28 import android.bluetooth.BluetoothDevice;
     29 import android.content.BroadcastReceiver;
     30 import android.content.Context;
     31 import android.content.DialogInterface;
     32 import android.content.DialogInterface.OnCancelListener;
     33 import android.content.Intent;
     34 import android.content.IntentFilter;
     35 import android.media.AudioManager;
     36 import android.media.audiofx.AudioEffect;
     37 import android.media.audiofx.AudioEffect.Descriptor;
     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.Spinner;
     56 import android.widget.Switch;
     57 import android.widget.TextView;
     58 import android.widget.Toast;
     59 
     60 import java.util.Formatter;
     61 import java.util.Locale;
     62 import java.util.UUID;
     63 
     64 /**
     65  *
     66  */
     67 public 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