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