--- /dev/null
+//
+// Copyright (C) 2022 The LineageOS 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.
+//
+
+android_app {
+ name: "SamsungDAP",
+ defaults: ["SettingsLibDefaults"],
+ srcs: ["src/**/*.kt"],
+ certificate: "platform",
+ platform_apis: true,
+ static_libs: ["androidx.preference_preference"],
+ system_ext_specific: true,
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The LineageOS 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.lineageos.dap"
+ android:sharedUserId="android.uid.system"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+
+ <application
+ android:defaultToDeviceProtectedStorage="true"
+ android:directBootAware="true"
+ android:label="SamsungDAP"
+ android:persistent="true"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.SubSettingsBase">
+
+ <activity
+ android:name=".DolbyActivity"
+ android:exported="true"
+ android:label="@string/dolby_title">
+ <intent-filter>
+ <action android:name="com.android.settings.action.IA_SETTINGS" />
+ </intent-filter>
+ <meta-data
+ android:name="com.android.settings.category"
+ android:value="com.android.settings.category.ia.sound" />
+ </activity>
+
+ <receiver
+ android:name=".BootCompletedReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </receiver>
+
+ <service
+ android:name=".DolbyTile"
+ android:exported="true"
+ android:icon="@drawable/ic_dolby"
+ android:label="@string/dolby_title"
+ android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
+ <intent-filter>
+ <action android:name="android.service.quicksettings.action.QS_TILE" />
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+
+ <path android:pathData="M0 0h24v24H0z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M20 4H4c-1.1 0-2 0.9-2 2v12c0 1.1 0.9 2 2 2h16c1.1 0 2-0.9 2-2V6c0-1.1-0.9-2-2-2zM7.76 16.24l-1.41 1.41C4.78 16.1 4 14.05 4 12c0-2.05 0.78 -4.1 2.34-5.66l1.41 1.41C6.59 8.93 6 10.46 6 12s0.59 3.07 1.76 4.24zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm5.66 1.66l-1.41-1.41C17.41 15.07 18 13.54 18 12s-0.59-3.07-1.76-4.24l1.41-1.41C19.22 7.9 20 9.95 20 12c0 2.05-0.78 4.1-2.34 5.66zM12 10c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2z" />
+</vector>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="#000"
+ android:pathData="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z" />
+</vector>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Title -->
+ <string name="dolby_title" translatable="false">Dolby Atmos</string>
+
+ <!-- Switch bar -->
+ <string name="dolby_enable_title">Enable Dolby Atmos</string>
+
+ <!-- Intro -->
+ <string name="dolby_top_intro_summary">Experience breakthrough audio for media playback that flows above and around you</string>
+
+ <!-- Profiles -->
+ <string name="dolby_profile_auto">Auto Profile</string>
+ <string name="dolby_profile_game">Game Profile</string>
+ <string name="dolby_profile_game_1">Game 1 Profile</string>
+ <string name="dolby_profile_game_2">Game 2 Profile</string>
+ <string name="dolby_profile_movie">Movie Profile</string>
+ <string name="dolby_profile_music">Music Profile</string>
+ <string name="dolby_profile_off">No Profile</string>
+ <string name="dolby_profile_spacial_audio">Spacial Audio Profile</string>
+ <string name="dolby_profile_voice">Voice Profile</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/dolby_title">
+
+ <com.android.settingslib.widget.MainSwitchPreference
+ android:defaultValue="false"
+ android:key="dolby_enable"
+ android:title="@string/dolby_enable_title" />
+
+ <com.android.settingslib.widget.TopIntroPreference
+ android:key="dolby_top_intro"
+ android:title="@string/dolby_top_intro_summary" />
+
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:defaultValue="true"
+ android:key="dolby_profile_auto"
+ android:title="@string/dolby_profile_auto" />
+
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:key="dolby_profile_game"
+ android:title="@string/dolby_profile_game" />
+
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:key="dolby_profile_game_1"
+ android:title="@string/dolby_profile_game_1" />
+
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:key="dolby_profile_game_2"
+ android:title="@string/dolby_profile_game_2" />
+
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:key="dolby_profile_movie"
+ android:title="@string/dolby_profile_movie" />
+
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:key="dolby_profile_music"
+ android:title="@string/dolby_profile_music" />
+
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:key="dolby_profile_off"
+ android:title="@string/dolby_profile_off" />
+
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:key="dolby_profile_spacial_audio"
+ android:title="@string/dolby_profile_spacial_audio" />
+
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:key="dolby_profile_voice"
+ android:title="@string/dolby_profile_voice" />
+
+</PreferenceScreen>
--- /dev/null
+/*
+ * Copyright (C) 2022 The LineageOS 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.lineageos.dap
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+
+import androidx.preference.PreferenceManager
+
+import org.lineageos.dap.DolbyFragment.Companion.PREF_DOLBY_ENABLE
+import org.lineageos.dap.DolbyFragment.Companion.PREF_DOLBY_MODES
+
+class BootCompletedReceiver : BroadcastReceiver() {
+
+ override fun onReceive(context: Context, intent: Intent) {
+ val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context)
+ for ((key, value) in PREF_DOLBY_MODES) {
+ if (sharedPrefs.getBoolean(key, false)) {
+ DolbyCore.setProfile(value)
+ break
+ }
+ }
+ DolbyCore.setEnabled(sharedPrefs.getBoolean(PREF_DOLBY_ENABLE, false))
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2022 The LineageOS 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.lineageos.dap
+
+import android.os.Bundle
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity
+import com.android.settingslib.collapsingtoolbar.R
+
+class DolbyActivity : CollapsingToolbarBaseActivity() {
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ fragmentManager.beginTransaction().replace(
+ R.id.content_frame,
+ DolbyFragment()
+ ).commit()
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2022 The LineageOS 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.lineageos.dap
+
+import android.media.audiofx.AudioEffect
+
+import java.util.UUID
+
+object DolbyCore {
+ private const val EFFECT_PARAM_PROFILE = 0
+ private const val EFFECT_PARAM_EFF_ENAB = 19
+
+ const val PROFILE_AUTO = 0
+ const val PROFILE_MOVIE = 1
+ const val PROFILE_MUSIC = 2
+ const val PROFILE_VOICE = 3
+ const val PROFILE_GAME = 4
+ const val PROFILE_OFF = 5
+ const val PROFILE_GAME_1 = 6
+ const val PROFILE_GAME_2 = 7
+ const val PROFILE_SPACIAL_AUDIO = 8
+
+ private val audioEffect = AudioEffect(
+ UUID.fromString("46d279d9-9be7-453d-9d7c-ef937f675587"), AudioEffect.EFFECT_TYPE_NULL, 0, 0
+ )
+
+ fun setProfile(profile: Int) {
+ audioEffect.setParameter(EFFECT_PARAM_EFF_ENAB, 1)
+ audioEffect.setParameter(EFFECT_PARAM_PROFILE, profile)
+ }
+
+ fun setEnabled(enabled: Boolean) {
+ audioEffect.enabled = enabled
+ }
+
+ fun isEnabled() = audioEffect.enabled
+}
--- /dev/null
+/*
+ * Copyright (C) 2022 The LineageOS 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.lineageos.dap
+
+import android.os.Bundle
+import android.widget.Switch
+
+import androidx.preference.PreferenceFragment
+
+import com.android.settingslib.widget.MainSwitchPreference
+import com.android.settingslib.widget.OnMainSwitchChangeListener
+import com.android.settingslib.widget.RadioButtonPreference
+
+import org.lineageos.dap.R
+
+class DolbyFragment : PreferenceFragment(), OnMainSwitchChangeListener {
+
+ private lateinit var switchBar: MainSwitchPreference
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ addPreferencesFromResource(R.xml.dolby_settings)
+
+ switchBar = findPreference<MainSwitchPreference>(PREF_DOLBY_ENABLE)!!
+ switchBar.addOnSwitchChangeListener(this)
+ switchBar.isChecked = DolbyCore.isEnabled()
+
+ for ((key, value) in PREF_DOLBY_MODES) {
+ val preference = findPreference<RadioButtonPreference>(key)!!
+ preference.setOnPreferenceClickListener {
+ setProfile(value)
+ true
+ }
+ }
+ }
+
+ override fun onSwitchChanged(switchView: Switch, isChecked: Boolean) {
+ DolbyCore.setEnabled(isChecked)
+ }
+
+ private fun setProfile(profile: Int) {
+ DolbyCore.setProfile(profile)
+
+ for ((key, value) in PREF_DOLBY_MODES) {
+ val preference = findPreference<RadioButtonPreference>(key)!!
+ preference.isChecked = value == profile
+ }
+ }
+
+ companion object {
+ const val PREF_DOLBY_ENABLE = "dolby_enable"
+
+ val PREF_DOLBY_MODES = mapOf(
+ "dolby_profile_auto" to DolbyCore.PROFILE_AUTO,
+ "dolby_profile_movie" to DolbyCore.PROFILE_MOVIE,
+ "dolby_profile_music" to DolbyCore.PROFILE_MUSIC,
+ "dolby_profile_voice" to DolbyCore.PROFILE_VOICE,
+ "dolby_profile_game" to DolbyCore.PROFILE_GAME,
+ "dolby_profile_off" to DolbyCore.PROFILE_OFF,
+ "dolby_profile_game_1" to DolbyCore.PROFILE_GAME_1,
+ "dolby_profile_game_2" to DolbyCore.PROFILE_GAME_2,
+ "dolby_profile_spacial_audio" to DolbyCore.PROFILE_SPACIAL_AUDIO,
+ )
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2022 The LineageOS 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.lineageos.dap
+
+import android.service.quicksettings.Tile
+import android.service.quicksettings.TileService
+
+import androidx.preference.PreferenceManager
+
+import org.lineageos.dap.DolbyFragment.Companion.PREF_DOLBY_ENABLE
+
+class DolbyTile : TileService() {
+ private var isEnabled = false
+ set(value) {
+ field = value
+ qsTile.state = if (value) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
+ qsTile.updateTile()
+ }
+
+ override fun onStartListening() {
+ super.onStartListening()
+ isEnabled = DolbyCore.isEnabled()
+ }
+
+ override fun onClick() {
+ isEnabled = !isEnabled
+ DolbyCore.setEnabled(isEnabled)
+ PreferenceManager.getDefaultSharedPreferences(this)
+ .edit()
+ .putBoolean(PREF_DOLBY_ENABLE, isEnabled)
+ .commit()
+ }
+}