ActivityMusic.java revision f3b2798428ebb3e3895eb5106a4cb69c6bbc8181
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 android.app.Activity;
20import android.app.AlertDialog;
21import android.app.Dialog;
22import android.bluetooth.BluetoothClass;
23import android.bluetooth.BluetoothDevice;
24import android.content.BroadcastReceiver;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.DialogInterface.OnCancelListener;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.media.AudioManager;
31import android.media.audiofx.AudioEffect;
32import android.media.audiofx.AudioEffect.Descriptor;
33import android.os.Bundle;
34import android.util.Log;
35import android.view.Gravity;
36import android.view.LayoutInflater;
37import android.view.Menu;
38import android.view.MenuInflater;
39import android.view.MenuItem;
40import android.view.MotionEvent;
41import android.view.View;
42import android.view.View.OnClickListener;
43import android.view.View.OnTouchListener;
44import android.view.ViewGroup;
45import android.widget.AbsListView;
46import android.widget.CheckBox;
47import android.widget.CompoundButton;
48import android.widget.CompoundButton.OnCheckedChangeListener;
49import android.widget.LinearLayout;
50import android.widget.ListView;
51import android.widget.RelativeLayout;
52import com.android.musicfx.seekbar.SeekBar;
53import com.android.musicfx.seekbar.SeekBar.OnSeekBarChangeListener;
54import android.widget.TextView;
55import android.widget.Toast;
56
57import com.android.audiofx.OpenSLESConstants;
58
59import java.util.Formatter;
60import java.util.HashMap;
61import java.util.Locale;
62import java.util.Map;
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     * Dialog IDS
77     */
78    static final int DIALOG_EQUALIZER = 0;
79    static final int DIALOG_PRESET_REVERB = 1;
80    static final int DIALOG_RESET_DEFAULTS = 2;
81
82    /**
83     * Indicates if Virtualizer effect is supported.
84     */
85    private boolean mVirtualizerSupported;
86    /**
87     * Indicates if BassBoost effect is supported.
88     */
89    private boolean mBassBoostSupported;
90    /**
91     * Indicates if Equalizer effect is supported.
92     */
93    private boolean mEqualizerSupported;
94    /**
95     * Indicates if Preset Reverb effect is supported.
96     */
97    private boolean mPresetReverbSupported;
98
99    // Equalizer fields
100    private final SeekBar[] mEqualizerSeekBar = new SeekBar[EQUALIZER_MAX_BANDS];
101    private final TextView[] mEqualizerValueText = new TextView[EQUALIZER_MAX_BANDS];
102    private int mNumberEqualizerBands;
103    private int mEqualizerMinBandLevel;
104    private int mEQPresetUserPos = 1;
105    private View mEqualizerView;
106    private int mEQPreset;
107    private int mEQPresetPrevious;
108    private int[] mEQPresetUserBandLevelsPrev;
109
110    private int mPRPreset;
111
112    private boolean mIsHeadsetOn = false;
113
114    private StringBuilder mFormatBuilder = new StringBuilder();
115    private Formatter mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
116
117    /**
118     * Mapping for the EQ widget ids per band
119     */
120    private static final int[][] EQViewElementIds = {
121            { R.id.EQBand0TextView, R.id.EQBand0SeekBar, R.id.EQBand0Value },
122            { R.id.EQBand1TextView, R.id.EQBand1SeekBar, R.id.EQBand1Value },
123            { R.id.EQBand2TextView, R.id.EQBand2SeekBar, R.id.EQBand2Value },
124            { R.id.EQBand3TextView, R.id.EQBand3SeekBar, R.id.EQBand3Value },
125            { R.id.EQBand4TextView, R.id.EQBand4SeekBar, R.id.EQBand4Value },
126            { R.id.EQBand5TextView, R.id.EQBand5SeekBar, R.id.EQBand5Value },
127            { R.id.EQBand6TextView, R.id.EQBand6SeekBar, R.id.EQBand6Value },
128            { R.id.EQBand7TextView, R.id.EQBand7SeekBar, R.id.EQBand7Value },
129            { R.id.EQBand8TextView, R.id.EQBand8SeekBar, R.id.EQBand8Value },
130            { R.id.EQBand9TextView, R.id.EQBand9SeekBar, R.id.EQBand9Value },
131            { R.id.EQBand10TextView, R.id.EQBand10SeekBar, R.id.EQBand10Value },
132            { R.id.EQBand11TextView, R.id.EQBand11SeekBar, R.id.EQBand11Value },
133            { R.id.EQBand12TextView, R.id.EQBand12SeekBar, R.id.EQBand12Value },
134            { R.id.EQBand13TextView, R.id.EQBand13SeekBar, R.id.EQBand13Value },
135            { R.id.EQBand14TextView, R.id.EQBand14SeekBar, R.id.EQBand14Value },
136            { R.id.EQBand15TextView, R.id.EQBand15SeekBar, R.id.EQBand15Value },
137            { R.id.EQBand16TextView, R.id.EQBand16SeekBar, R.id.EQBand16Value },
138            { R.id.EQBand17TextView, R.id.EQBand17SeekBar, R.id.EQBand17Value },
139            { R.id.EQBand18TextView, R.id.EQBand18SeekBar, R.id.EQBand18Value },
140            { R.id.EQBand19TextView, R.id.EQBand19SeekBar, R.id.EQBand19Value },
141            { R.id.EQBand20TextView, R.id.EQBand20SeekBar, R.id.EQBand20Value },
142            { R.id.EQBand21TextView, R.id.EQBand21SeekBar, R.id.EQBand21Value },
143            { R.id.EQBand22TextView, R.id.EQBand22SeekBar, R.id.EQBand22Value },
144            { R.id.EQBand23TextView, R.id.EQBand23SeekBar, R.id.EQBand23Value },
145            { R.id.EQBand24TextView, R.id.EQBand24SeekBar, R.id.EQBand24Value },
146            { R.id.EQBand25TextView, R.id.EQBand25SeekBar, R.id.EQBand25Value },
147            { R.id.EQBand26TextView, R.id.EQBand26SeekBar, R.id.EQBand26Value },
148            { R.id.EQBand27TextView, R.id.EQBand27SeekBar, R.id.EQBand27Value },
149            { R.id.EQBand28TextView, R.id.EQBand28SeekBar, R.id.EQBand28Value },
150            { R.id.EQBand29TextView, R.id.EQBand29SeekBar, R.id.EQBand29Value },
151            { R.id.EQBand30TextView, R.id.EQBand30SeekBar, R.id.EQBand30Value },
152            { R.id.EQBand31TextView, R.id.EQBand31SeekBar, R.id.EQBand31Value } };
153
154    // Preset Reverb fields
155    /**
156     * Array containing the PR preset names.
157     */
158    private static final String[] PRESETREVERBPRESETSTRINGS = { "None", "SmallRoom", "MediumRoom",
159            "LargeRoom", "MediumHall", "LargeHall", "Plate" };
160
161    /**
162     * Context field
163     */
164    private Context mContext;
165
166    /**
167     * Calling package name field
168     */
169    private String mCallingPackageName = "empty";
170
171    /**
172     * Audio session field
173     */
174    private int mAudioSession = AudioEffect.ERROR_BAD_VALUE;
175
176    // Broadcast receiver to handle wired and Bluetooth A2dp headset events
177    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
178        @Override
179        public void onReceive(final Context context, final Intent intent) {
180            final String action = intent.getAction();
181            final boolean isHeadsetOnPrev = mIsHeadsetOn;
182            final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
183            if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
184                mIsHeadsetOn = (intent.getIntExtra("state", 0) == 1)
185                        || audioManager.isBluetoothA2dpOn();
186            } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
187                final int deviceClass = ((BluetoothDevice) intent
188                        .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).getBluetoothClass()
189                        .getDeviceClass();
190                if ((deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES)
191                        || (deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) {
192                    mIsHeadsetOn = true;
193                }
194            } else if (action.equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
195                mIsHeadsetOn = audioManager.isBluetoothA2dpOn() || audioManager.isWiredHeadsetOn();
196            } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
197                final int deviceClass = ((BluetoothDevice) intent
198                        .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).getBluetoothClass()
199                        .getDeviceClass();
200                if ((deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES)
201                        || (deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) {
202                    mIsHeadsetOn = audioManager.isWiredHeadsetOn();
203                }
204            }
205            if (isHeadsetOnPrev != mIsHeadsetOn) {
206                updateUIHeadset();
207            }
208        }
209    };
210
211    /*
212     * Declares and initializes all objects and widgets in the layouts and the CheckBox and SeekBar
213     * onchange methods on creation.
214     *
215     * (non-Javadoc)
216     *
217     * @see android.app.ActivityGroup#onCreate(android.os.Bundle)
218     */
219    @Override
220    public void onCreate(final Bundle savedInstanceState) {
221        super.onCreate(savedInstanceState);
222
223        // Init context to be used in listeners
224        mContext = this;
225
226        // Receive intent
227        // get calling intent
228        final Intent intent = getIntent();
229        mAudioSession = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION,
230                AudioEffect.ERROR_BAD_VALUE);
231        Log.v(TAG, "audio session: " + mAudioSession);
232
233        mCallingPackageName = getCallingPackage();
234
235        // check for errors
236        if (mCallingPackageName == null) {
237            Log.e(TAG, "Package name is null");
238            setResult(RESULT_CANCELED);
239            finish();
240            return;
241        }
242        setResult(RESULT_OK);
243
244        Log.v(TAG, mCallingPackageName + " (" + mAudioSession + ")");
245
246        ControlPanelEffect.initEffectsPreferences(mContext, mCallingPackageName, mAudioSession);
247
248        // query available effects
249        final Descriptor[] effects = AudioEffect.queryEffects();
250
251        // Determine available/supported effects
252        Log.v(TAG, "Available effects:");
253        for (final Descriptor effect : effects) {
254            Log.v(TAG, effect.name.toString() + ", type: " + effect.type.toString());
255
256            if (effect.type.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
257                mVirtualizerSupported = true;
258            } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) {
259                mBassBoostSupported = true;
260            } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
261                mEqualizerSupported = true;
262            } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_PRESET_REVERB)) {
263                mPresetReverbSupported = true;
264            }
265        }
266
267        setContentView(R.layout.music_main);
268        final ViewGroup viewGroup = (ViewGroup) findViewById(R.id.contentSoundEffects);
269        final View mainToggleView = findViewById(R.id.mainToggleEffectsLayout);
270
271        // Watch for button clicks and initialization.
272        if ((mVirtualizerSupported) || (mBassBoostSupported) || (mEqualizerSupported)
273                || (mPresetReverbSupported)) {
274            // Set the listener for the main enhancements toggle button.
275            // Depending on the state enable the supported effects if they were
276            // checked in the setup tab.
277            final CompoundButton toggleEffects = (CompoundButton) findViewById(R.id.mainToggleEffectsCheckBox);
278            toggleEffects.setOnCheckedChangeListener(new OnCheckedChangeListener() {
279                @Override
280                public void onCheckedChanged(final CompoundButton buttonView,
281                        final boolean isChecked) {
282
283                    // set parameter and state
284                    ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName,
285                            mAudioSession, ControlPanelEffect.Key.global_enabled, isChecked);
286                    // Enable Linear layout (in scroll layout) view with all
287                    // effect contents depending on checked state
288                    setEnabledAllChilds(viewGroup, isChecked);
289                    // update UI according to headset state
290                    updateUIHeadset();
291                }
292            });
293
294            mainToggleView.setVisibility(View.VISIBLE);
295
296            // Initialize the Virtualizer elements.
297            // Set the SeekBar listener.
298            if (mVirtualizerSupported) {
299                // Show msg when disabled slider (layout) is touched
300                findViewById(R.id.vILayout).setOnTouchListener(new OnTouchListener() {
301
302                    @Override
303                    public boolean onTouch(final View v, final MotionEvent event) {
304                        showHeadsetMsg();
305                        return false;
306                    }
307                });
308
309                final SeekBar seekbar = (SeekBar) findViewById(R.id.vIStrengthSeekBar);
310                seekbar.setMax(OpenSLESConstants.VIRTUALIZER_MAX_STRENGTH
311                        - OpenSLESConstants.VIRTUALIZER_MIN_STRENGTH);
312
313                seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
314                    // Update the parameters while SeekBar changes and set the
315                    // effect parameter.
316
317                    @Override
318                    public void onProgressChanged(final SeekBar seekBar, final int progress,
319                            final boolean fromUser) {
320                        // set parameter and state
321                        ControlPanelEffect.setParameterInt(mContext, mCallingPackageName,
322                                mAudioSession, ControlPanelEffect.Key.virt_strength, progress);
323                    }
324
325                    // If slider pos was 0 when starting re-enable effect
326                    @Override
327                    public void onStartTrackingTouch(final SeekBar seekBar) {
328                        if (seekBar.getProgress() == 0) {
329                            ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName,
330                                    mAudioSession, ControlPanelEffect.Key.virt_enabled, true);
331                        }
332                    }
333
334                    // If slider pos = 0 when stopping disable effect
335                    @Override
336                    public void onStopTrackingTouch(final SeekBar seekBar) {
337                        if (seekBar.getProgress() == 0) {
338                            // disable
339                            ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName,
340                                    mAudioSession, ControlPanelEffect.Key.virt_enabled, false);
341                        }
342                    }
343                });
344            }
345
346            // Initialize the Bass Boost elements.
347            // Set the SeekBar listener.
348            if (mBassBoostSupported) {
349                // Show msg when disabled slider (layout) is touched
350                findViewById(R.id.bBLayout).setOnTouchListener(new OnTouchListener() {
351
352                    @Override
353                    public boolean onTouch(final View v, final MotionEvent event) {
354                        showHeadsetMsg();
355                        return false;
356                    }
357                });
358
359                final SeekBar seekbar = (SeekBar) findViewById(R.id.bBStrengthSeekBar);
360                seekbar.setMax(OpenSLESConstants.BASSBOOST_MAX_STRENGTH
361                        - OpenSLESConstants.BASSBOOST_MIN_STRENGTH);
362
363                seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
364                    // Update the parameters while SeekBar changes and set the
365                    // effect parameter.
366
367                    @Override
368                    public void onProgressChanged(final SeekBar seekBar, final int progress,
369                            final boolean fromUser) {
370                        // set parameter and state
371                        ControlPanelEffect.setParameterInt(mContext, mCallingPackageName,
372                                mAudioSession, ControlPanelEffect.Key.bb_strength, progress);
373                    }
374
375                    // If slider pos was 0 when starting re-enable effect
376                    @Override
377                    public void onStartTrackingTouch(final SeekBar seekBar) {
378                        if (seekBar.getProgress() == 0) {
379                            ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName,
380                                    mAudioSession, ControlPanelEffect.Key.bb_enabled, true);
381                        }
382                    }
383
384                    // If slider pos = 0 when stopping disable effect
385                    @Override
386                    public void onStopTrackingTouch(final SeekBar seekBar) {
387                        if (seekBar.getProgress() == 0) {
388                            // disable
389                            ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName,
390                                    mAudioSession, ControlPanelEffect.Key.bb_enabled, false);
391                        }
392
393                    }
394                });
395            }
396
397            // Initialize the Equalizer elements.
398            if (mEqualizerSupported) {
399                final View view = findViewById(R.id.eqLayout);
400                view.setOnClickListener(new OnClickListener() {
401
402                    @Override
403                    public void onClick(final View v) {
404                        showDialog(DIALOG_EQUALIZER);
405                    }
406                });
407            }
408
409            // Initialize the Preset Reverb elements.
410            // Set Spinner listeners.
411            if (mPresetReverbSupported) {
412                final View view = findViewById(R.id.eRLayout);
413                view.setOnClickListener(new OnClickListener() {
414
415                    @Override
416                    public void onClick(final View v) {
417                        showDialog(DIALOG_PRESET_REVERB);
418                    }
419                });
420            }
421
422            // init reset defaults
423            final View view = findViewById(R.id.resetDefaultsLayout);
424            view.setOnClickListener(new OnClickListener() {
425
426                @Override
427                public void onClick(final View v) {
428                    showDialog(DIALOG_RESET_DEFAULTS);
429                }
430            });
431        } else {
432            viewGroup.setVisibility(View.GONE);
433            mainToggleView.setVisibility(View.GONE);
434            ((TextView) findViewById(R.id.noEffectsTextView)).setVisibility(View.VISIBLE);
435        }
436    }
437
438    /*
439     * (non-Javadoc)
440     *
441     * @see android.app.Activity#onResume()
442     */
443    @Override
444    protected void onResume() {
445        super.onResume();
446        if ((mVirtualizerSupported) || (mBassBoostSupported) || (mEqualizerSupported)
447                || (mPresetReverbSupported)) {
448            // Listen for broadcast intents that might affect the onscreen UI for headset.
449            final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
450            intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
451            intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
452            intentFilter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
453            registerReceiver(mReceiver, intentFilter);
454
455            // Check if wired or Bluetooth headset is connected/on
456            final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
457            mIsHeadsetOn = (audioManager.isWiredHeadsetOn() || audioManager.isBluetoothA2dpOn());
458            Log.v(TAG, "onResume: mIsHeadsetOn : " + mIsHeadsetOn);
459
460            // Update UI
461            updateUI();
462        }
463    }
464
465    /*
466     * (non-Javadoc)
467     *
468     * @see android.app.Activity#onPause()
469     */
470    @Override
471    protected void onPause() {
472        super.onPause();
473
474        // Unregister for broadcast intents. (These affect the visible UI,
475        // so we only care about them while we're in the foreground.)
476        unregisterReceiver(mReceiver);
477    }
478
479    /*
480     * Create dialogs for about, EQ preset control, PR and reset to default (alert) dialogs
481     *
482     * (non-Javadoc)
483     *
484     * @see android.app.Activity#onCreateDialog(int)
485     */
486    @Override
487    protected Dialog onCreateDialog(final int id) {
488        final AlertDialog alertDialog;
489        switch (id) {
490        case DIALOG_EQUALIZER: {
491            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
492            builder.setTitle(R.string.eq_dialog_title);
493            builder.setSingleChoiceItems(getEQPresetStrings(), -1,
494                    new DialogInterface.OnClickListener() {
495                        @Override
496                        public void onClick(final DialogInterface dialog, final int item) {
497                            if (item != mEQPresetPrevious) {
498                                equalizerSetPreset(item);
499                                final ListView listView = ((AlertDialog) dialog).getListView();
500                                // For the user preset, where EQ sliders need to be shown at the
501                                // bottom of the list when selected, the footer view of a list is
502                                // used to display them in.
503                                // This footer view will then be added or removed from the list
504                                // depending on whether user preset is selected or not.
505                                // Using transcript mode to scroll to bottom when in user
506                                if (!isEqualizerUserPreset(item)) {
507                                    listView.removeFooterView(mEqualizerView);
508                                    listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_DISABLED);
509                                } else {
510                                    listView.addFooterView(mEqualizerView);
511                                    listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
512                                }
513                            }
514                            mEQPresetPrevious = item;
515                        }
516                    });
517            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
518                @Override
519                public void onClick(final DialogInterface dialog, final int whichButton) {
520                    final ListView listView = ((AlertDialog) dialog).getListView();
521                    final int newPreset = listView.getCheckedItemPosition();
522                    equalizerSetPreset(newPreset);
523                    ((TextView) findViewById(R.id.eqPresetsTitleTextView))
524                            .setText(getString(R.string.eq_title) + " "
525                                    + listView.getItemAtPosition(newPreset).toString());
526                }
527            });
528            builder.setNegativeButton(android.R.string.cancel,
529                    new DialogInterface.OnClickListener() {
530                        @Override
531                        public void onClick(final DialogInterface dialog, final int whichButton) {
532                            dialog.cancel();
533                        }
534                    });
535            builder.setOnCancelListener(new OnCancelListener() {
536                @Override
537                public void onCancel(final DialogInterface dialog) {
538                    if (!isEqualizerUserPreset(mEQPreset)) {
539                        final ListView listView = ((AlertDialog) dialog).getListView();
540                        listView.removeFooterView(mEqualizerView);
541                    }
542                    equalizerSetPreset(mEQPreset);
543                    final int[] presetUserBandLevels = ControlPanelEffect.getParameterIntArray(
544                            mContext, mCallingPackageName, mAudioSession,
545                            ControlPanelEffect.Key.eq_preset_user_band_level);
546                    short band = 0;
547                    for (final int bandLevel : mEQPresetUserBandLevelsPrev) {
548                        if (bandLevel != presetUserBandLevels[band]) {
549                            if (!isEqualizerUserPreset(mEQPreset)) {
550                                ControlPanelEffect.setParameterInt(mContext, mCallingPackageName,
551                                        mAudioSession,
552                                        ControlPanelEffect.Key.eq_preset_user_band_level,
553                                        bandLevel, band);
554                            } else {
555                                equalizerBandUpdate(band, (short) bandLevel);
556                            }
557                        }
558                        band++;
559                    }
560                }
561            });
562
563            alertDialog = builder.create();
564            final LayoutInflater factory = LayoutInflater.from(this);
565            mEqualizerView = factory.inflate(R.layout.music_eq, null);
566            equalizerInit();
567            final ListView listView = alertDialog.getListView();
568            // Add empty footer view
569            listView.addFooterView(mEqualizerView);
570
571            mEQPresetUserPos = getEQPresetStrings().length - 1;
572            break;
573        }
574        case DIALOG_PRESET_REVERB: {
575            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
576            builder.setTitle(R.string.pr_dialog_title);
577            builder.setSingleChoiceItems(PRESETREVERBPRESETSTRINGS, -1,
578                    new DialogInterface.OnClickListener() {
579                        @Override
580                        public void onClick(final DialogInterface dialog, final int item) {
581                            presetReverbSetPreset(item);
582                        }
583                    });
584            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
585                @Override
586                public void onClick(final DialogInterface dialog, final int whichButton) {
587                    final ListView listView = ((AlertDialog) dialog).getListView();
588                    final int newPreset = listView.getCheckedItemPosition();
589                    presetReverbSetPreset(newPreset);
590                    ((TextView) findViewById(R.id.eRPresetsTitleTextView))
591                            .setText(getString(R.string.pr_title) + " "
592                                    + listView.getItemAtPosition(newPreset).toString());
593                }
594            });
595            builder.setNegativeButton(android.R.string.cancel,
596                    new DialogInterface.OnClickListener() {
597                        @Override
598                        public void onClick(final DialogInterface dialog, final int whichButton) {
599                            dialog.cancel();
600                        }
601                    });
602            builder.setOnCancelListener(new OnCancelListener() {
603                @Override
604                public void onCancel(final DialogInterface dialog) {
605                    presetReverbSetPreset(mPRPreset);
606                }
607            });
608
609            alertDialog = builder.create();
610            break;
611        }
612        case DIALOG_RESET_DEFAULTS: {
613            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
614            builder.setTitle(R.string.reset_defaults_dialog_title);
615            builder.setMessage(getString(R.string.reset_defaults_dialog_message));
616            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
617                @Override
618                public void onClick(final DialogInterface dialog, final int whichButton) {
619                    ControlPanelEffect.setEffectDefaults(mContext, mCallingPackageName,
620                            mAudioSession);
621                    updateUI();
622                }
623            });
624            builder.setNegativeButton(android.R.string.cancel, null);
625            alertDialog = builder.create();
626            break;
627        }
628        default:
629            Log.e(TAG, "onCreateDialog invalid Dialog id: " + id);
630            alertDialog = null;
631            break;
632        }
633        return alertDialog;
634    }
635
636    /*
637     * Updates dialog (selections) before they are shown if necessary
638     *
639     * (non-Javadoc)
640     *
641     * @see android.app.Activity#onPrepareDialog(int, android.app.Dialog, android.os.Bundle)
642     */
643    @Override
644    protected void onPrepareDialog(final int id, final Dialog dialog, final Bundle args) {
645        switch (id) {
646        case DIALOG_EQUALIZER: {
647            mEQPreset = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName,
648                    mAudioSession, ControlPanelEffect.Key.eq_current_preset);
649            mEQPresetPrevious = mEQPreset;
650            mEQPresetUserBandLevelsPrev = ControlPanelEffect.getParameterIntArray(mContext,
651                    mCallingPackageName, mAudioSession,
652                    ControlPanelEffect.Key.eq_preset_user_band_level);
653            final ListView listView = ((AlertDialog) dialog).getListView();
654            listView.setItemChecked(mEQPreset, true);
655            listView.setSelection(mEQPreset);
656
657            if (isEqualizerUserPreset(mEQPreset)) {
658                if (listView.getFooterViewsCount() == 0) {
659                    listView.addFooterView(mEqualizerView);
660                    listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
661                }
662                equalizerUpdateDisplay();
663            } else {
664                // FIXME: because of a probable bug in Android removeFooterView, we need to catch
665                // NPE which is sometimes thrown from inside removeFooterView (encountered only in
666                // Honeycomb).
667                // Should ideally be be avoided otherwise.
668                try {
669                    listView.removeFooterView(mEqualizerView);
670                    listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_DISABLED);
671                } catch (final NullPointerException e) {
672                    Log.w(TAG, "onPrepareDialog: DIALOG_EQUALIZER: removeFooterView: " + e);
673                }
674            }
675
676            break;
677        }
678        case DIALOG_PRESET_REVERB: {
679            mPRPreset = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName,
680                    mAudioSession, ControlPanelEffect.Key.pr_current_preset);
681            final ListView listView = ((AlertDialog) dialog).getListView();
682            listView.setItemChecked(mPRPreset, true);
683            listView.setSelection(mPRPreset);
684            break;
685        }
686        case DIALOG_RESET_DEFAULTS: {
687            break;
688        }
689        default:
690            Log.e(TAG, "onPrepareDialog invalid Dialog id: " + id);
691            break;
692        }
693    }
694
695    /**
696     * En/disables all childs for a given view. For linear and relative layout childs do this
697     * recursively
698     *
699     * @param view
700     * @param enabled
701     */
702    private void setEnabledAllChilds(final ViewGroup viewGroup, final boolean enabled) {
703        final int count = viewGroup.getChildCount();
704        for (int i = 0; i < count; i++) {
705            final View view = viewGroup.getChildAt(i);
706            if ((view instanceof LinearLayout) || (view instanceof RelativeLayout)) {
707                final ViewGroup vg = (ViewGroup) view;
708                setEnabledAllChilds(vg, enabled);
709            }
710            view.setEnabled(enabled);
711        }
712    }
713
714    /**
715     * Updates UI (checkbox, seekbars, enabled states) according to the current stored preferences.
716     */
717    private void updateUI() {
718        final boolean isEnabled = ControlPanelEffect.getParameterBoolean(mContext,
719                mCallingPackageName, mAudioSession, ControlPanelEffect.Key.global_enabled);
720        ((CompoundButton) findViewById(R.id.mainToggleEffectsCheckBox)).setChecked(isEnabled);
721        setEnabledAllChilds((ViewGroup) findViewById(R.id.contentSoundEffects), isEnabled);
722        updateUIHeadset();
723
724        if (mVirtualizerSupported) {
725            ((SeekBar) findViewById(R.id.vIStrengthSeekBar)).setProgress(ControlPanelEffect
726                    .getParameterInt(mContext, mCallingPackageName, mAudioSession,
727                            ControlPanelEffect.Key.virt_strength));
728        }
729        if (mBassBoostSupported) {
730            ((SeekBar) findViewById(R.id.bBStrengthSeekBar)).setProgress(ControlPanelEffect
731                    .getParameterInt(mContext, mCallingPackageName, mAudioSession,
732                            ControlPanelEffect.Key.bb_strength));
733        }
734        if (mEqualizerSupported) {
735            String [] presets = getEQPresetStrings();
736            int idx = ControlPanelEffect.getParameterInt(mContext,
737                                    mCallingPackageName, mAudioSession,
738                                    ControlPanelEffect.Key.eq_current_preset);
739            if (idx >= presets.length) {
740                idx = 0;
741            }
742            ((TextView) findViewById(R.id.eqPresetsTitleTextView))
743                    .setText(getString(R.string.eq_title)
744                            + " "
745                            + presets[idx]);
746        }
747        if (mPresetReverbSupported) {
748            ((TextView) findViewById(R.id.eRPresetsTitleTextView))
749                    .setText(getString(R.string.pr_title)
750                            + " "
751                            + PRESETREVERBPRESETSTRINGS[ControlPanelEffect.getParameterInt(
752                                    mContext, mCallingPackageName, mAudioSession,
753                                    ControlPanelEffect.Key.pr_current_preset)]);
754        }
755    }
756
757    /**
758     * Updates UI for headset mode. En/disable VI and BB controls depending on headset state
759     * (on/off) if effects are on. Do the inverse for their layouts so they can take over
760     * control/events.
761     */
762    private void updateUIHeadset() {
763        if (((CompoundButton) findViewById(R.id.mainToggleEffectsCheckBox)).isChecked()) {
764            ((TextView) findViewById(R.id.vIStrengthText)).setEnabled(mIsHeadsetOn);
765            ((SeekBar) findViewById(R.id.vIStrengthSeekBar)).setEnabled(mIsHeadsetOn);
766            findViewById(R.id.vILayout).setEnabled(!mIsHeadsetOn);
767            ((TextView) findViewById(R.id.bBStrengthText)).setEnabled(mIsHeadsetOn);
768            ((SeekBar) findViewById(R.id.bBStrengthSeekBar)).setEnabled(mIsHeadsetOn);
769            findViewById(R.id.bBLayout).setEnabled(!mIsHeadsetOn);
770        }
771    }
772
773    /**
774     * Initializes the equalizer elements. Set the SeekBars and Spinner listeners.
775     */
776    private void equalizerInit() {
777        // Initialize the N-Band Equalizer elements.
778        mNumberEqualizerBands = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName,
779                mAudioSession, ControlPanelEffect.Key.eq_num_bands);
780        mEQPresetUserBandLevelsPrev = ControlPanelEffect.getParameterIntArray(mContext,
781                mCallingPackageName, mAudioSession,
782                ControlPanelEffect.Key.eq_preset_user_band_level);
783        final int[] centerFreqs = ControlPanelEffect.getParameterIntArray(mContext,
784                mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_center_freq);
785        final int[] bandLevelRange = ControlPanelEffect.getParameterIntArray(mContext,
786                mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_level_range);
787        mEqualizerMinBandLevel = bandLevelRange[0];
788        final int mEqualizerMaxBandLevel = bandLevelRange[1];
789
790        for (int band = 0; band < mNumberEqualizerBands; band++) {
791            // Unit conversion from mHz to Hz and use k prefix if necessary to display
792            final int centerFreq = centerFreqs[band] / 1000;
793            float centerFreqHz = centerFreq;
794            String unitPrefix = "";
795            if (centerFreqHz >= 1000) {
796                centerFreqHz = centerFreqHz / 1000;
797                unitPrefix = "k";
798            }
799            ((TextView) mEqualizerView.findViewById(EQViewElementIds[band][0])).setText(
800                    format("%.0f ", centerFreqHz) + unitPrefix + "Hz");
801            mEqualizerSeekBar[band] = (SeekBar) mEqualizerView
802                    .findViewById(EQViewElementIds[band][1]);
803            mEqualizerSeekBar[band].setMax(mEqualizerMaxBandLevel - mEqualizerMinBandLevel);
804            mEqualizerValueText[band] = (TextView) mEqualizerView
805                    .findViewById(EQViewElementIds[band][2]);
806            mEqualizerSeekBar[band].setOnSeekBarChangeListener(this);
807        }
808
809        // Hide the inactive Equalizer bands.
810        for (int band = mNumberEqualizerBands; band < EQUALIZER_MAX_BANDS; band++) {
811            // CenterFreq text
812            mEqualizerView.findViewById(EQViewElementIds[band][0]).setVisibility(View.GONE);
813            // SeekBar
814            mEqualizerView.findViewById(EQViewElementIds[band][1]).setVisibility(View.GONE);
815            // Value text
816            mEqualizerView.findViewById(EQViewElementIds[band][2]).setVisibility(View.GONE);
817        }
818    }
819
820    private String format(String format, Object... args) {
821        mFormatBuilder.setLength(0);
822        mFormatter.format(format, args);
823        return mFormatBuilder.toString();
824    }
825
826    /*
827     * For the EQ Band SeekBars
828     *
829     * (non-Javadoc)
830     *
831     * @see android.widget.SeekBar.OnSeekBarChangeListener#onProgressChanged(android
832     * .widget.SeekBar, int, boolean)
833     */
834
835    @Override
836    public void onProgressChanged(final SeekBar seekbar, final int progress, final boolean fromUser) {
837        final int id = seekbar.getId();
838
839        for (short band = 0; band < mNumberEqualizerBands; band++) {
840            if (id == EQViewElementIds[band][1]) {
841                final short level = (short) (progress + mEqualizerMinBandLevel);
842                equalizerBandUpdateDisplay(band, level);
843                if (fromUser) {
844                    equalizerBandUpdate(band, level);
845                }
846                break;
847            }
848        }
849    }
850
851    /*
852     * (non-Javadoc)
853     *
854     * @see android.widget.SeekBar.OnSeekBarChangeListener#onStartTrackingTouch(android
855     * .widget.SeekBar)
856     */
857
858    @Override
859    public void onStartTrackingTouch(final SeekBar seekbar) {
860        // Do nothing
861    }
862
863    /*
864     * Updates the EQ display when the user stops changing.
865     *
866     * (non-Javadoc)
867     *
868     * @see android.widget.SeekBar.OnSeekBarChangeListener#onStopTrackingTouch(android
869     * .widget.SeekBar)
870     */
871
872    @Override
873    public void onStopTrackingTouch(final SeekBar seekbar) {
874        equalizerUpdateDisplay();
875    }
876
877    /**
878     * Updates the EQ by getting the parameters.
879     */
880    private void equalizerUpdateDisplay() {
881        // Update and show the active N-Band Equalizer bands.
882        final int[] bandLevels = ControlPanelEffect.getParameterIntArray(mContext,
883                mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_band_level);
884        for (short band = 0; band < mNumberEqualizerBands; band++) {
885            final int level = bandLevels[band];
886            equalizerBandUpdateDisplay(band, (short) level);
887            final int progress = level - mEqualizerMinBandLevel;
888            mEqualizerSeekBar[band].setProgress(progress);
889        }
890    }
891
892    /**
893     * Updates/sets a given EQ band level.
894     *
895     * @param band
896     *            Band id
897     * @param level
898     *            EQ band level
899     */
900    private void equalizerBandUpdate(final short band, final short level) {
901        ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession,
902                ControlPanelEffect.Key.eq_band_level, level, band);
903    }
904
905    /**
906     * Updates the EQ band level display.
907     *
908     * @param band
909     *            Band id
910     * @param level
911     *            EQ band level
912     */
913    private void equalizerBandUpdateDisplay(final short band, final short level) {
914        final int dBValue = level / 100;
915        mEqualizerValueText[band].setText(format("%d dB", dBValue));
916    }
917
918    /**
919     * Sets the given EQ preset.
920     *
921     * @param preset
922     *            EQ preset id.
923     */
924    private void equalizerSetPreset(final short preset) {
925        ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession,
926                ControlPanelEffect.Key.eq_current_preset, preset);
927        if (isEqualizerUserPreset(preset)) {
928            equalizerUpdateDisplay();
929        }
930    }
931
932    /**
933     * Sets the given EQ preset.
934     *
935     * @param preset
936     *            EQ preset id.
937     */
938    private void equalizerSetPreset(final int preset) {
939        equalizerSetPreset((short) preset);
940    }
941
942    /**
943     * Checks if an User EQ preset is set.
944     */
945    private boolean isEqualizerUserPreset(final int preset) {
946        return (preset == mEQPresetUserPos);
947    }
948
949    /**
950     * Gets the EQ preset names
951     *
952     * @return
953     */
954    private final String[] getEQPresetStrings() {
955        final int numPresets = ControlPanelEffect.getParameterInt(mContext, mCallingPackageName,
956                mAudioSession, ControlPanelEffect.Key.eq_num_presets);
957        // Fill array with presets from AudioEffects call.
958        // allocate a space for 2 extra strings (CI Extreme & User)
959        final String[] eQViewPresetStrings = new String[numPresets + 2];
960        for (short i = 0; i < numPresets; i++) {
961            eQViewPresetStrings[i] = ControlPanelEffect.getParameterString(mContext,
962                    mCallingPackageName, mAudioSession, ControlPanelEffect.Key.eq_preset_name, i);
963        }
964
965        eQViewPresetStrings[numPresets] = getString(R.string.ci_extreme);
966        eQViewPresetStrings[numPresets + 1] = getString(R.string.user);
967        return eQViewPresetStrings;
968    }
969
970    /**
971     * Sets the given PR preset.
972     *
973     * @param preset
974     *            PR preset id.
975     */
976    private void presetReverbSetPreset(final short preset) {
977        ControlPanelEffect.setParameterInt(mContext, mCallingPackageName, mAudioSession,
978                ControlPanelEffect.Key.pr_current_preset, preset);
979    }
980
981    /**
982     * Sets the given PR preset.
983     *
984     * @param preset
985     *            PR preset id.
986     */
987    private void presetReverbSetPreset(final int preset) {
988        presetReverbSetPreset((short) preset);
989    }
990
991    /**
992     * Show msg that headset needs to be plugged.
993     */
994    private void showHeadsetMsg() {
995        final Context context = getApplicationContext();
996        final int duration = Toast.LENGTH_SHORT;
997
998        final Toast toast = Toast.makeText(context, getString(R.string.headset_plug), duration);
999        toast.setGravity(Gravity.CENTER, toast.getXOffset() / 2, toast.getYOffset() / 2);
1000        toast.show();
1001    }
1002}
1003