summaryrefslogtreecommitdiffstats
path: root/src/org/cyanogenmod/audiofx/activity
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/cyanogenmod/audiofx/activity')
-rw-r--r--src/org/cyanogenmod/audiofx/activity/ActivityMusic.java213
-rw-r--r--src/org/cyanogenmod/audiofx/activity/ControlPanelPicker.java127
-rw-r--r--src/org/cyanogenmod/audiofx/activity/EqualizerManager.java649
-rw-r--r--src/org/cyanogenmod/audiofx/activity/MasterConfigControl.java382
-rw-r--r--src/org/cyanogenmod/audiofx/activity/StateCallbacks.java168
5 files changed, 1539 insertions, 0 deletions
diff --git a/src/org/cyanogenmod/audiofx/activity/ActivityMusic.java b/src/org/cyanogenmod/audiofx/activity/ActivityMusic.java
new file mode 100644
index 0000000..bd12702
--- /dev/null
+++ b/src/org/cyanogenmod/audiofx/activity/ActivityMusic.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.cyanogenmod.audiofx.activity;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewStub;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import com.cyanogen.ambient.analytics.Event;
+import org.cyanogenmod.audiofx.AudioFxApplication;
+import org.cyanogenmod.audiofx.Constants;
+import org.cyanogenmod.audiofx.R;
+import org.cyanogenmod.audiofx.fragment.AudioFxFragment;
+import org.cyanogenmod.audiofx.knobs.KnobCommander;
+import org.cyanogenmod.audiofx.service.AudioFxService;
+import org.cyanogenmod.audiofx.stats.AppState;
+import org.cyanogenmod.audiofx.stats.UserSession;
+
+public class ActivityMusic extends Activity {
+
+ private static final String TAG = ActivityMusic.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ public static final String TAG_AUDIOFX = "audiofx";
+ public static final String EXTRA_CALLING_PACKAGE = "audiofx::extra_calling_package";
+
+ private CheckBox mCurrentDeviceToggle;
+ MasterConfigControl mConfig;
+ String mCallingPackage;
+
+ private boolean mWaitingForService = true;
+ private SharedPreferences.OnSharedPreferenceChangeListener mServiceReadyObserver;
+
+ private CompoundButton.OnCheckedChangeListener mGlobalEnableToggleListener
+ = new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(final CompoundButton buttonView,
+ final boolean isChecked) {
+ if (UserSession.getInstance() != null) {
+ UserSession.getInstance().deviceEnabledDisabled();
+ }
+ mConfig.setCurrentDeviceEnabled(isChecked);
+ }
+ };
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ if (DEBUG)
+ Log.i(TAG, "onCreate() called with "
+ + "savedInstanceState = [" + savedInstanceState + "]");
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ mCallingPackage = getIntent().getStringExtra(EXTRA_CALLING_PACKAGE);
+ Log.i(TAG, "calling package: " + mCallingPackage);
+
+ mConfig = MasterConfigControl.getInstance(this);
+
+ final SharedPreferences globalPrefs = Constants.getGlobalPrefs(this);
+
+ mWaitingForService = !defaultsSetup();
+ if (mWaitingForService) {
+ Log.w(TAG, "waiting for service.");
+ mServiceReadyObserver = new SharedPreferences.OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+ String key) {
+ if (key.equals(Constants.SAVED_DEFAULTS) && defaultsSetup()) {
+ sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
+ mConfig.onResetDefaults();
+ init(savedInstanceState);
+
+ mWaitingForService = false;
+ invalidateOptionsMenu();
+ mServiceReadyObserver = null;
+ }
+ }
+ };
+ globalPrefs.registerOnSharedPreferenceChangeListener(mServiceReadyObserver);
+ startService(new Intent(ActivityMusic.this, AudioFxService.class));
+ // TODO add loading fragment if service initialization takes too long
+ } else {
+ init(savedInstanceState);
+ }
+ }
+
+ private boolean defaultsSetup() {
+ final int targetVersion = Constants.CURRENT_PREFS_INT_VERSION;
+ final SharedPreferences prefs = Constants.getGlobalPrefs(this);
+ final int currentVersion = prefs.getInt(Constants.AUDIOFX_GLOBAL_PREFS_VERSION_INT, 0);
+ final boolean defaultsSaved = prefs.getBoolean(Constants.SAVED_DEFAULTS, false);
+ return defaultsSaved && currentVersion >= targetVersion;
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ // should null it out if one was there, compat redirector with package will go through onCreate
+ mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mServiceReadyObserver != null) {
+ Constants.getGlobalPrefs(this)
+ .unregisterOnSharedPreferenceChangeListener(mServiceReadyObserver);
+ mServiceReadyObserver = null;
+ }
+ super.onDestroy();
+ }
+
+ private void init(Bundle savedInstanceState) {
+ mConfig = MasterConfigControl.getInstance(this);
+
+ ActionBar ab = getActionBar();
+ ab.setTitle(R.string.app_name);
+ ab.setDisplayShowTitleEnabled(true);
+
+ final View extraView = LayoutInflater.from(this)
+ .inflate(R.layout.action_bar_custom_components, null);
+ ActionBar.LayoutParams lp = new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT,
+ ActionBar.LayoutParams.WRAP_CONTENT, Gravity.RIGHT | Gravity.CENTER_VERTICAL);
+ ab.setCustomView(extraView, lp);
+ ab.setDisplayShowCustomEnabled(true);
+
+ mCurrentDeviceToggle = (CheckBox) ab.getCustomView().findViewById(R.id.global_toggle);
+ mCurrentDeviceToggle.setOnCheckedChangeListener(mGlobalEnableToggleListener);
+
+ if (savedInstanceState == null && findViewById(R.id.main_fragment) != null) {
+ getFragmentManager()
+ .beginTransaction()
+ .add(R.id.main_fragment, new AudioFxFragment(), TAG_AUDIOFX)
+ .commit();
+ }
+ applyOemDecor();
+ }
+
+ private void applyOemDecor() {
+ ActionBar ab = getActionBar();
+ if (mConfig.hasMaxxAudio()) {
+ ab.setSubtitle(R.string.powered_by_maxx_audio);
+ } else if (mConfig.hasDts()) {
+ final ViewStub stub = (ViewStub) ab.getCustomView().findViewById(R.id.logo_stub);
+ stub.setLayoutResource(R.layout.action_bar_dts_logo);
+ stub.inflate();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ if (DEBUG) Log.i(TAG, "onResume() called with " + "");
+ super.onResume();
+
+ // initiate a new session
+ new UserSession(mCallingPackage);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ if (DEBUG) Log.d(TAG, "Session: " + UserSession.getInstance());
+
+ final Event.Builder builder = new Event.Builder("session", "ended");
+ UserSession.getInstance().append(builder);
+ AppState.appendState(mConfig, KnobCommander.getInstance(this), builder);
+ ((AudioFxApplication) getApplicationContext()).sendEvent(builder.build());
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (DEBUG) Log.i(TAG, "onConfigurationChanged() called with "
+ + "newConfig = [" + newConfig + "]");
+ if (newConfig.orientation != getResources().getConfiguration().orientation) {
+ mCurrentDeviceToggle = null;
+ }
+ }
+
+ public void setGlobalToggleChecked(boolean checked) {
+ if (mCurrentDeviceToggle != null) {
+ mCurrentDeviceToggle.setOnCheckedChangeListener(null);
+ mCurrentDeviceToggle.setChecked(checked);
+ mCurrentDeviceToggle.setOnCheckedChangeListener(mGlobalEnableToggleListener);
+ }
+ }
+
+ public CompoundButton getGlobalSwitch() {
+ return mCurrentDeviceToggle;
+ }
+}
diff --git a/src/org/cyanogenmod/audiofx/activity/ControlPanelPicker.java b/src/org/cyanogenmod/audiofx/activity/ControlPanelPicker.java
new file mode 100644
index 0000000..03e6b06
--- /dev/null
+++ b/src/org/cyanogenmod/audiofx/activity/ControlPanelPicker.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.cyanogenmod.audiofx.activity;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.internal.app.AlertController.AlertParams.OnPrepareListViewListener;
+import org.cyanogenmod.audiofx.Compatibility;
+import org.cyanogenmod.audiofx.Compatibility.Service;
+
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.media.audiofx.AudioEffect;
+import android.os.Bundle;
+import android.widget.ListView;
+import org.cyanogenmod.audiofx.R;
+
+import java.util.List;
+
+/**
+ * shows a dialog that lets the user switch between control panels
+ */
+public class ControlPanelPicker extends AlertActivity implements OnClickListener, OnPrepareListViewListener {
+
+
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ String [] cols = new String [] { "_id", "title", "package", "name" };
+ MatrixCursor c = new MatrixCursor(cols);
+
+ PackageManager pmgr = getPackageManager();
+ Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL);
+ List<ResolveInfo> ris = pmgr.queryIntentActivities(i, PackageManager.GET_DISABLED_COMPONENTS);
+ SharedPreferences pref = getSharedPreferences("musicfx", MODE_PRIVATE);
+ String savedDefPackage = pref.getString("defaultpanelpackage", null);
+ String savedDefName = pref.getString("defaultpanelname", null);
+ int cnt = -1;
+ int defpanelidx = 0;
+ for (ResolveInfo foo: ris) {
+ if (foo.activityInfo.name.equals(Compatibility.Redirector.class.getName())) {
+ continue;
+ }
+ CharSequence name = pmgr.getApplicationLabel(foo.activityInfo.applicationInfo);
+ c.addRow(new Object [] { 0, name, foo.activityInfo.packageName, foo.activityInfo.name });
+ cnt += 1;
+ if (foo.activityInfo.name.equals(savedDefName) &&
+ foo.activityInfo.packageName.equals(savedDefPackage) &&
+ foo.activityInfo.enabled) {
+ // mark as default in the list
+ defpanelidx = cnt;
+ }
+ }
+
+ final AlertController.AlertParams p = mAlertParams;
+ p.mCursor = c;
+ p.mOnClickListener = mItemClickListener;
+ p.mLabelColumn = "title";
+ p.mIsSingleChoice = true;
+ p.mPositiveButtonText = getString(getOkStringResId());
+ p.mPositiveButtonListener = this;
+ p.mNegativeButtonText = getString(getCancelStringResId());
+ p.mOnPrepareListViewListener = this;
+ p.mTitle = getString(R.string.picker_title);
+ p.mCheckedItem = defpanelidx;
+
+ setupAlert();
+ }
+
+ private int getOkStringResId() {
+ return getResources().getIdentifier("ok", "string", "android");
+ }
+
+ private int getCancelStringResId() {
+ return getResources().getIdentifier("cancel", "string", "android");
+ }
+
+ private DialogInterface.OnClickListener mItemClickListener =
+ new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int which) {
+ // Save the position of most recently clicked item
+ mAlertParams.mCheckedItem = which;
+ }
+
+ };
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ // set new default
+ Intent updateIntent = new Intent(this, Service.class);
+ Cursor c = mAlertParams.mCursor;
+ c.moveToPosition(mAlertParams.mCheckedItem);
+ updateIntent.putExtra("defPackage", c.getString(2));
+ updateIntent.putExtra("defName", c.getString(3));
+ startService(updateIntent);
+ }
+ }
+
+ @Override
+ public void onPrepareListView(ListView listView) {
+ //mAlertParams.mCheckedItem = mDefPanelPos;
+ }
+}
diff --git a/src/org/cyanogenmod/audiofx/activity/EqualizerManager.java b/src/org/cyanogenmod/audiofx/activity/EqualizerManager.java
new file mode 100644
index 0000000..b040b71
--- /dev/null
+++ b/src/org/cyanogenmod/audiofx/activity/EqualizerManager.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.cyanogenmod.audiofx.activity;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.widget.CompoundButton;
+
+import org.cyanogenmod.audiofx.Constants;
+import org.cyanogenmod.audiofx.Preset;
+import org.cyanogenmod.audiofx.R;
+import org.cyanogenmod.audiofx.eq.EqUtils;
+import org.cyanogenmod.audiofx.service.AudioFxService;
+import org.cyanogenmod.audiofx.stats.UserSession;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class EqualizerManager {
+
+ private static final String TAG = EqualizerManager.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final MasterConfigControl mConfig;
+ private final Context mContext;
+
+ private float mMinFreq;
+ private float mMaxFreq;
+
+ private float mMinDB;
+ private float mMaxDB;
+ private int mNumBands;
+ private CompoundButton.OnCheckedChangeListener mLockChangeListener;
+
+ /*
+ * presets from the library custom preset.
+ */
+ private int mPredefinedPresets;
+ private float[] mCenterFreqs;
+ private float[] mGlobalLevels;
+
+ private AtomicBoolean mAnimatingToCustom = new AtomicBoolean(false);
+
+ // whether we are in between presets, animating them and such
+ private boolean mChangingPreset = false;
+
+ private int mCurrentPreset;
+
+ private final ArrayList<Preset> mEqPresets = new ArrayList<Preset>();
+ private int mEQCustomPresetPosition;
+
+ private String mZeroedBandString;
+
+ private static final int MSG_SAVE_PRESETS = 1;
+ private static final int MSG_SEND_EQ_OVERRIDE = 2;
+
+ private Handler mHandler = new Handler(new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SAVE_PRESETS:
+ Constants.saveCustomPresets(mContext, mEqPresets);
+ break;
+ case MSG_SEND_EQ_OVERRIDE:
+ mConfig.overrideEqLevels((short)msg.arg1, (short) msg.arg2);
+ break;
+ }
+ return true;
+ }}, true);
+
+ public EqualizerManager(Context context, MasterConfigControl config) {
+ mContext = context;
+ mConfig = config;
+
+ applyDefaults();
+ }
+
+ public void applyDefaults() {
+ mEqPresets.clear();
+ // setup eq
+ int bands = Integer.parseInt(getGlobalPref("equalizer.number_of_bands", "5"));
+ final int[] centerFreqs = Constants.getCenterFreqs(mContext, bands);
+ final int[] bandLevelRange = Constants.getBandLevelRange(mContext);
+
+ float[] centerFreqsKHz = new float[centerFreqs.length];
+ for (int i = 0; i < centerFreqs.length; i++) {
+ centerFreqsKHz[i] = (float) centerFreqs[i] / 1000.0f;
+ }
+
+ mMinDB = bandLevelRange[0] / 100;
+ mMaxDB = bandLevelRange[1] / 100;
+
+ mNumBands = centerFreqsKHz.length;
+ mGlobalLevels = new float[mNumBands];
+ for (int i = 0; i < mGlobalLevels.length; i++) {
+ mGlobalLevels[i] = 0;
+ }
+
+ mZeroedBandString = EqUtils.getZeroedBandsString(getNumBands());
+
+ mCenterFreqs = Arrays.copyOf(centerFreqsKHz, mNumBands);
+ System.arraycopy(centerFreqsKHz, 0, mCenterFreqs, 0, mNumBands);
+ mMinFreq = mCenterFreqs[0] / 2;
+ mMaxFreq = (float) Math.pow(mCenterFreqs[mNumBands - 1], 2) / mCenterFreqs[mNumBands - 2] / 2;
+
+ // setup equalizer presets
+ final int numPresets = Integer.parseInt(getGlobalPref("equalizer.number_of_presets", "0"));
+
+ if (numPresets > 0) {
+ // add library-provided presets
+ String[] presetNames = getGlobalPref("equalizer.preset_names", "").split("\\|");
+ mPredefinedPresets = presetNames.length + 1; // we consider first EQ to be part of predefined
+ for (int i = 0; i < numPresets; i++) {
+ mEqPresets.add(new Preset.StaticPreset(presetNames[i], getPersistedPresetLevels(i)));
+ }
+ } else {
+ mPredefinedPresets = 1; // custom is predefined
+ }
+ // add custom preset
+ mEqPresets.add(new Preset.PermCustomPreset(mContext.getString(R.string.user),
+ getPersistedCustomLevels()));
+ mEQCustomPresetPosition = mEqPresets.size() - 1;
+
+ // restore custom prefs
+ mEqPresets.addAll(Constants.getCustomPresets(mContext, mNumBands));
+
+ // setup default preset for speaker
+ mCurrentPreset = Integer.parseInt(getPref(Constants.DEVICE_AUDIOFX_EQ_PRESET, "0"));
+ if (mCurrentPreset > mEqPresets.size() - 1) {
+ mCurrentPreset = 0;
+ }
+ setPreset(mCurrentPreset);
+ }
+
+ public boolean isUserPreset() {
+ boolean result = mCurrentPreset >= mPredefinedPresets;
+ /*if (DEBUG) {
+ Log.i(TAG, "isUserPreset(), current preset: " + mCurrentPreset);
+ Log.i(TAG, "----> predefined presets: " + mPredefinedPresets);
+ Log.d(TAG, "----> RESULT: " + result);
+ }*/
+ return result;
+ }
+
+ public boolean isCustomPreset() {
+ return mCurrentPreset == mEQCustomPresetPosition;
+ }
+
+ public CompoundButton.OnCheckedChangeListener getLockChangeListener() {
+ if (mLockChangeListener == null) {
+ mLockChangeListener = new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isUserPreset()) {
+ ((Preset.CustomPreset) mEqPresets.get(mCurrentPreset)).setLocked(isChecked);
+ }
+ }
+ };
+ }
+ return mLockChangeListener;
+ }
+
+ public boolean isChangingPresets() {
+ return mChangingPreset;
+ }
+
+ public void setChangingPresets(boolean changing) {
+ if (mChangingPreset != changing) {
+ mChangingPreset = changing;
+ if (changing) {
+ mConfig.getCallbacks().notifyEqControlStateChanged(false, false, false, false);
+ } else {
+ updateEqControls();
+ }
+ }
+ }
+
+ public boolean isAnimatingToCustom() {
+ return mAnimatingToCustom.get();
+ }
+
+ public void setAnimatingToCustom(boolean animating) {
+ mAnimatingToCustom.set(animating);
+ if (!animating) {
+ // finished animation
+ updateEqControls();
+ }
+ }
+
+ private void savePresetsDelayed() {
+ mHandler.sendEmptyMessageDelayed(MSG_SAVE_PRESETS, 500);
+ }
+
+ public int indexOf(Preset p) {
+ return mEqPresets.indexOf(p);
+ }
+
+ void onPreDeviceChanged() {
+ // need to update the current preset based on the device here.
+ int newPreset = Integer.parseInt(getPref(Constants.DEVICE_AUDIOFX_EQ_PRESET, "0"));
+ if (newPreset > mEqPresets.size() - 1) {
+ newPreset = 0;
+ }
+
+ // this should be ready to go for callbacks to query the new device preset below
+ mCurrentPreset = newPreset;
+
+ }
+
+ void onPostDeviceChanged() {
+ setPreset(mCurrentPreset, false);
+ }
+
+ public Preset getCurrentPreset() {
+ return mEqPresets.get(mCurrentPreset);
+ }
+
+ /**
+ * Copy current config levels from the current preset into custom values since the user has
+ * initiated some change. Then update the current preset to 'custom'.
+ */
+ public int copyToCustom() {
+ updateGlobalLevels(mCurrentPreset);
+ if (DEBUG) {
+ Log.w(TAG, "using levels from preset: " + mCurrentPreset + ": " + Arrays.toString(mGlobalLevels));
+ }
+
+ String levels = EqUtils.floatLevelsToString(
+ EqUtils.convertDecibelsToMillibels(
+ mEqPresets.get(mCurrentPreset).getLevels()));
+ setGlobalPref("custom", levels);
+
+ ((Preset.PermCustomPreset) mEqPresets.get(mEQCustomPresetPosition)).setLevels(mGlobalLevels);
+ if (DEBUG)
+ Log.i(TAG, "copyToCustom() wrote current preset levels to index: " + mEQCustomPresetPosition);
+ setPreset(mEQCustomPresetPosition);
+ savePresetsDelayed();
+ return mEQCustomPresetPosition;
+ }
+
+ public int addPresetFromCustom() {
+ updateGlobalLevels(mEQCustomPresetPosition);
+ if (DEBUG) {
+ Log.w(TAG, "using levels from preset: " + mCurrentPreset + ": " + Arrays.toString(mGlobalLevels));
+ }
+
+ int writtenToIndex = addPreset(mGlobalLevels);
+ if (DEBUG)
+ Log.i(TAG, "addPresetFromCustom() wrote current preset levels to index: " + writtenToIndex);
+ setPreset(writtenToIndex);
+ savePresetsDelayed();
+ return writtenToIndex;
+ }
+
+ /**
+ * Loops through all presets. And finds the first preset that can be written to.
+ * If one is not found, then one is inserted, and that new index is returned.
+ * @return the index that the levels were copied to
+ */
+ private int addPreset(float[] levels) {
+ if (UserSession.getInstance() != null) {
+ UserSession.getInstance().presetCreated();
+ }
+
+ final int customPresets = Constants.getCustomPresets(mContext, mNumBands).size();
+ // format the name so it's like "Custom <N>", start with "Custom 2"
+ final String name = String.format(mContext.getString(R.string.user_n), customPresets + 2);
+
+ Preset.CustomPreset customPreset = new Preset.CustomPreset(name, levels, false);
+ mEqPresets.add(customPreset);
+
+ mConfig.getCallbacks().notifyPresetsChanged();
+
+ return mEqPresets.size() - 1;
+ }
+
+ /**
+ * Set a new level!
+ * <p/>
+ * This call will be propogated to all listeners registered with addEqStateChangeCallback().
+ *
+ * @param band the band index the band index which changed
+ * @param dB the new decibel value
+ * @param systemChange is this change generated by the system?
+ */
+ public void setLevel(final int band, final float dB, final boolean fromSystem) {
+ if (DEBUG) Log.i(TAG, "setLevel(" + band + ", " + dB + ", " + fromSystem + ")");
+
+ mGlobalLevels[band] = dB;
+
+ if (fromSystem && !mConfig.isUserDeviceOverride()) {
+ // quickly convert decibel to millibel and send away to the service
+ mHandler.obtainMessage(MSG_SEND_EQ_OVERRIDE, band, (short) (dB * 100)).sendToTarget();
+ }
+
+ mConfig.getCallbacks().notifyBandLevelChangeChanged(band, dB, fromSystem);
+
+ if (!fromSystem) { // user is touching
+ // persist
+
+ final Preset preset = mEqPresets.get(mCurrentPreset);
+ if (preset instanceof Preset.CustomPreset) {
+ if (mAnimatingToCustom.get()) {
+ if (DEBUG) {
+ Log.d(TAG, "setLevel() not persisting new custom band becuase animating.");
+ }
+ } else {
+ ((Preset.CustomPreset) preset).setLevel(band, dB);
+ if (preset instanceof Preset.PermCustomPreset) {
+ // store these as millibels
+ String levels = EqUtils.floatLevelsToString(
+ EqUtils.convertDecibelsToMillibels(
+ preset.getLevels()));
+ setGlobalPref("custom", levels);
+ }
+ }
+ // needs to be updated immediately here for the service.
+ final String levels = EqUtils.floatLevelsToString(preset.getLevels());
+ setPref(Constants.DEVICE_AUDIOFX_EQ_PRESET_LEVELS, levels);
+
+ mConfig.updateService(AudioFxService.EQ_CHANGED);
+ }
+ savePresetsDelayed();
+ }
+ }
+
+ /**
+ * Set a new preset index.
+ * <p/>
+ * This call will be propogated to all listeners registered with addEqStateChangeCallback().
+ *
+ * @param newPresetIndex the new preset index.
+ */
+ public void setPreset(final int newPresetIndex, boolean updateBackend) {
+ mCurrentPreset = newPresetIndex;
+ updateEqControls(); // do this before callback is propogated
+
+ mConfig.getCallbacks().notifyPresetChanged(newPresetIndex);
+
+ // persist
+ setPref(Constants.DEVICE_AUDIOFX_EQ_PRESET, String.valueOf(newPresetIndex));
+
+ // update mGlobalLevels
+ float[] newlevels = getPresetLevels(newPresetIndex);
+ for (int i = 0; i < newlevels.length; i++) {
+ setLevel(i, newlevels[i], true);
+ }
+
+ setPref(Constants.DEVICE_AUDIOFX_EQ_PRESET_LEVELS, EqUtils.floatLevelsToString(newlevels));
+
+ if (updateBackend) {
+ mConfig.updateService(AudioFxService.EQ_CHANGED);
+ }
+ }
+
+ public void setPreset(final int newPresetIndex) {
+ setPreset(newPresetIndex, true);
+ }
+
+ private void updateEqControls() {
+ final boolean userPreset = isUserPreset();
+ mConfig.getCallbacks().notifyEqControlStateChanged(mEQCustomPresetPosition == mCurrentPreset,
+ userPreset, userPreset, userPreset);
+ }
+
+ /**
+ * @return Get the current preset index
+ */
+ public int getCurrentPresetIndex() {
+ return mCurrentPreset;
+ }
+
+ /*===============
+ * eq methods
+ *===============*/
+
+ public float projectX(double freq) {
+ double pos = Math.log(freq);
+ double minPos = Math.log(mMinFreq);
+ double maxPos = Math.log(mMaxFreq);
+ return (float) ((pos - minPos) / (maxPos - minPos));
+ }
+
+ public double reverseProjectX(float pos) {
+ double minPos = Math.log(mMinFreq);
+ double maxPos = Math.log(mMaxFreq);
+ return Math.exp(pos * (maxPos - minPos) + minPos);
+ }
+
+ public float projectY(double dB) {
+ double pos = (dB - mMinDB) / (mMaxDB - mMinDB);
+ return (float) (1 - pos);
+ }
+
+ public static double lin2dB(double rho) {
+ return rho != 0 ? Math.log(rho) / Math.log(10) * 20 : -99.9;
+ }
+
+ public float getMinFreq() {
+ return mMinFreq;
+ }
+
+ public float getMaxFreq() {
+ return mMaxFreq;
+ }
+
+ public float getMinDB() {
+ return mMinDB;
+ }
+
+ public float getMaxDB() {
+ return mMaxDB;
+ }
+
+ public int getNumBands() {
+ return mNumBands;
+ }
+
+ public float getCenterFreq(int band) {
+ return mCenterFreqs[band];
+ }
+
+ public float[] getCenterFreqs() {
+ return mCenterFreqs;
+ }
+
+ public float[] getLevels() {
+ return mGlobalLevels;
+ }
+
+ public float getLevel(int band) {
+ return mGlobalLevels[band];
+ }
+
+ /*===============
+ * preset methods
+ *===============*/
+
+ public float[] getPersistedPresetLevels(int presetIndex) {
+ String newLevels = null;
+
+ if (mEqPresets.size() > presetIndex
+ && mEqPresets.get(presetIndex) instanceof Preset.PermCustomPreset) {
+ return getPersistedCustomLevels();
+ } else {
+ newLevels = getGlobalPref("equalizer.preset." + presetIndex, mZeroedBandString);
+ }
+
+ // stored as millibels, convert to decibels
+ float[] levels = EqUtils.stringBandsToFloats(newLevels);
+ return EqUtils.convertMillibelsToDecibels(levels);
+ }
+
+ private float[] getPersistedCustomLevels() {
+ String newLevels = getGlobalPref("custom", mZeroedBandString);
+ // stored as millibels, convert to decibels
+ float[] levels = EqUtils.stringBandsToFloats(newLevels);
+ return EqUtils.convertMillibelsToDecibels(levels);
+ }
+
+ /**
+ * Get preset levels in decibels for a given index
+ *
+ * @param presetIndex index which to fetch preset levels for
+ * @return an array of floats[] with the given index's preset levels
+ */
+ public float[] getPresetLevels(int presetIndex) {
+ return mEqPresets.get(presetIndex).getLevels();
+ }
+
+ /**
+ * Helper method which maps a preset index to a color value.
+ *
+ * @param index the preset index which to fetch a color for
+ * @return a color which is associated with this preset.
+ */
+ public int getAssociatedPresetColorHex(int index) {
+ int r = -1;
+ index = index % mEqPresets.size();
+ if (mEqPresets.get(index) instanceof Preset.CustomPreset) {
+ r = R.color.preset_custom;
+ } else {
+ switch (index) {
+ case 0:
+ r = R.color.preset_normal;
+ break;
+ case 1:
+ r = R.color.preset_classical;
+ break;
+ case 2:
+ r = R.color.preset_dance;
+ break;
+ case 3:
+ r = R.color.preset_flat;
+ break;
+ case 4:
+ r = R.color.preset_folk;
+ break;
+ case 5:
+ r = R.color.preset_metal;
+ break;
+ case 6:
+ r = R.color.preset_hiphop;
+ break;
+ case 7:
+ r = R.color.preset_jazz;
+ break;
+ case 8:
+ r = R.color.preset_pop;
+ break;
+ case 9:
+ r = R.color.preset_rock;
+ break;
+ case 10:
+ r = R.color.preset_electronic;
+ break;
+ case 11:
+ r = R.color.preset_small_speakers;
+ break;
+ default:
+ return r;
+ }
+ }
+ return mContext.getResources().getColor(r);
+ }
+
+ /**
+ * Get total number of presets
+ *
+ * @return int value with total number of presets
+ */
+ public int getPresetCount() {
+ return mEqPresets.size();
+ }
+
+ public Preset getPreset(int index) {
+ return mEqPresets.get(index);
+ }
+
+ public String getLocalizedPresetName(int index) {
+ // already localized
+ return localizePresetName(mEqPresets.get(index).getName());
+ }
+
+ private final String localizePresetName(final String name) {
+ // missing electronic, multimedia, small speakers, custom
+ final String[] names = {
+ "Normal", "Classical", "Dance", "Flat", "Folk",
+ "Heavy Metal", "Hip Hop", "Jazz", "Pop", "Rock",
+ "Electronic", "Small speakers", "Multimedia",
+ "Custom"
+ };
+ final int[] ids = {
+ R.string.normal, R.string.classical, R.string.dance, R.string.flat, R.string.folk,
+ R.string.heavy_metal, R.string.hip_hop, R.string.jazz, R.string.pop, R.string.rock,
+ R.string.ci_extreme, R.string.small_speakers, R.string.multimedia,
+ R.string.user
+ };
+
+ for (int i = names.length - 1; i >= 0; --i) {
+ if (names[i].equalsIgnoreCase(name)) {
+ return mContext.getString(ids[i]);
+ }
+ }
+ return name;
+ }
+
+ public boolean isEqualizerLocked() {
+ return getCurrentPreset() instanceof Preset.CustomPreset
+ && !(getCurrentPreset() instanceof Preset.PermCustomPreset)
+ && ((Preset.CustomPreset) getCurrentPreset()).isLocked();
+ }
+
+ public void renameCurrentPreset(String s) {
+ if (UserSession.getInstance() != null) {
+ UserSession.getInstance().presetRenamed();
+ }
+
+ if (isUserPreset()) {
+ ((Preset.CustomPreset) getCurrentPreset()).setName(s);
+ }
+
+ mConfig.getCallbacks().notifyPresetsChanged();
+
+ savePresetsDelayed();
+ }
+
+ public boolean removePreset(int index) {
+ if (UserSession.getInstance() != null) {
+ UserSession.getInstance().presetRemoved();
+ }
+
+ if (index > mEQCustomPresetPosition) {
+ mEqPresets.remove(index);
+ mConfig.getCallbacks().notifyPresetsChanged();
+
+ if (mCurrentPreset == index) {
+ if (DEBUG) {
+ Log.w(TAG, "removePreset() called on current preset, changing preset");
+ }
+ updateGlobalLevels(mCurrentPreset - 1);
+ setPreset(mCurrentPreset - 1);
+ }
+ savePresetsDelayed();
+ return true;
+ }
+ return false;
+ }
+
+ private void updateGlobalLevels(int presetIndexToCopy) {
+ final float[] presetLevels = getPresetLevels(presetIndexToCopy);
+ for (int i = 0; i < mGlobalLevels.length; i++) {
+ mGlobalLevels[i] = presetLevels[i];
+ }
+ }
+
+ // I AM SO LAZY!
+ private String getGlobalPref(String key, String defValue) {
+ return mConfig.getGlobalPrefs().getString(key, defValue);
+ }
+
+ private void setGlobalPref(String key, String value) {
+ mConfig.getGlobalPrefs().edit().putString(key, value).apply();
+ }
+
+ private String getPref(String key, String defValue) {
+ return mConfig.getPrefs().getString(key, defValue);
+ }
+
+ private void setPref(String key, String value) {
+ mConfig.getPrefs().edit().putString(key, value).apply();
+ }
+}
diff --git a/src/org/cyanogenmod/audiofx/activity/MasterConfigControl.java b/src/org/cyanogenmod/audiofx/activity/MasterConfigControl.java
new file mode 100644
index 0000000..1c5ac0c
--- /dev/null
+++ b/src/org/cyanogenmod/audiofx/activity/MasterConfigControl.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.cyanogenmod.audiofx.activity;
+
+import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
+import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
+import static android.media.AudioDeviceInfo.TYPE_DOCK;
+import static android.media.AudioDeviceInfo.TYPE_IP;
+import static android.media.AudioDeviceInfo.TYPE_LINE_ANALOG;
+import static android.media.AudioDeviceInfo.TYPE_LINE_DIGITAL;
+import static android.media.AudioDeviceInfo.TYPE_USB_ACCESSORY;
+import static android.media.AudioDeviceInfo.TYPE_USB_DEVICE;
+import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
+import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADSET;
+import static android.media.AudioDeviceInfo.convertDeviceTypeToInternalDevice;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.os.IBinder;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+
+import org.cyanogenmod.audiofx.Constants;
+import org.cyanogenmod.audiofx.service.AudioFxService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Master configuration class for AudioFX.
+ *
+ * Contains the main hub where data is stored for the current eq graph (which there should be
+ * one of, thus only once instance of this class exists).
+ *
+ * Anyone can obtain an instance of this class. If one does not exist, a new one is created.
+ * Immediately before the new instance creation happens, some defaults are pre-populated
+ * with MasterConfigControl.saveDefaults(). That method doesn't ever have to be directly called.
+ */
+public class MasterConfigControl {
+
+ private static final String TAG = MasterConfigControl.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean SERVICE_DEBUG = false;
+
+ private final Context mContext;
+
+ private AudioFxService.LocalBinder mService;
+ private ServiceConnection mServiceConnection;
+ private int mServiceRefCount = 0;
+
+ private AudioDeviceInfo mCurrentDevice;
+ private AudioDeviceInfo mUserDeviceOverride;
+
+ private final StateCallbacks mCallbacks;
+ private final EqualizerManager mEqManager;
+ private final AudioManager mAudioManager;
+
+ private static MasterConfigControl sInstance;
+ private boolean mShouldBindToService = false;
+
+ public static MasterConfigControl getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new MasterConfigControl(context);
+ }
+ return sInstance;
+ }
+
+ private MasterConfigControl(Context context) {
+ mContext = context.getApplicationContext();
+
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+
+ mCallbacks = new StateCallbacks(this);
+ mEqManager = new EqualizerManager(context, this);
+ }
+
+ public void onResetDefaults() {
+ mEqManager.applyDefaults();
+ }
+
+ public synchronized boolean bindService() {
+ boolean conn = true;
+ if (SERVICE_DEBUG) Log.i(TAG, "bindService() refCount=" + mServiceRefCount);
+ if (mServiceConnection == null && mServiceRefCount == 0) {
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ if (SERVICE_DEBUG) Log.i(TAG, "onServiceConnected refCount=" + mServiceRefCount);
+ mService = ((AudioFxService.LocalBinder) binder);
+ LocalBroadcastManager.getInstance(mContext).registerReceiver(
+ mDeviceChangeReceiver,
+ new IntentFilter(AudioFxService.ACTION_DEVICE_OUTPUT_CHANGED));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (SERVICE_DEBUG) Log.w(TAG, "onServiceDisconnected refCount =" + mServiceRefCount);
+ LocalBroadcastManager.getInstance(mContext).unregisterReceiver(
+ mDeviceChangeReceiver);
+ mService = null;
+ }
+ };
+
+ Intent serviceIntent = new Intent(mContext, AudioFxService.class);
+ conn = mContext.bindService(serviceIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+ if (conn) {
+ mServiceRefCount++;
+ }
+ return mServiceRefCount > 0;
+ }
+
+ public synchronized void unbindService() {
+ if (SERVICE_DEBUG) Log.i(TAG, "unbindService() called refCount=" + mServiceRefCount);
+ if (mServiceRefCount > 0) {
+ mServiceRefCount--;
+ if (mServiceRefCount == 0) {
+ mContext.unbindService(mServiceConnection);
+ mService = null;
+ mServiceConnection = null;
+ }
+ }
+ }
+
+ public boolean checkService() {
+ if (mService == null && mServiceRefCount == 0 && mShouldBindToService) {
+ Log.e(TAG, "Service went away, rebinding");
+ bindService();
+ }
+ return mService != null;
+ }
+
+ private final BroadcastReceiver mDeviceChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int device = intent.getIntExtra("device", -1);
+ Log.d(TAG, "deviceChanged: " + device);
+ if (device > -1) {
+ AudioDeviceInfo info = getDeviceById(device);
+ if (info != null) {
+ setCurrentDevice(info, false);
+ }
+ }
+ }
+ };
+
+ public void updateService(int flags) {
+ if (checkService()) {
+ mService.update(flags);
+ }
+ }
+
+ public StateCallbacks getCallbacks() {
+ return mCallbacks;
+ }
+
+ public EqualizerManager getEqualizerManager() {
+ return mEqManager;
+ }
+
+ public synchronized void setCurrentDeviceEnabled(boolean isChecked) {
+ getPrefs().edit().putBoolean(Constants.DEVICE_AUDIOFX_GLOBAL_ENABLE, isChecked).apply();
+ getCallbacks().notifyGlobalToggle(isChecked);
+ updateService(AudioFxService.ALL_CHANGED);
+ }
+
+ public synchronized boolean isCurrentDeviceEnabled() {
+ return getPrefs().getBoolean(Constants.DEVICE_AUDIOFX_GLOBAL_ENABLE, false);
+ }
+
+ public synchronized SharedPreferences getGlobalPrefs() {
+ return mContext.getSharedPreferences(Constants.AUDIOFX_GLOBAL_FILE, 0);
+ }
+
+ /**
+ * Update the current device used when querying any device-specific values such as the current
+ * preset, or the user's custom eq preset settings.
+ *
+ * @param audioOutputRouting the new device key
+ */
+ public synchronized void setCurrentDevice(AudioDeviceInfo device, final boolean userSwitch) {
+
+ final AudioDeviceInfo current = getCurrentDevice();
+
+ Log.d(TAG, "setCurrentDevice name=" + (current == null ? null : current.getProductName()) +
+ " fromUser=" + userSwitch +
+ " cur=" + (current == null ? null : current.getType()) +
+ " new=" + (device == null ? null : device.getType()));
+
+ if (userSwitch) {
+ mUserDeviceOverride = device;
+ } else {
+ if (device != null) {
+ mCurrentDevice = device;
+ }
+ mUserDeviceOverride = null;
+ }
+
+ mEqManager.onPreDeviceChanged();
+
+ mCallbacks.notifyDeviceChanged(device, userSwitch);
+
+ mEqManager.onPostDeviceChanged();
+ }
+
+ public AudioDeviceInfo getSystemDevice() {
+ if (mCurrentDevice == null) {
+ final int forMusic = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
+ for (AudioDeviceInfo ai : getConnectedDevices()) {
+ if ((convertDeviceTypeToInternalDevice(ai.getType()) & forMusic) > 0) {
+ return ai;
+ }
+ }
+ }
+ return mCurrentDevice;
+ }
+
+ public boolean isUserDeviceOverride() {
+ return mUserDeviceOverride != null;
+ }
+
+ public AudioDeviceInfo getCurrentDevice() {
+ if (isUserDeviceOverride()) {
+ return mUserDeviceOverride;
+ }
+ return getSystemDevice();
+ }
+
+ public AudioDeviceInfo getDeviceById(int id) {
+ for (AudioDeviceInfo ai : mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
+ if (ai.getId() == id) {
+ return ai;
+ }
+ }
+ return null;
+ }
+
+ public List<AudioDeviceInfo> getConnectedDevices(int... filter) {
+ final List<AudioDeviceInfo> devices = new ArrayList<AudioDeviceInfo>();
+ for (AudioDeviceInfo ai : mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
+ if (filter.length == 0) {
+ devices.add(ai);
+ } else {
+ for (int i = 0; i < filter.length; i++) {
+ if (ai.getType() == filter[i]) {
+ devices.add(ai);
+ continue;
+ }
+ }
+ }
+ }
+ return devices;
+ }
+
+ public String getCurrentDeviceIdentifier() {
+ return getDeviceIdentifierString(getCurrentDevice());
+ }
+
+ public SharedPreferences getPrefs() {
+ return mContext.getSharedPreferences(getCurrentDeviceIdentifier(), 0);
+ }
+
+ public boolean hasDts() {
+ return getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_DTS, false);
+ }
+
+ public boolean hasMaxxAudio() {
+ return getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_MAXXAUDIO, false);
+ }
+
+ public boolean getMaxxVolumeEnabled() {
+ return getPrefs().getBoolean(Constants.DEVICE_AUDIOFX_MAXXVOLUME_ENABLE, false);
+ }
+
+ public boolean hasBassBoost() {
+ return getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_BASSBOOST, false);
+ }
+
+ public boolean hasVirtualizer() {
+ return getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_VIRTUALIZER, false);
+ }
+
+ public void setMaxxVolumeEnabled(boolean enable) {
+ getPrefs().edit().putBoolean(Constants.DEVICE_AUDIOFX_MAXXVOLUME_ENABLE, enable).apply();
+ updateService(AudioFxService.VOLUME_BOOST_CHANGED);
+ }
+
+ void overrideEqLevels(short band, short level) {
+ if (checkService()) {
+ mService.setOverrideLevels(band, level);
+ }
+ }
+
+ public static String getDeviceDisplayString(Context context, AudioDeviceInfo info) {
+ int type = info == null ? -1 : info.getType();
+ switch (type) {
+ case TYPE_WIRED_HEADSET:
+ case TYPE_WIRED_HEADPHONES:
+ return context.getString(org.cyanogenmod.audiofx.R.string.device_headset);
+ case TYPE_LINE_ANALOG:
+ case TYPE_LINE_DIGITAL:
+ return context.getString(org.cyanogenmod.audiofx.R.string.device_line_out);
+ case TYPE_BLUETOOTH_SCO:
+ case TYPE_BLUETOOTH_A2DP:
+ case TYPE_USB_DEVICE:
+ case TYPE_USB_ACCESSORY:
+ case TYPE_DOCK:
+ case TYPE_IP:
+ return info.getProductName().toString();
+ default:
+ return context.getString(org.cyanogenmod.audiofx.R.string.device_speaker);
+ }
+ }
+
+ private static String appendProductName(AudioDeviceInfo info, String prefix) {
+ StringBuilder nm = new StringBuilder(prefix);
+ if (info != null && info.getProductName() != null) {
+ nm.append("-").append(info.getProductName().toString().replaceAll("\\W+", ""));
+ }
+ return nm.toString();
+ }
+
+ private static String appendDeviceAddress(AudioDeviceInfo info, String prefix) {
+ StringBuilder nm = new StringBuilder(prefix);
+ if (info != null && info.getAddress() != null) {
+ nm.append("-").append(info.getAddress().replace(":", ""));
+ }
+ return nm.toString();
+ }
+
+ public static String getDeviceIdentifierString(AudioDeviceInfo info) {
+ int type = info == null ? -1 : info.getType();
+ switch (type) {
+ case TYPE_WIRED_HEADSET:
+ case TYPE_WIRED_HEADPHONES:
+ return Constants.DEVICE_HEADSET;
+ case TYPE_LINE_ANALOG:
+ case TYPE_LINE_DIGITAL:
+ return Constants.DEVICE_LINE_OUT;
+ case TYPE_BLUETOOTH_SCO:
+ case TYPE_BLUETOOTH_A2DP:
+ return appendDeviceAddress(info, Constants.DEVICE_PREFIX_BLUETOOTH);
+ case TYPE_USB_DEVICE:
+ case TYPE_USB_ACCESSORY:
+ case TYPE_DOCK:
+ return appendProductName(info, Constants.DEVICE_PREFIX_USB);
+ case TYPE_IP:
+ return appendProductName(info, Constants.DEVICE_PREFIX_CAST);
+ default:
+ return Constants.DEVICE_SPEAKER;
+ }
+ }
+
+ /**
+ * Set whether to automatically attempt to bind to the service.
+ * @param bindToService
+ */
+ public void setAutoBindToService(boolean bindToService) {
+ mShouldBindToService = bindToService;
+ }
+}
diff --git a/src/org/cyanogenmod/audiofx/activity/StateCallbacks.java b/src/org/cyanogenmod/audiofx/activity/StateCallbacks.java
new file mode 100644
index 0000000..0196b15
--- /dev/null
+++ b/src/org/cyanogenmod/audiofx/activity/StateCallbacks.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.cyanogenmod.audiofx.activity;
+
+import android.media.AudioDeviceInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class StateCallbacks {
+
+ private static final String TAG = "StateCallbacks";
+
+ private final MasterConfigControl mConfig;
+
+ private final List<EqUpdatedCallback> mEqUpdateCallbacks = new ArrayList<EqUpdatedCallback>();
+
+ private final List<DeviceChangedCallback> mDeviceChangedCallbacks = new ArrayList<DeviceChangedCallback>();
+
+ private final List<EqControlStateCallback> mEqControlStateCallbacks = new ArrayList<EqControlStateCallback>();
+
+ StateCallbacks(MasterConfigControl config) {
+ mConfig = config;
+ }
+
+ /**
+ * Implement this callback to receive any changes called to the
+ * MasterConfigControl instance
+ */
+ public interface EqUpdatedCallback {
+ /**
+ * A band level has been changed
+ *
+ * @param band the band index which changed
+ * @param dB the new decibel value
+ * @param fromSystem whether the event was from the system or from the
+ * user
+ */
+ public void onBandLevelChange(int band, float dB, boolean fromSystem);
+
+ /**
+ * The preset has been set
+ *
+ * @param newPresetIndex the new preset index.
+ */
+ public void onPresetChanged(int newPresetIndex);
+
+ public void onPresetsChanged();
+ }
+
+ public void addEqUpdatedCallback(EqUpdatedCallback callback) {
+ synchronized (mEqUpdateCallbacks) {
+ mEqUpdateCallbacks.add(callback);
+ }
+ }
+
+ public void removeEqUpdatedCallback(EqUpdatedCallback callback) {
+ synchronized (mEqUpdateCallbacks) {
+ mEqUpdateCallbacks.remove(callback);
+ }
+ }
+
+ void notifyPresetsChanged() {
+ synchronized (mEqUpdateCallbacks) {
+ for (final EqUpdatedCallback callback : mEqUpdateCallbacks) {
+ callback.onPresetsChanged();
+ }
+ }
+ }
+
+ void notifyPresetChanged(final int index) {
+ synchronized (mEqUpdateCallbacks) {
+ for (final EqUpdatedCallback callback : mEqUpdateCallbacks) {
+ callback.onPresetChanged(index);
+ }
+ }
+ }
+
+ void notifyBandLevelChangeChanged(final int band, final float dB, final boolean fromSystem) {
+ synchronized (mEqUpdateCallbacks) {
+ for (final EqUpdatedCallback callback : mEqUpdateCallbacks) {
+ callback.onBandLevelChange(band, dB, fromSystem);
+ }
+ }
+ }
+
+ /**
+ * Callback for changes to visibility and state of the EQ
+ */
+ public interface EqControlStateCallback {
+ public void updateEqState(boolean saveVisible, boolean removeVisible,
+ boolean renameVisible, boolean unlockVisible);
+ }
+
+ public void addEqControlStateCallback(EqControlStateCallback callback) {
+ synchronized (mEqControlStateCallbacks) {
+ mEqControlStateCallbacks.add(callback);
+ }
+ }
+
+ public synchronized void removeEqControlStateCallback(EqControlStateCallback callback) {
+ synchronized (mEqControlStateCallbacks) {
+ mEqControlStateCallbacks.remove(callback);
+ }
+ }
+
+ void notifyEqControlStateChanged(boolean saveVisible, boolean removeVisible,
+ boolean renameVisible, boolean unlockVisible) {
+ synchronized (mEqControlStateCallbacks) {
+ for (final EqControlStateCallback callback : mEqControlStateCallbacks) {
+ callback.updateEqState(saveVisible, removeVisible, renameVisible, unlockVisible);
+ }
+ }
+ }
+
+ /**
+ * Register this callback to receive notification when the output device
+ * changes.
+ */
+ public interface DeviceChangedCallback {
+ public void onDeviceChanged(AudioDeviceInfo device, boolean userChange);
+ public void onGlobalDeviceToggle(boolean on);
+
+ }
+
+ public void addDeviceChangedCallback(DeviceChangedCallback callback) {
+ synchronized (mDeviceChangedCallbacks) {
+ mDeviceChangedCallbacks.add(callback);
+ callback.onDeviceChanged(mConfig.getCurrentDevice(), false);
+ }
+ }
+
+ public synchronized void removeDeviceChangedCallback(DeviceChangedCallback callback) {
+ synchronized (mDeviceChangedCallbacks) {
+ mDeviceChangedCallbacks.remove(callback);
+ }
+ }
+
+ void notifyGlobalToggle(boolean on) {
+ synchronized (mDeviceChangedCallbacks) {
+ for (DeviceChangedCallback callback : mDeviceChangedCallbacks) {
+ callback.onGlobalDeviceToggle(on);
+ }
+
+ }
+ }
+
+ void notifyDeviceChanged(final AudioDeviceInfo newDevice, final boolean fromUser) {
+ synchronized (mDeviceChangedCallbacks) {
+ for (final DeviceChangedCallback callback : mDeviceChangedCallbacks) {
+ callback.onDeviceChanged(newDevice, fromUser);
+ }
+ }
+ }
+}