dolby: Rewrite in Kotlin

Some cleanup and restructuring while we're at it.

Change-Id: I2f1fc53c202d91421c7b6af68c814c25398a62e4
This commit is contained in:
Adithya R 2024-07-05 22:12:56 +05:30 committed by basamaryan
parent 6a00b38d83
commit d5620a07ee
No known key found for this signature in database
GPG Key ID: 707BA6C82329E8F9
17 changed files with 850 additions and 903 deletions

View File

@ -8,7 +8,7 @@
android_app {
name: "XiaomiDolby",
srcs: ["src/**/*.java"],
srcs: ["src/**/*.kt"],
resource_dirs: ["res"],
certificate: "platform",
platform_apis: true,

View File

@ -1,29 +0,0 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class BootCompletedReceiver extends BroadcastReceiver {
private static final String TAG = "XiaomiParts-BCR";
@Override
public void onReceive(final Context context, Intent intent) {
Log.d(TAG, "Received intent: " + intent.getAction());
if (!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
return;
}
Log.i(TAG, "Boot completed, starting dolby");
DolbyUtils.getInstance(context).onBootCompleted();
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
private const val TAG = "XiaomiDolby-Boot"
class BootCompletedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "Received intent: ${intent.action}")
if (intent.action != Intent.ACTION_BOOT_COMPLETED) {
return
}
Log.i(TAG, "Boot completed, starting dolby")
DolbyController.getInstance(context).onBootCompleted()
}
}

View File

@ -1,25 +0,0 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi;
import android.os.Bundle;
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
import com.android.settingslib.widget.R;
public class DolbyActivity extends CollapsingToolbarBaseActivity {
private static final String TAG_DOLBY = "DolbyActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(R.id.content_frame,
new DolbySettingsFragment(), TAG_DOLBY).commit();
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi
import android.os.Bundle
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity
import com.android.settingslib.widget.R
private const val TAG = "DolbyActivity"
class DolbyActivity : CollapsingToolbarBaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fragmentManager.beginTransaction()
.replace(R.id.content_frame, DolbySettingsFragment(), TAG)
.commit()
}
}

View File

@ -1,156 +0,0 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi;
import android.media.audiofx.AudioEffect;
import android.util.Log;
import co.aospa.dolby.xiaomi.DolbyConstants.DsParam;
import java.util.UUID;
class DolbyAtmos extends AudioEffect {
private static final String TAG = "DolbyAtmos";
private static final UUID EFFECT_TYPE_DAP =
UUID.fromString("9d4921da-8225-4f29-aefa-39537a04bcaa");
private static final int
EFFECT_PARAM_CPDP_VALUES = 5,
EFFECT_PARAM_ENABLE = 0,
EFFECT_PARAM_PROFILE = 0xA000000,
EFFECT_PARAM_SET_PROFILE_PARAMETER = 0x1000000,
EFFECT_PARAM_GET_PROFILE_PARAMETER = 0x1000005,
EFFECT_PARAM_RESET_PROFILE_SETTINGS = 0xC000000;
DolbyAtmos(int priority, int audioSession) {
super(EFFECT_TYPE_NULL, EFFECT_TYPE_DAP, priority, audioSession);
}
private static int int32ToByteArray(int value, byte[] dst, int index) {
dst[index++] = (byte) (value & 0xff);
dst[index++] = (byte) ((value >>> 8) & 0xff);
dst[index++] = (byte) ((value >>> 16) & 0xff);
dst[index] = (byte) ((value >>> 24) & 0xff);
return 4;
}
private static int byteArrayToInt32(byte[] ba) {
return ((ba[3] & 0xff) << 24) | ((ba[2] & 0xff) << 16)
| ((ba[1] & 0xff) << 8) | (ba[0] & 0xff);
}
private static int int32ArrayToByteArray(int[] src, byte[] dst, int index) {
for (int x : src) {
dst[index++] = (byte) ((x >>> 0) & 0xff);
dst[index++] = (byte) ((x >>> 8) & 0xff);
dst[index++] = (byte) ((x >>> 16) & 0xff);
dst[index++] = (byte) ((x >>> 24) & 0xff);
}
return src.length << 2;
}
private static int[] byteArrayToInt32Array(byte[] ba, int dstLength) {
int srcLength = ba.length >> 2;
if (dstLength > srcLength) {
dstLength = srcLength;
}
int[] dst = new int[dstLength];
for (int i = 0; i < dstLength; i++) {
dst[i] = ((ba[i * 4 + 3] & 0xff) << 24) | ((ba[i * 4 + 2] & 0xff) << 16)
| ((ba[i * 4 + 1] & 0xff) << 8) | (ba[i * 4] & 0xff);
}
return dst;
}
private void setIntParam(int param, int value) {
byte[] buf = new byte[12];
int i = int32ToByteArray(param, buf, 0);
int32ToByteArray(value, buf, i + int32ToByteArray(1, buf, i));
checkStatus(setParameter(EFFECT_PARAM_CPDP_VALUES, buf));
}
private int getIntParam(int param) {
byte[] buf = new byte[12];
int32ToByteArray(param, buf, 0);
checkStatus(getParameter(EFFECT_PARAM_CPDP_VALUES + param, buf));
return byteArrayToInt32(buf);
}
void setDsOn(boolean on) {
setIntParam(EFFECT_PARAM_ENABLE, on ? 1 : 0);
super.setEnabled(on);
}
boolean getDsOn() {
return getIntParam(EFFECT_PARAM_ENABLE) == 1;
}
void setProfile(int index) {
setIntParam(EFFECT_PARAM_PROFILE, index);
}
int getProfile() {
return getIntParam(EFFECT_PARAM_PROFILE);
}
void resetProfileSpecificSettings() {
int profile = getProfile();
dlog("resetProfileSpecificSettings: profile=" + profile);
setIntParam(EFFECT_PARAM_RESET_PROFILE_SETTINGS, profile);
}
void setDapParameter(int profile, DsParam param, int values[]) {
dlog("setDapParameter: profile=" + profile + " param=" + param);
int length = values.length;
byte[] buf = new byte[(length + 4) * 4];
int i = int32ToByteArray(EFFECT_PARAM_SET_PROFILE_PARAMETER, buf, 0);
int i2 = i + int32ToByteArray(length + 1, buf, i);
int i3 = i2 + int32ToByteArray(profile, buf, i2);
int32ArrayToByteArray(values, buf, i3 + int32ToByteArray(param.id, buf, i3));
checkStatus(setParameter(EFFECT_PARAM_CPDP_VALUES, buf));
}
void setDapParameter(DsParam param, int values[]) {
setDapParameter(getProfile(), param, values);
}
void setDapParameterBool(DsParam param, boolean enable) {
setDapParameter(param, new int[]{enable ? 1 : 0});
}
void setDapParameterInt(DsParam param, int value) {
setDapParameter(param, new int[]{value});
}
int[] getDapParameter(int profile, DsParam param) {
dlog("getDapParameter: profile=" + profile + " param=" + param);
int length = param.length;
byte[] buf = new byte[(length + 2) * 4];
int i = (param.id << 16) + EFFECT_PARAM_GET_PROFILE_PARAMETER;
checkStatus(getParameter(i + (profile << 8), buf));
return byteArrayToInt32Array(buf, length);
}
int[] getDapParameter(DsParam param) {
return getDapParameter(getProfile(), param);
}
boolean getDapParameterBool(DsParam param) {
return getDapParameter(param)[0] == 1;
}
int getDapParameterInt(DsParam param) {
return getDapParameter(param)[0];
}
private static void dlog(String msg) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, msg);
}
}
}

View File

@ -0,0 +1,136 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi
import android.media.audiofx.AudioEffect
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.dlog
import co.aospa.dolby.xiaomi.DolbyConstants.DsParam
import java.util.UUID
class DolbyAudioEffect(priority: Int, audioSession: Int) : AudioEffect(
EFFECT_TYPE_NULL, EFFECT_TYPE_DAP, priority, audioSession
) {
var dsOn: Boolean
get() = getIntParam(EFFECT_PARAM_ENABLE) == 1
set(value) {
setIntParam(EFFECT_PARAM_ENABLE, if (value) 1 else 0)
enabled = value
}
var profile: Int
get() = getIntParam(EFFECT_PARAM_PROFILE)
set(value) {
setIntParam(EFFECT_PARAM_PROFILE, value)
}
private fun setIntParam(param: Int, value: Int) {
dlog(TAG, "setIntParam($param, $value)")
val buf = ByteArray(12)
int32ToByteArray(param, buf, 0)
int32ToByteArray(1, buf, 4)
int32ToByteArray(value, buf, 8)
checkStatus(setParameter(EFFECT_PARAM_CPDP_VALUES, buf))
}
private fun getIntParam(param: Int): Int {
val buf = ByteArray(12)
int32ToByteArray(param, buf, 0)
checkStatus(getParameter(EFFECT_PARAM_CPDP_VALUES + param, buf))
return byteArrayToInt32(buf).also {
dlog(TAG, "getIntParam($param): $it")
}
}
fun resetProfileSpecificSettings() {
dlog(TAG, "resetProfileSpecificSettings: profile=$profile")
setIntParam(EFFECT_PARAM_RESET_PROFILE_SETTINGS, profile)
}
fun setDapParameter(param: DsParam, values: IntArray, profile: Int = this.profile) {
dlog(TAG, "setDapParameter: profile=$profile param=$param")
val length = values.size
val buf = ByteArray((length + 4) * 4)
int32ToByteArray(EFFECT_PARAM_SET_PROFILE_PARAMETER, buf, 0)
int32ToByteArray(length + 1, buf, 4)
int32ToByteArray(profile, buf, 8)
int32ToByteArray(param.id, buf, 12)
int32ArrayToByteArray(values, buf, 16)
checkStatus(setParameter(EFFECT_PARAM_CPDP_VALUES, buf))
}
fun setDapParameter(param: DsParam, enable: Boolean, profile: Int = this.profile) =
setDapParameter(param, intArrayOf(if (enable) 1 else 0), profile)
fun setDapParameter(param: DsParam, value: Int, profile: Int = this.profile) =
setDapParameter(param, intArrayOf(value), profile)
fun getDapParameter(param: DsParam, profile: Int = this.profile): IntArray {
dlog(TAG, "getDapParameter: profile=$profile param=$param")
val length = param.length
val buf = ByteArray((length + 2) * 4)
val p = (param.id shl 16) + (profile shl 8) + EFFECT_PARAM_GET_PROFILE_PARAMETER
checkStatus(getParameter(p, buf))
return byteArrayToInt32Array(buf, length)
}
fun getDapParameterBool(param: DsParam, profile: Int = this.profile): Boolean =
getDapParameter(param, profile)[0] == 1
fun getDapParameterInt(param: DsParam, profile: Int = this.profile): Int =
getDapParameter(param, profile)[0]
companion object {
private const val TAG = "DolbyAudioEffect"
private val EFFECT_TYPE_DAP =
UUID.fromString("9d4921da-8225-4f29-aefa-39537a04bcaa")
private const val EFFECT_PARAM_ENABLE = 0
private const val EFFECT_PARAM_CPDP_VALUES = 5
private const val EFFECT_PARAM_PROFILE = 0xA000000
private const val EFFECT_PARAM_SET_PROFILE_PARAMETER = 0x1000000
private const val EFFECT_PARAM_GET_PROFILE_PARAMETER = 0x1000005
private const val EFFECT_PARAM_RESET_PROFILE_SETTINGS = 0xC000000
private fun int32ToByteArray(value: Int, dst: ByteArray, index: Int) {
var idx = index
dst[idx++] = (value and 0xff).toByte()
dst[idx++] = ((value ushr 8) and 0xff).toByte()
dst[idx++] = ((value ushr 16) and 0xff).toByte()
dst[idx] = ((value ushr 24) and 0xff).toByte()
}
private fun byteArrayToInt32(ba: ByteArray): Int {
return ((ba[3].toInt() and 0xff) shl 24) or
((ba[2].toInt() and 0xff) shl 16) or
((ba[1].toInt() and 0xff) shl 8) or
(ba[0].toInt() and 0xff)
}
private fun int32ArrayToByteArray(src: IntArray, dst: ByteArray, index: Int) {
var idx = index
for (x in src) {
dst[idx++] = (x and 0xff).toByte()
dst[idx++] = ((x ushr 8) and 0xff).toByte()
dst[idx++] = ((x ushr 16) and 0xff).toByte()
dst[idx++] = ((x ushr 24) and 0xff).toByte()
}
}
private fun byteArrayToInt32Array(ba: ByteArray, dstLength: Int): IntArray {
val srcLength = ba.size shr 2
val dst = IntArray(dstLength.coerceAtMost(srcLength))
for (i in dst.indices) {
dst[i] = ((ba[i * 4 + 3].toInt() and 0xff) shl 24) or
((ba[i * 4 + 2].toInt() and 0xff) shl 16) or
((ba[i * 4 + 1].toInt() and 0xff) shl 8) or
(ba[i * 4].toInt() and 0xff)
}
return dst
}
}
}

View File

@ -1,38 +0,0 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi;
class DolbyConstants {
enum DsParam {
HEADPHONE_VIRTUALIZER(101),
SPEAKER_VIRTUALIZER(102),
VOLUME_LEVELER_ENABLE(103),
DIALOGUE_ENHANCER_ENABLE(105),
DIALOGUE_ENHANCER_AMOUNT(108),
GEQ_BAND_GAINS(110, 20),
BASS_ENHANCER_ENABLE(111),
STEREO_WIDENING_AMOUNT(113),
VOLUME_LEVELER_AMOUNT(116);
public int id, length;
DsParam(int id, int length) {
this.id = id;
this.length = length;
}
DsParam(int id) {
this(id, 1);
}
public String toString() {
return String.format("%s(%s)", name(), id);
}
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi
import android.util.Log
class DolbyConstants {
enum class DsParam(val id: Int, val length: Int = 1) {
HEADPHONE_VIRTUALIZER(101),
SPEAKER_VIRTUALIZER(102),
VOLUME_LEVELER_ENABLE(103),
DIALOGUE_ENHANCER_ENABLE(105),
DIALOGUE_ENHANCER_AMOUNT(108),
GEQ_BAND_GAINS(110, 20),
BASS_ENHANCER_ENABLE(111),
STEREO_WIDENING_AMOUNT(113),
VOLUME_LEVELER_AMOUNT(116);
override fun toString(): String {
return "${name}(${id})"
}
}
companion object {
const val TAG = "XiaomiDolby"
const val PREF_ENABLE = "dolby_enable"
const val PREF_PROFILE = "dolby_profile"
const val PREF_PRESET = "dolby_preset"
const val PREF_VIRTUALIZER = "dolby_virtualizer"
const val PREF_STEREO = "dolby_stereo"
const val PREF_DIALOGUE = "dolby_dialogue"
const val PREF_BASS = "dolby_bass"
const val PREF_VOLUME = "dolby_volume"
const val PREF_RESET = "dolby_reset"
fun dlog(tag: String, msg: String) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(tag, msg)
}
}
}
}

View File

@ -0,0 +1,247 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi
import android.content.Context
import android.media.AudioDeviceCallback
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.media.AudioManager.AudioPlaybackCallback
import android.media.AudioPlaybackConfiguration
import android.os.Handler
import android.util.Log
import androidx.preference.PreferenceManager
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.dlog
import co.aospa.dolby.xiaomi.DolbyConstants.DsParam
import co.aospa.dolby.xiaomi.R
internal class DolbyController private constructor(
private val context: Context
) {
private var dolbyEffect = DolbyAudioEffect(EFFECT_PRIORITY, audioSession = 0)
private val audioManager = context.getSystemService(AudioManager::class.java)
private val handler = Handler(context.mainLooper)
// Restore current profile on every media session
private val playbackCallback = object : AudioPlaybackCallback() {
override fun onPlaybackConfigChanged(configs: List<AudioPlaybackConfiguration>) {
val isPlaying = configs.any {
it.playerState == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
}
dlog(TAG, "onPlaybackConfigChanged: isPlaying=$isPlaying")
if (isPlaying)
setCurrentProfile()
}
}
// Restore current profile on audio device change
private val audioDeviceCallback = object : AudioDeviceCallback() {
override fun onAudioDevicesAdded(addedDevices: Array<AudioDeviceInfo>) {
dlog(TAG, "onAudioDevicesAdded")
setCurrentProfile()
}
override fun onAudioDevicesRemoved(removedDevices: Array<AudioDeviceInfo>) {
dlog(TAG, "onAudioDevicesRemoved")
setCurrentProfile()
}
}
private var registerCallbacks = false
set(value) {
if (field == value) return
field = value
dlog(TAG, "setRegisterCallbacks($value)")
if (value) {
audioManager.registerAudioPlaybackCallback(playbackCallback, handler)
audioManager.registerAudioDeviceCallback(audioDeviceCallback, handler)
} else {
audioManager.unregisterAudioPlaybackCallback(playbackCallback)
audioManager.unregisterAudioDeviceCallback(audioDeviceCallback)
}
}
var dsOn: Boolean
get() =
dolbyEffect.dsOn.also {
dlog(TAG, "getDsOn: $it")
}
set(value) {
dlog(TAG, "setDsOn: $value")
checkEffect()
dolbyEffect.dsOn = value
registerCallbacks = value
if (value)
setCurrentProfile()
}
var profile: Int
get() =
dolbyEffect.profile.also {
dlog(TAG, "getProfile: $it")
}
set(value) {
dlog(TAG, "setProfile: $value")
checkEffect()
dolbyEffect.profile = value
}
var preset: String
get() {
val gains = dolbyEffect.getDapParameter(DsParam.GEQ_BAND_GAINS)
return gains.joinToString(separator = ",").also {
dlog(TAG, "getPreset: $it")
}
}
set(value) {
dlog(TAG, "setPreset: $value")
checkEffect()
val gains = value.split(",")
.map { it.toInt() }
.toIntArray()
dolbyEffect.setDapParameter(DsParam.GEQ_BAND_GAINS, gains)
}
var headphoneVirtEnabled: Boolean
get() =
dolbyEffect.getDapParameterBool(DsParam.HEADPHONE_VIRTUALIZER).also {
dlog(TAG, "getHeadphoneVirtEnabled: $it")
}
set(value) {
dlog(TAG, "setHeadphoneVirtEnabled: $value")
checkEffect()
dolbyEffect.setDapParameter(DsParam.HEADPHONE_VIRTUALIZER, value)
}
var speakerVirtEnabled: Boolean
get() =
dolbyEffect.getDapParameterBool(DsParam.SPEAKER_VIRTUALIZER).also {
dlog(TAG, "getSpeakerVirtEnabled: $it")
}
set(value) {
dlog(TAG, "setSpeakerVirtEnabled: $value")
checkEffect()
dolbyEffect.setDapParameter(DsParam.SPEAKER_VIRTUALIZER, value)
}
var bassEnhancerEnabled: Boolean
get() =
dolbyEffect.getDapParameterBool(DsParam.BASS_ENHANCER_ENABLE).also {
dlog(TAG, "getBassEnhancerEnabled: $it")
}
set(value) {
dlog(TAG, "setBassEnhancerEnabled: $value")
checkEffect()
dolbyEffect.setDapParameter(DsParam.BASS_ENHANCER_ENABLE, value)
}
var volumeLevelerEnabled: Boolean
get() {
val enabled = dolbyEffect.getDapParameterBool(DsParam.VOLUME_LEVELER_ENABLE)
val amount = dolbyEffect.getDapParameterInt(DsParam.VOLUME_LEVELER_AMOUNT)
dlog(TAG, "getVolumeLevelerEnabled: enabled=$enabled amount=$amount")
return enabled && amount > 0
}
set(value) {
dlog(TAG, "setVolumeLevelerEnabled: $value")
checkEffect()
dolbyEffect.setDapParameter(DsParam.VOLUME_LEVELER_ENABLE, value)
dolbyEffect.setDapParameter(
DsParam.VOLUME_LEVELER_AMOUNT,
if (value) VOLUME_LEVELER_AMOUNT else 0
)
}
var stereoWideningAmount: Int
get() =
dolbyEffect.getDapParameterInt(DsParam.STEREO_WIDENING_AMOUNT).also {
dlog(TAG, "getStereoWideningAmount: $it")
}
set(value) {
dlog(TAG, "setStereoWideningAmount: $value")
checkEffect()
dolbyEffect.setDapParameter(DsParam.STEREO_WIDENING_AMOUNT, value)
}
var dialogueEnhancerAmount: Int
get() {
val enabled = dolbyEffect.getDapParameterBool(DsParam.DIALOGUE_ENHANCER_ENABLE)
val amount = if (enabled) {
dolbyEffect.getDapParameterInt(DsParam.DIALOGUE_ENHANCER_AMOUNT)
} else 0
dlog(TAG, "getDialogueEnhancerAmount: enabled=$enabled amount=$amount")
return amount
}
set(value) {
dlog(TAG, "setDialogueEnhancerAmount: $value")
checkEffect()
dolbyEffect.setDapParameter(DsParam.DIALOGUE_ENHANCER_ENABLE, (value > 0))
dolbyEffect.setDapParameter(DsParam.DIALOGUE_ENHANCER_AMOUNT, value)
}
init {
dlog(TAG, "initialized")
}
fun onBootCompleted() {
dlog(TAG, "onBootCompleted")
// Restore current profile now and on certain audio changes.
val on = dsOn
dolbyEffect.enabled = on
registerCallbacks = on
if (on)
setCurrentProfile()
}
private fun checkEffect() {
if (!dolbyEffect.hasControl()) {
Log.w(TAG, "lost control, recreating effect")
dolbyEffect.release()
dolbyEffect = DolbyAudioEffect(EFFECT_PRIORITY, audioSession = 0)
}
}
private fun setCurrentProfile() {
if (!dsOn) {
dlog(TAG, "setCurrentProfile: skip, dolby is off")
return
}
dlog(TAG, "setCurrentProfile")
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
profile = prefs.getString(DolbyConstants.PREF_PROFILE, "0" /*dynamic*/).toInt()
}
fun getProfileName(): String? {
val profile = dolbyEffect.profile.toString()
val profiles = context.resources.getStringArray(R.array.dolby_profile_values)
val profileIndex = profiles.indexOf(profile)
dlog(TAG, "getProfileName: profile=$profile index=$profileIndex")
return if (profileIndex == -1) null else context.resources.getStringArray(
R.array.dolby_profile_entries
)[profileIndex]
}
fun resetProfileSpecificSettings() {
checkEffect()
dolbyEffect.resetProfileSpecificSettings()
}
companion object {
private const val TAG = "DolbyController"
private const val EFFECT_PRIORITY = 100
private const val VOLUME_LEVELER_AMOUNT = 2
@Volatile
private var instance: DolbyController? = null
fun getInstance(context: Context) =
instance ?: synchronized(this) {
instance ?: DolbyController(context).also { instance = it }
}
}
}

View File

@ -1,260 +0,0 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.Switch;
import android.widget.Toast;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceFragment;
import androidx.preference.SwitchPreference;
import com.android.settingslib.widget.MainSwitchPreference;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
import co.aospa.dolby.xiaomi.R;
import java.util.Arrays;
import java.util.List;
public class DolbySettingsFragment extends PreferenceFragment implements
OnPreferenceChangeListener, OnMainSwitchChangeListener {
private static final String TAG = "DolbySettingsFragment";
private static final AudioAttributes ATTRIBUTES_MEDIA = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.build();
public static final String PREF_ENABLE = "dolby_enable";
public static final String PREF_PROFILE = "dolby_profile";
public static final String PREF_PRESET = "dolby_preset";
public static final String PREF_VIRTUALIZER = "dolby_virtualizer";
public static final String PREF_STEREO = "dolby_stereo";
public static final String PREF_DIALOGUE = "dolby_dialogue";
public static final String PREF_BASS = "dolby_bass";
public static final String PREF_VOLUME = "dolby_volume";
public static final String PREF_RESET = "dolby_reset";
private MainSwitchPreference mSwitchBar;
private ListPreference mProfilePref, mPresetPref, mStereoPref, mDialoguePref;
private SwitchPreference mBassPref, mVirtualizerPref, mVolumePref;
private Preference mResetPref;
private CharSequence[] mPresets, mDeValues, mSwValues;
private DolbyUtils mDolbyUtils;
private AudioManager mAudioManager;
private boolean mDsOn, mIsOnSpeaker;
private int mCurrentProfile = -1;
private final Handler mHandler = new Handler();
private final AudioDeviceCallback mAudioDeviceCallback = new AudioDeviceCallback() {
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
updateSpeakerState(false);
}
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
updateSpeakerState(false);
}
};
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.dolby_settings);
mAudioManager = getActivity().getSystemService(AudioManager.class);
mDolbyUtils = DolbyUtils.getInstance(getActivity());
mDsOn = mDolbyUtils.getDsOn();
mSwitchBar = (MainSwitchPreference) findPreference(PREF_ENABLE);
mSwitchBar.addOnSwitchChangeListener(this);
mSwitchBar.setChecked(mDsOn);
mProfilePref = (ListPreference) findPreference(PREF_PROFILE);
mProfilePref.setOnPreferenceChangeListener(this);
mProfilePref.setEnabled(mDsOn);
final CharSequence[] profiles = mProfilePref.getEntryValues();
final int profile = mDolbyUtils.getProfile();
if (Arrays.asList(profiles).contains(Integer.toString(profile))) {
mCurrentProfile = profile;
mProfilePref.setSummary("%s");
mProfilePref.setValue(Integer.toString(profile));
} else {
mCurrentProfile = -1;
mProfilePref.setSummary(getActivity().getString(R.string.dolby_unknown));
}
mPresetPref = (ListPreference) findPreference(PREF_PRESET);
mPresetPref.setOnPreferenceChangeListener(this);
mPresets = mPresetPref.getEntryValues();
mVirtualizerPref = (SwitchPreference) findPreference(PREF_VIRTUALIZER);
mVirtualizerPref.setOnPreferenceChangeListener(this);
mStereoPref = (ListPreference) findPreference(PREF_STEREO);
mStereoPref.setOnPreferenceChangeListener(this);
mSwValues = mStereoPref.getEntryValues();
mDialoguePref = (ListPreference) findPreference(PREF_DIALOGUE);
mDialoguePref.setOnPreferenceChangeListener(this);
mDeValues = mDialoguePref.getEntryValues();
mBassPref = (SwitchPreference) findPreference(PREF_BASS);
mBassPref.setOnPreferenceChangeListener(this);
mVolumePref = (SwitchPreference) findPreference(PREF_VOLUME);
mVolumePref.setOnPreferenceChangeListener(this);
mResetPref = (Preference) findPreference(PREF_RESET);
mResetPref.setOnPreferenceClickListener(p -> {
mDolbyUtils.resetProfileSpecificSettings();
updateProfileSpecificPrefs();
Toast.makeText(getActivity(),
getActivity().getString(R.string.dolby_reset_profile_toast,
mProfilePref.getSummary()), Toast.LENGTH_SHORT).show();
return true;
});
mAudioManager.registerAudioDeviceCallback(mAudioDeviceCallback, mHandler);
updateSpeakerState(true);
}
@Override
public void onDestroyView() {
mAudioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
super.onDestroyView();
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
switch (preference.getKey()) {
case PREF_PROFILE:
mCurrentProfile = Integer.parseInt((newValue.toString()));
mDolbyUtils.setProfile(mCurrentProfile);
updateProfileSpecificPrefs();
return true;
case PREF_PRESET:
mDolbyUtils.setPreset(newValue.toString());
return true;
case PREF_VIRTUALIZER:
if (mIsOnSpeaker)
mDolbyUtils.setSpeakerVirtualizerEnabled((Boolean) newValue);
else
mDolbyUtils.setHeadphoneVirtualizerEnabled((Boolean) newValue);
return true;
case PREF_STEREO:
mDolbyUtils.setStereoWideningAmount(Integer.parseInt((newValue.toString())));
return true;
case PREF_DIALOGUE:
mDolbyUtils.setDialogueEnhancerAmount(Integer.parseInt((newValue.toString())));
return true;
case PREF_BASS:
mDolbyUtils.setBassEnhancerEnabled((Boolean) newValue);
return true;
case PREF_VOLUME:
mDolbyUtils.setVolumeLevelerEnabled((Boolean) newValue);
return true;
default:
return false;
}
}
@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
mDsOn = isChecked;
mDolbyUtils.setDsOn(isChecked);
mProfilePref.setEnabled(isChecked);
mResetPref.setEnabled(isChecked);
updateProfileSpecificPrefs();
}
private void updateSpeakerState(boolean force) {
final AudioDeviceAttributes device =
mAudioManager.getDevicesForAttributes(ATTRIBUTES_MEDIA).get(0);
final boolean isOnSpeaker = (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
if (mIsOnSpeaker != isOnSpeaker || force) {
dlog("updateSpeakerState: " + mIsOnSpeaker);
mIsOnSpeaker = isOnSpeaker;
updateProfileSpecificPrefs();
}
}
private void updateProfileSpecificPrefs() {
final String unknownRes = getActivity().getString(R.string.dolby_unknown);
final String headphoneRes = getActivity().getString(R.string.dolby_connect_headphones);
dlog("updateProfileSpecificPrefs: mDsOn=" + mDsOn
+ " mCurrentProfile=" + mCurrentProfile + " mIsOnSpeaker=" + mIsOnSpeaker);
final boolean enable = mDsOn && (mCurrentProfile != -1);
mPresetPref.setEnabled(enable);
mVirtualizerPref.setEnabled(enable);
mDialoguePref.setEnabled(enable);
mVolumePref.setEnabled(enable);
mResetPref.setEnabled(enable);
mStereoPref.setEnabled(enable && !mIsOnSpeaker);
mBassPref.setEnabled(enable && !mIsOnSpeaker);
if (!enable) return;
final String preset = mDolbyUtils.getPreset();
if (Arrays.asList(mPresets).contains(preset)) {
mPresetPref.setSummary("%s");
mPresetPref.setValue(preset);
} else {
mPresetPref.setSummary(unknownRes);
}
final String deValue = Integer.toString(mDolbyUtils.getDialogueEnhancerAmount());
if (Arrays.asList(mDeValues).contains(deValue)) {
mDialoguePref.setSummary("%s");
mDialoguePref.setValue(deValue);
} else {
mDialoguePref.setSummary(unknownRes);
}
mVirtualizerPref.setChecked(mIsOnSpeaker ? mDolbyUtils.getSpeakerVirtualizerEnabled()
: mDolbyUtils.getHeadphoneVirtualizerEnabled());
mVolumePref.setChecked(mDolbyUtils.getVolumeLevelerEnabled());
if (mIsOnSpeaker) {
mStereoPref.setSummary(headphoneRes);
mBassPref.setSummary(headphoneRes);
return;
}
final String swValue = Integer.toString(mDolbyUtils.getStereoWideningAmount());
if (Arrays.asList(mSwValues).contains(swValue)) {
mStereoPref.setSummary("%s");
mStereoPref.setValue(swValue);
} else {
mStereoPref.setSummary(unknownRes);
}
mBassPref.setChecked(mDolbyUtils.getBassEnhancerEnabled());
mBassPref.setSummary(null);
}
private static void dlog(String msg) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, msg);
}
}
}

View File

@ -0,0 +1,263 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi
import android.media.AudioAttributes
import android.media.AudioDeviceCallback
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.os.Bundle
import android.os.Handler
import android.widget.Switch
import android.widget.Toast
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.Preference.OnPreferenceChangeListener
import androidx.preference.PreferenceFragment
import androidx.preference.SwitchPreference
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.dlog
import co.aospa.dolby.xiaomi.R
import com.android.settingslib.widget.MainSwitchPreference
import com.android.settingslib.widget.OnMainSwitchChangeListener
class DolbySettingsFragment : PreferenceFragment(),
OnPreferenceChangeListener, OnMainSwitchChangeListener {
private val switchBar by lazy {
findPreference<MainSwitchPreference>(DolbyConstants.PREF_ENABLE)!!
}
private val profilePref by lazy {
findPreference<ListPreference>(DolbyConstants.PREF_PROFILE)!!
}
private val presetPref by lazy {
findPreference<ListPreference>(DolbyConstants.PREF_PRESET)!!
}
private val stereoPref by lazy {
findPreference<ListPreference>(DolbyConstants.PREF_STEREO)!!
}
private val dialoguePref by lazy {
findPreference<ListPreference>(DolbyConstants.PREF_DIALOGUE)!!
}
private val bassPref by lazy {
findPreference<SwitchPreference>(DolbyConstants.PREF_BASS)!!
}
private val virtPref by lazy {
findPreference<SwitchPreference>(DolbyConstants.PREF_VIRTUALIZER)!!
}
private val volumePref by lazy {
findPreference<SwitchPreference>(DolbyConstants.PREF_VOLUME)!!
}
private val resetPref by lazy {
findPreference<Preference>(DolbyConstants.PREF_RESET)!!
}
private val dolbyController by lazy { DolbyController.getInstance(context) }
private val audioManager by lazy { context.getSystemService(AudioManager::class.java) }
private val handler = Handler()
private var isOnSpeaker = true
set(value) {
if (field == value) return
field = value
dlog(TAG, "setIsOnSpeaker($value)")
updateProfileSpecificPrefs()
}
private val audioDeviceCallback = object : AudioDeviceCallback() {
override fun onAudioDevicesAdded(addedDevices: Array<AudioDeviceInfo>) {
dlog(TAG, "onAudioDevicesAdded")
updateSpeakerState()
}
override fun onAudioDevicesRemoved(removedDevices: Array<AudioDeviceInfo>) {
dlog(TAG, "onAudioDevicesRemoved")
updateSpeakerState()
}
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
dlog(TAG, "onCreatePreferences")
addPreferencesFromResource(R.xml.dolby_settings)
val dsOn = dolbyController.dsOn
switchBar.addOnSwitchChangeListener(this)
switchBar.setChecked(dsOn)
profilePref.onPreferenceChangeListener = this
profilePref.setEnabled(dsOn)
val profile = dolbyController.profile
profilePref.apply {
if (entryValues.contains(profile.toString())) {
summary = "%s"
value = profile.toString()
} else {
summary = context.getString(R.string.dolby_unknown)
}
}
presetPref.onPreferenceChangeListener = this
virtPref.onPreferenceChangeListener = this
stereoPref.onPreferenceChangeListener = this
dialoguePref.onPreferenceChangeListener = this
bassPref.onPreferenceChangeListener = this
volumePref.onPreferenceChangeListener = this
resetPref.setOnPreferenceClickListener {
dolbyController.resetProfileSpecificSettings()
updateProfileSpecificPrefs()
Toast.makeText(
context,
context.getString(R.string.dolby_reset_profile_toast, profilePref.summary),
Toast.LENGTH_SHORT
).show()
true
}
audioManager.registerAudioDeviceCallback(audioDeviceCallback, handler)
updateSpeakerState()
updateProfileSpecificPrefs()
}
override fun onDestroyView() {
dlog(TAG, "onDestroyView")
audioManager.unregisterAudioDeviceCallback(audioDeviceCallback)
super.onDestroyView()
}
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
dlog(TAG, "onPreferenceChange: key=${preference.key} value=$newValue")
when (preference.key) {
DolbyConstants.PREF_PROFILE -> {
dolbyController.profile = newValue.toString().toInt()
updateProfileSpecificPrefs()
}
DolbyConstants.PREF_PRESET -> {
dolbyController.preset = newValue.toString()
}
DolbyConstants.PREF_VIRTUALIZER -> {
if (isOnSpeaker)
dolbyController.speakerVirtEnabled = newValue as Boolean
else
dolbyController.headphoneVirtEnabled = newValue as Boolean
}
DolbyConstants.PREF_STEREO -> {
dolbyController.stereoWideningAmount = newValue.toString().toInt()
}
DolbyConstants.PREF_DIALOGUE -> {
dolbyController.dialogueEnhancerAmount = newValue.toString().toInt()
}
DolbyConstants.PREF_BASS -> {
dolbyController.bassEnhancerEnabled = newValue as Boolean
}
DolbyConstants.PREF_VOLUME -> {
dolbyController.volumeLevelerEnabled = newValue as Boolean
}
else -> return false
}
return true
}
override fun onSwitchChanged(switchView: Switch, isChecked: Boolean) {
dlog(TAG, "onSwitchChanged($isChecked)")
dolbyController.dsOn = isChecked
profilePref.setEnabled(isChecked)
updateProfileSpecificPrefs()
}
private fun updateSpeakerState() {
val device = audioManager.getDevicesForAttributes(ATTRIBUTES_MEDIA)[0]
isOnSpeaker = (device.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)
}
private fun updateProfileSpecificPrefs() {
val unknownRes = context.getString(R.string.dolby_unknown)
val headphoneRes = context.getString(R.string.dolby_connect_headphones)
val dsOn = dolbyController.dsOn
val currentProfile = dolbyController.profile
dlog(
TAG, "updateProfileSpecificPrefs: dsOn=$dsOn currentProfile=$currentProfile"
+ " isOnSpeaker=$isOnSpeaker"
)
val enable = dsOn && (currentProfile != -1)
presetPref.setEnabled(enable)
virtPref.setEnabled(enable)
dialoguePref.setEnabled(enable)
volumePref.setEnabled(enable)
resetPref.setEnabled(enable)
stereoPref.setEnabled(enable && !isOnSpeaker)
bassPref.setEnabled(enable && !isOnSpeaker)
if (!enable) return
val preset = dolbyController.preset
presetPref.apply {
if (entryValues.contains(preset)) {
summary = "%s"
value = preset
} else {
summary = unknownRes
}
}
val deValue = dolbyController.dialogueEnhancerAmount.toString()
dialoguePref.apply {
if (entryValues.contains(deValue)) {
summary = "%s"
value = deValue
} else {
summary = unknownRes
}
}
virtPref.setChecked(if (isOnSpeaker) {
dolbyController.speakerVirtEnabled
} else {
dolbyController.headphoneVirtEnabled
})
volumePref.setChecked(dolbyController.volumeLevelerEnabled)
// below prefs are not enabled on loudspeaker
if (isOnSpeaker) {
stereoPref.summary = headphoneRes
bassPref.summary = headphoneRes
return
}
val swValue = dolbyController.stereoWideningAmount.toString()
stereoPref.apply {
if (entryValues.contains(swValue)) {
summary = "%s"
value = swValue
} else {
summary = unknownRes
}
}
bassPref.apply {
setChecked(dolbyController.bassEnhancerEnabled)
summary = null
}
}
companion object {
private const val TAG = "DolbySettingsFragment"
private val ATTRIBUTES_MEDIA = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
}
}

View File

@ -1,44 +0,0 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
public class DolbyTileService extends TileService {
private static final String TAG = "DolbyTileService";
@Override
public void onStartListening() {
Tile tile = getQsTile();
DolbyUtils dolbyUtils = DolbyUtils.getInstance(getApplicationContext());
if (dolbyUtils.getDsOn()) {
tile.setState(Tile.STATE_ACTIVE);
} else {
tile.setState(Tile.STATE_INACTIVE);
}
tile.setSubtitle(dolbyUtils.getProfileName());
tile.updateTile();
super.onStartListening();
}
@Override
public void onClick() {
Tile tile = getQsTile();
DolbyUtils dolbyUtils = DolbyUtils.getInstance(getApplicationContext());
if (dolbyUtils.getDsOn()) {
dolbyUtils.setDsOn(false);
tile.setState(Tile.STATE_INACTIVE);
} else {
dolbyUtils.setDsOn(true);
tile.setState(Tile.STATE_ACTIVE);
}
tile.updateTile();
super.onClick();
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
private const val TAG = "DolbyTileService"
class DolbyTileService : TileService() {
private val dolbyController by lazy { DolbyController.getInstance(applicationContext) }
override fun onStartListening() {
qsTile.apply {
state = if (dolbyController.dsOn) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
subtitle = dolbyController.getProfileName() ?: getString(R.string.dolby_unknown)
updateTile()
}
super.onStartListening()
}
override fun onClick() {
val isDsOn = dolbyController.dsOn
dolbyController.dsOn = !isDsOn
qsTile.apply {
state = if (isDsOn) Tile.STATE_INACTIVE else Tile.STATE_ACTIVE
updateTile()
}
super.onClick()
}
}

View File

@ -1,262 +0,0 @@
/*
* Copyright (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi;
import android.content.Context;
import android.content.SharedPreferences;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.AudioManager.AudioPlaybackCallback;
import android.media.AudioPlaybackConfiguration;
import android.media.session.MediaSessionManager;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
import co.aospa.dolby.xiaomi.DolbyConstants.DsParam;
import co.aospa.dolby.xiaomi.R;
import java.util.Arrays;
import java.util.List;
public final class DolbyUtils {
private static final String TAG = "DolbyUtils";
private static final int EFFECT_PRIORITY = 100;
private static final int VOLUME_LEVELER_AMOUNT = 2;
private static DolbyUtils mInstance;
private DolbyAtmos mDolbyAtmos;
private Context mContext;
private AudioManager mAudioManager;
private Handler mHandler = new Handler();
private boolean mCallbacksRegistered = false;
// Restore current profile on every media session
private final AudioPlaybackCallback mPlaybackCallback = new AudioPlaybackCallback() {
@Override
public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
boolean isPlaying = configs.stream().anyMatch(
c -> c.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
dlog("onPlaybackConfigChanged isPlaying=" + isPlaying);
if (isPlaying)
setCurrentProfile();
}
};
// Restore current profile on audio device change
private final AudioDeviceCallback mAudioDeviceCallback = new AudioDeviceCallback() {
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
dlog("onAudioDevicesAdded");
setCurrentProfile();
}
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
dlog("onAudioDevicesRemoved");
setCurrentProfile();
}
};
private DolbyUtils(Context context) {
mContext = context;
mDolbyAtmos = new DolbyAtmos(EFFECT_PRIORITY, 0);
mAudioManager = context.getSystemService(AudioManager.class);
dlog("initalized");
}
public static synchronized DolbyUtils getInstance(Context context) {
if (mInstance == null) {
mInstance = new DolbyUtils(context);
}
return mInstance;
}
public void onBootCompleted() {
dlog("onBootCompleted");
// Restore current profile now and on certain audio changes.
final boolean dsOn = getDsOn();
mDolbyAtmos.setEnabled(dsOn);
registerCallbacks(dsOn);
if (dsOn)
setCurrentProfile();
}
private void checkEffect() {
if (!mDolbyAtmos.hasControl()) {
Log.w(TAG, "lost control, recreating effect");
mDolbyAtmos.release();
mDolbyAtmos = new DolbyAtmos(EFFECT_PRIORITY, 0);
}
}
private void setCurrentProfile() {
if (!getDsOn()) {
dlog("setCurrentProfile: skip, dolby is off");
return;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
int profile = Integer.parseInt(prefs.getString(
DolbySettingsFragment.PREF_PROFILE, "0" /*dynamic*/));
setProfile(profile);
}
private void registerCallbacks(boolean register) {
dlog("registerCallbacks(" + register + ") mCallbacksRegistered=" + mCallbacksRegistered);
if (register && !mCallbacksRegistered) {
mAudioManager.registerAudioPlaybackCallback(mPlaybackCallback, mHandler);
mAudioManager.registerAudioDeviceCallback(mAudioDeviceCallback, mHandler);
mCallbacksRegistered = true;
} else if (!register && mCallbacksRegistered) {
mAudioManager.unregisterAudioPlaybackCallback(mPlaybackCallback);
mAudioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
mCallbacksRegistered = false;
}
}
public void setDsOn(boolean on) {
checkEffect();
dlog("setDsOn: " + on);
mDolbyAtmos.setDsOn(on);
registerCallbacks(on);
if (on)
setCurrentProfile();
}
public boolean getDsOn() {
boolean on = mDolbyAtmos.getDsOn();
dlog("getDsOn: " + on);
return on;
}
public void setProfile(int index) {
checkEffect();
dlog("setProfile: " + index);
mDolbyAtmos.setProfile(index);
}
public int getProfile() {
int profile = mDolbyAtmos.getProfile();
dlog("getProfile: " + profile);
return profile;
}
public String getProfileName() {
String profile = Integer.toString(mDolbyAtmos.getProfile());
List<String> profiles = Arrays.asList(mContext.getResources().getStringArray(
R.array.dolby_profile_values));
int profileIndex = profiles.indexOf(profile);
dlog("getProfileName: profile=" + profile + " index=" + profileIndex);
return profileIndex == -1 ? null : mContext.getResources().getStringArray(
R.array.dolby_profile_entries)[profileIndex];
}
public void resetProfileSpecificSettings() {
checkEffect();
mDolbyAtmos.resetProfileSpecificSettings();
}
public void setPreset(String preset) {
checkEffect();
int[] gains = Arrays.stream(preset.split(",")).mapToInt(Integer::parseInt).toArray();
dlog("setPreset: " + Arrays.toString(gains));
mDolbyAtmos.setDapParameter(DsParam.GEQ_BAND_GAINS, gains);
}
public String getPreset() {
int[] gains = mDolbyAtmos.getDapParameter(DsParam.GEQ_BAND_GAINS);
dlog("getPreset: " + Arrays.toString(gains));
String[] preset = Arrays.stream(gains).mapToObj(String::valueOf).toArray(String[]::new);
return String.join(",", preset);
}
public void setHeadphoneVirtualizerEnabled(boolean enable) {
checkEffect();
dlog("setHeadphoneVirtualizerEnabled: " + enable);
mDolbyAtmos.setDapParameterBool(DsParam.HEADPHONE_VIRTUALIZER, enable);
}
public boolean getHeadphoneVirtualizerEnabled() {
boolean enabled = mDolbyAtmos.getDapParameterBool(DsParam.HEADPHONE_VIRTUALIZER);
dlog("getHeadphoneVirtualizerEnabled: " + enabled);
return enabled;
}
public void setSpeakerVirtualizerEnabled(boolean enable) {
checkEffect();
dlog("setSpeakerVirtualizerEnabled: " + enable);
mDolbyAtmos.setDapParameterBool(DsParam.SPEAKER_VIRTUALIZER, enable);
}
public boolean getSpeakerVirtualizerEnabled() {
boolean enabled = mDolbyAtmos.getDapParameterBool(DsParam.SPEAKER_VIRTUALIZER);
dlog("getSpeakerVirtualizerEnabled: " + enabled);
return enabled;
}
public void setStereoWideningAmount(int amount) {
checkEffect();
dlog("setStereoWideningAmount: " + amount);
mDolbyAtmos.setDapParameterInt(DsParam.STEREO_WIDENING_AMOUNT, amount);
}
public int getStereoWideningAmount() {
int amount = mDolbyAtmos.getDapParameterInt(DsParam.STEREO_WIDENING_AMOUNT);
dlog("getStereoWideningAmount: " + amount);
return amount;
}
public void setDialogueEnhancerAmount(int amount) {
checkEffect();
dlog("setDialogueEnhancerAmount: " + amount);
mDolbyAtmos.setDapParameterBool(DsParam.DIALOGUE_ENHANCER_ENABLE, amount > 0);
mDolbyAtmos.setDapParameterInt(DsParam.DIALOGUE_ENHANCER_AMOUNT, amount);
}
public int getDialogueEnhancerAmount() {
boolean enabled = mDolbyAtmos.getDapParameterBool(
DsParam.DIALOGUE_ENHANCER_ENABLE);
int amount = enabled ? mDolbyAtmos.getDapParameterInt(
DsParam.DIALOGUE_ENHANCER_AMOUNT) : 0;
dlog("getDialogueEnhancerAmount: " + enabled + " amount=" + amount);
return amount;
}
public void setBassEnhancerEnabled(boolean enable) {
checkEffect();
dlog("setBassEnhancerEnabled: " + enable);
mDolbyAtmos.setDapParameterBool(DsParam.BASS_ENHANCER_ENABLE, enable);
}
public boolean getBassEnhancerEnabled() {
boolean enabled = mDolbyAtmos.getDapParameterBool(DsParam.BASS_ENHANCER_ENABLE);
dlog("getBassEnhancerEnabled: " + enabled);
return enabled;
}
public void setVolumeLevelerEnabled(boolean enable) {
checkEffect();
dlog("setVolumeLevelerEnabled: " + enable);
mDolbyAtmos.setDapParameterBool(DsParam.VOLUME_LEVELER_ENABLE, enable);
mDolbyAtmos.setDapParameterInt(DsParam.VOLUME_LEVELER_AMOUNT,
enable ? VOLUME_LEVELER_AMOUNT : 0);
}
public boolean getVolumeLevelerEnabled() {
boolean enabled = mDolbyAtmos.getDapParameterBool(DsParam.VOLUME_LEVELER_ENABLE);
int amount = mDolbyAtmos.getDapParameterInt(DsParam.VOLUME_LEVELER_AMOUNT);
dlog("getVolumeLevelerEnabled: " + enabled + " amount=" + amount);
return enabled && (amount == VOLUME_LEVELER_AMOUNT);
}
private static void dlog(String msg) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, msg);
}
}
}

View File

@ -1,88 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
* (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import co.aospa.dolby.xiaomi.R;
import java.util.Arrays;
import java.util.List;
/** Provide preference summary for injected items. */
public class SummaryProvider extends ContentProvider {
private static final String KEY_DOLBY = "dolby";
@Override
public Bundle call(String method, String uri, Bundle extras) {
final Bundle bundle = new Bundle();
String summary;
switch (method) {
case KEY_DOLBY:
summary = getDolbySummary();
break;
default:
throw new IllegalArgumentException("Unknown method: " + method);
}
bundle.putString(META_DATA_PREFERENCE_SUMMARY, summary);
return bundle;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
throw new UnsupportedOperationException();
}
@Override
public String getType(Uri uri) {
throw new UnsupportedOperationException();
}
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException();
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
private String getDolbySummary() {
final DolbyUtils dolbyUtils = DolbyUtils.getInstance(getContext());
final boolean dsOn = dolbyUtils.getDsOn();
if (!dsOn) {
return getContext().getString(R.string.dolby_off);
}
final String profileName = dolbyUtils.getProfileName();
if (profileName == null) {
return getContext().getString(R.string.dolby_on);
} else {
return getContext().getString(R.string.dolby_on_with_profile, profileName);
}
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2019 The Android Open Source Project
* (C) 2023-24 Paranoid Android
*
* SPDX-License-Identifier: Apache-2.0
*/
package co.aospa.dolby.xiaomi
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import co.aospa.dolby.xiaomi.R
import com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY
private const val KEY_DOLBY = "dolby"
/** Provide preference summary for injected items. */
class SummaryProvider : ContentProvider() {
override fun call(
method: String,
arg: String?,
extras: Bundle?
): Bundle? {
val summary = when (method) {
KEY_DOLBY -> getDolbySummary()
else -> return null
}
return Bundle().apply {
putString(META_DATA_PREFERENCE_SUMMARY, summary)
}
}
override fun onCreate(): Boolean = true
override fun query(
uri: Uri,
projection: Array<String>?,
selection: String?,
selectionArgs: Array<String>?,
sortOrder: String?
): Cursor? = null
override fun getType(uri: Uri): String? = null
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int = 0
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<String>?
): Int = 0
private fun getDolbySummary(): String {
val dolbyController = DolbyController.getInstance(context)
if (!dolbyController.dsOn) {
return context.getString(R.string.dolby_off)
}
return dolbyController.getProfileName()?.let {
context.getString(R.string.dolby_on_with_profile, it)
} ?: context.getString(R.string.dolby_on)
}
}