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