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