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 
     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