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