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