diff options
Diffstat (limited to 'src/org/cyanogenmod/audiofx/activity/MasterConfigControl.java')
-rw-r--r-- | src/org/cyanogenmod/audiofx/activity/MasterConfigControl.java | 382 |
1 files changed, 382 insertions, 0 deletions
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; + } +} |