Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e6e67a60b | ||
|
|
ea228a83fe | ||
|
|
5af4457253 | ||
|
|
e73db17e01 | ||
|
|
48f0028833 | ||
|
|
644e3d1535 | ||
|
|
2f541dfbfc | ||
|
|
10cf15c402 | ||
|
|
0323f7b0e6 | ||
|
|
872e163ae9 | ||
|
|
bdf015820e | ||
|
|
5ff2752c40 | ||
|
|
8cb5e216a8 | ||
|
|
c3061b6b82 | ||
|
|
2407c73977 | ||
|
|
c12f2b7f19 | ||
|
|
fbb6973d74 | ||
|
|
4d300dc97e | ||
|
|
a8f7cfdf39 | ||
|
|
1e0db9dfa6 | ||
|
|
9854381e8a | ||
|
|
3808a4ae68 | ||
|
|
6b582cabb8 | ||
|
|
3c023d81a5 | ||
|
|
b84903b8ae | ||
|
|
8ed00feb62 | ||
|
|
aac59d1bb9 | ||
|
|
e2a08fae39 | ||
|
|
a55e7da413 | ||
|
|
9e64f1bfa4 | ||
|
|
ac49d682fc | ||
|
|
1c85c69ecd | ||
|
|
58fa6a7535 | ||
|
|
4ab4637909 | ||
|
|
a445c78924 | ||
|
|
fcb804fe98 | ||
|
|
3ca0074fef | ||
|
|
a43373294b | ||
|
|
d080d60171 | ||
|
|
ab8b987d02 | ||
|
|
1fdea7dc97 | ||
|
|
99c749a64c | ||
|
|
649443ae12 | ||
|
|
87ec3c12c3 | ||
|
|
beea2617ad | ||
|
|
d3353c6ab3 | ||
|
|
040e50155c | ||
|
|
91d15fa7af | ||
|
|
dbf0dc9428 | ||
|
|
2c14234077 | ||
|
|
f18dcb8e17 | ||
|
|
aae52ffce4 | ||
|
|
f677ebecd8 | ||
|
|
40ef19ab76 | ||
|
|
e400c781e8 | ||
|
|
b0a11aa8b4 | ||
|
|
5bb09cf6fa | ||
|
|
2eaa37731a | ||
|
|
04c945f5b3 | ||
|
|
15e7009f6f | ||
|
|
94a8ea7957 | ||
|
|
636ce206a0 | ||
|
|
33a419e8c1 | ||
|
|
064c24617e | ||
|
|
4d7a952308 | ||
|
|
06bfc823ef | ||
|
|
641b58600b | ||
|
|
6454f6c267 | ||
|
|
6785f93e35 | ||
|
|
3bb55820e8 | ||
|
|
0b8722e673 | ||
|
|
3a4813d67b | ||
|
|
1300e116bc | ||
|
|
88247d9e05 | ||
|
|
bb6f814a3d | ||
|
|
b56de4957c |
@@ -1 +0,0 @@
|
||||
../../build/soong/scripts/system-clang-format
|
||||
13
.clang-format
Normal file
13
.clang-format
Normal file
@@ -0,0 +1,13 @@
|
||||
BasedOnStyle: Google
|
||||
Standard: Cpp11
|
||||
AccessModifierOffset: -2
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
ColumnLimit: 100
|
||||
CommentPragmas: NOLINT:.*
|
||||
DerivePointerAlignment: false
|
||||
IncludeBlocks: Preserve
|
||||
IndentWidth: 4
|
||||
ContinuationIndentWidth: 8
|
||||
PointerAlignment: Left
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
13
.github/workflows/build/action.yml
vendored
Normal file
13
.github/workflows/build/action.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: build
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: pip install pre-commit
|
||||
|
||||
- name: Lint
|
||||
shell: bash
|
||||
run: pre-commit run --all
|
||||
38
.github/workflows/gerrit.yml
vendored
Normal file
38
.github/workflows/gerrit.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: gerrit checks
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
type: string
|
||||
gerrit-ref:
|
||||
type: string
|
||||
change:
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: lineageos-infra/fetch-gerrit-change@main
|
||||
with:
|
||||
gerrit-ref: ${{ inputs.gerrit-ref }}
|
||||
ref: ${{ inputs.ref }}
|
||||
|
||||
- name: Check if build/action.yml exists
|
||||
id: check
|
||||
run: |
|
||||
if [ -f ./.github/workflows/build/action.yml ]; then
|
||||
echo "run=1" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
if: ${{ steps.check.outputs.run }}
|
||||
uses: ./.github/workflows/build
|
||||
|
||||
- uses: lineageos-infra/gerrit-vote@main
|
||||
if: ${{ steps.check.outputs.run && always() }}
|
||||
with:
|
||||
auth: ${{ secrets.GERRIT_VOTE_CREDS }}
|
||||
change: ${{ inputs.change }}
|
||||
ref: ${{ inputs.ref }}
|
||||
20
.pre-commit-config.yaml
Normal file
20
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v21.1.8
|
||||
hooks:
|
||||
- id: clang-format
|
||||
types_or: [c, c++]
|
||||
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
|
||||
rev: v2.15.0
|
||||
hooks:
|
||||
- id: pretty-format-kotlin
|
||||
args: [--autofix, --ktfmt, --ktfmt-style=kotlinlang]
|
||||
48
DSPVolumeSynchronizer/Android.bp
Normal file
48
DSPVolumeSynchronizer/Android.bp
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// Copyright (C) 2024-2025 The LineageOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
android_app {
|
||||
name: "DSPVolumeSynchronizer",
|
||||
certificate: "platform",
|
||||
srcs: ["src/**/*.java"],
|
||||
platform_apis: true,
|
||||
privileged: true,
|
||||
system_ext_specific: true,
|
||||
static_libs: [
|
||||
"androidx.core_core",
|
||||
"SettingsLib",
|
||||
],
|
||||
|
||||
required: [
|
||||
"privapp-permissions-dspvolume",
|
||||
"config-dspvolume",
|
||||
"preinstalled-packages-platform-dspvolume",
|
||||
],
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "privapp-permissions-dspvolume",
|
||||
relative_install_path: "permissions",
|
||||
src: "privapp-permissions-dspvolume.xml",
|
||||
system_ext_specific: true,
|
||||
filename_from_src: true,
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "config-dspvolume",
|
||||
relative_install_path: "sysconfig",
|
||||
src: "config-dspvolume.xml",
|
||||
system_ext_specific: true,
|
||||
filename_from_src: true,
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "preinstalled-packages-platform-dspvolume",
|
||||
relative_install_path: "sysconfig",
|
||||
src: "preinstalled-packages-platform-dspvolume.xml",
|
||||
system_ext_specific: true,
|
||||
filename_from_src: true,
|
||||
}
|
||||
30
DSPVolumeSynchronizer/AndroidManifest.xml
Normal file
30
DSPVolumeSynchronizer/AndroidManifest.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.lineageos.dspvolume.xiaomi"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0"
|
||||
android:sharedUserId="android.uid.system">
|
||||
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
|
||||
<application
|
||||
android:label="@string/app_name"
|
||||
android:persistent="true"
|
||||
android:defaultToDeviceProtectedStorage="true"
|
||||
android:directBootAware="true">
|
||||
|
||||
<receiver
|
||||
android:name=".BootReceiver"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
|
||||
<intent-filter android:priority="999">
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".VolumeListenerService" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
5
DSPVolumeSynchronizer/config-dspvolume.xml
Normal file
5
DSPVolumeSynchronizer/config-dspvolume.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<config>
|
||||
<allow-in-power-save package="org.lineageos.dspvolume.xiaomi" />
|
||||
<hidden-api-whitelisted-app package="org.lineageos.dspvolume.xiaomi" />
|
||||
</config>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2025 crDroid Android 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.
|
||||
-->
|
||||
<config>
|
||||
|
||||
<!-- Dolby -->
|
||||
<install-in-user-type package="org.lineageos.dspvolume.xiaomi">
|
||||
<install-in user-type="FULL" />
|
||||
<install-in user-type="PROFILE" />
|
||||
<do-not-install-in user-type="android.os.usertype.profile.CLONE" />
|
||||
<do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
|
||||
</install-in-user-type>
|
||||
</config>
|
||||
9
DSPVolumeSynchronizer/privapp-permissions-dspvolume.xml
Normal file
9
DSPVolumeSynchronizer/privapp-permissions-dspvolume.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<permissions>
|
||||
<privapp-permissions package="org.lineageos.dspvolume.xiaomi">
|
||||
<permission name="android.permission.INTERACT_ACROSS_USERS" />
|
||||
<permission name="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||
<permission name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<permission name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
</privapp-permissions>
|
||||
</permissions>
|
||||
5
DSPVolumeSynchronizer/res/values/strings.xml
Normal file
5
DSPVolumeSynchronizer/res/values/strings.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- App Name -->
|
||||
<string name="app_name">DSP Volume Synchronizer</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.lineageos.dspvolume.xiaomi;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
|
||||
public class BootReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
context.startService(new Intent(context, VolumeListenerService.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.lineageos.dspvolume.xiaomi;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.util.Log;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
public class VolumeListenerReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(intent.getIntExtra("android.media.EXTRA_VOLUME_STREAM_TYPE", 0) == AudioManager.STREAM_MUSIC) {
|
||||
AudioManager audioManager = context.getSystemService(AudioManager.class);
|
||||
int current = intent.getIntExtra(
|
||||
"android.media.EXTRA_VOLUME_STREAM_VALUE",
|
||||
0
|
||||
);
|
||||
audioManager.setParameters("volume_change=" + current + ";flags=8");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.lineageos.dspvolume.xiaomi;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.media.AudioManager;
|
||||
import android.os.IBinder;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class VolumeListenerService extends Service {
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction("android.media.VOLUME_CHANGED_ACTION");
|
||||
registerReceiver(new VolumeListenerReceiver(), intentFilter);
|
||||
|
||||
AudioManager audioManager = getSystemService(AudioManager.class);
|
||||
int current = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
|
||||
audioManager.setParameters("volume_change=" + current + ";flags=8");
|
||||
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 The LineageOS Project
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.ifaa.aidl.manager
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.IHwBinder
|
||||
import android.os.SystemProperties
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import org.ifaa.aidl.manager.IfaaManagerService
|
||||
import org.json.JSONObject
|
||||
import vendor.xiaomi.hardware.mlipay.V1_1.IMlipayService
|
||||
|
||||
class IfaaService : Service() {
|
||||
private var _mlipayService: IMlipayService? = null
|
||||
|
||||
private val mlipayServiceDeathRecipient = IHwBinder.DeathRecipient {
|
||||
Log.i(LOG_TAG, "mlipay service died")
|
||||
_mlipayService = null
|
||||
}
|
||||
|
||||
private val mBinder = object : IfaaManagerService.Stub() {
|
||||
override fun getSupportBIOTypes(): Int {
|
||||
val fpVendor = SystemProperties.get(FP_VENDOR_PROP, "")
|
||||
val isUdfps = SystemProperties.getBoolean(IS_UDFPS_PROP, false)
|
||||
|
||||
val supportedBioMask = when (!invalidFpVendors.contains(fpVendor.lowercase())) {
|
||||
true -> AUTH_TYPE_FINGERPRINT or AUTH_TYPE_IRIS
|
||||
else -> AUTH_TYPE_IRIS
|
||||
}
|
||||
|
||||
val ifaaProp = SystemProperties.getInt(
|
||||
SUPPORTED_BIO_MASK_PROP, 0
|
||||
) and supportedBioMask or when (isUdfps) {
|
||||
true -> AUTH_TYPE_OPTICAL_FINGERPRINT
|
||||
else -> 0
|
||||
}
|
||||
|
||||
return ifaaProp
|
||||
}
|
||||
|
||||
override fun startBIOManager(authType: Int) = when (authType) {
|
||||
AUTH_TYPE_FINGERPRINT -> {
|
||||
val intent = Intent(Settings.ACTION_SECURITY_SETTINGS).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
|
||||
applicationContext.startActivity(intent)
|
||||
|
||||
COMMAND_OK
|
||||
}
|
||||
|
||||
else -> COMMAND_FAIL
|
||||
}
|
||||
|
||||
override fun getDeviceModel() = "${Build.MANUFACTURER}-${Build.DEVICE}"
|
||||
|
||||
override fun processCmd(param: ByteArray) = getMlipayService()?.let { mlipayService ->
|
||||
var receiveBuffer: ByteArray? = null
|
||||
|
||||
val paramByteArray = ArrayList<Byte>().apply {
|
||||
for (byte in param) {
|
||||
add(byte)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
val receiveBufferByteArray = mlipayService.invoke_command(
|
||||
paramByteArray, paramByteArray.size
|
||||
)
|
||||
|
||||
receiveBuffer = receiveBufferByteArray.toByteArray()
|
||||
} catch (e: Exception) {
|
||||
Log.e(LOG_TAG, "processCmdImpl: mlipay invoke_command failed", e)
|
||||
}
|
||||
|
||||
receiveBuffer
|
||||
}
|
||||
|
||||
override fun getVersion() = 4
|
||||
|
||||
override fun getExtInfo(authType: Int, keyExtInfo: String) = initExtString()
|
||||
|
||||
override fun setExtInfo(authType: Int, keyExtInfo: String, valExtInfo: String) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
override fun getEnabled(bioType: Int) = when (bioType) {
|
||||
AUTH_TYPE_FINGERPRINT -> BIOMETRICS_AVAILABLE
|
||||
else -> SCREEN_LOCK_NONE
|
||||
}
|
||||
|
||||
override fun getIDList(bioType: Int): IntArray {
|
||||
var idList = IntArray(0)
|
||||
|
||||
getMlipayService()?.let { mlipayService ->
|
||||
try {
|
||||
val idListAL = mlipayService.ifaa_get_idlist(bioType)
|
||||
|
||||
idList = idListAL.toIntArray()
|
||||
} catch (e: Exception) {
|
||||
Log.e(LOG_TAG, "getIDListImpl: mlipay ifaa_get_idlist failed", e)
|
||||
}
|
||||
}
|
||||
|
||||
return idList
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent) = mBinder
|
||||
|
||||
private fun getMlipayService() = _mlipayService ?: runCatching {
|
||||
IMlipayService.getService(true)
|
||||
}.onSuccess {
|
||||
_mlipayService = it
|
||||
it.linkToDeath(mlipayServiceDeathRecipient, 0)
|
||||
}.getOrNull()
|
||||
|
||||
private fun initExtString(): String {
|
||||
val obj = JSONObject()
|
||||
val keyInfo = JSONObject()
|
||||
|
||||
val xy = SystemProperties.get(UDFPS_LOCATION_X_Y_PROP, "")
|
||||
val wh = SystemProperties.get(UDFPS_SIZE_W_H_PROP, "")
|
||||
|
||||
try {
|
||||
if (!validateVal(xy) || !validateVal(wh)) {
|
||||
Log.e(LOG_TAG, "initExtString: invalidate, xy: $xy, wh: $wh")
|
||||
return ""
|
||||
}
|
||||
|
||||
val split = xy.split(",")
|
||||
val split2 = wh.split(",")
|
||||
|
||||
keyInfo.put("startX", split[0].toInt())
|
||||
keyInfo.put("startY", split[1].toInt())
|
||||
keyInfo.put("width", split2[0].toInt())
|
||||
keyInfo.put("height", split2[1].toInt())
|
||||
keyInfo.put("navConflict", true)
|
||||
|
||||
obj.put("type", 0)
|
||||
obj.put("fullView", keyInfo)
|
||||
|
||||
return obj.toString()
|
||||
} catch (e: Exception) {
|
||||
Log.e(LOG_TAG, "initExtString: Exception, xy: $xy, wh: $wh", e)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateVal(str: String) = !"".equals(str, ignoreCase = true) && str.contains(",")
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = IfaaService::class.simpleName!!
|
||||
|
||||
private const val AUTH_TYPE_NOT_SUPPORT = 0
|
||||
private const val AUTH_TYPE_FINGERPRINT = 1
|
||||
private const val AUTH_TYPE_IRIS = 1.shl(1)
|
||||
private const val AUTH_TYPE_OPTICAL_FINGERPRINT = 1.shl(2)
|
||||
|
||||
private const val BIOMETRICS_AVAILABLE = 1000
|
||||
private const val SCREEN_LOCK_NONE = 1003
|
||||
|
||||
private const val COMMAND_OK = 0
|
||||
private const val COMMAND_FAIL = -1
|
||||
|
||||
private const val SUPPORTED_BIO_MASK_PROP = "persist.vendor.sys.pay.ifaa"
|
||||
private const val FP_VENDOR_PROP = "persist.vendor.sys.fp.vendor"
|
||||
private const val IS_UDFPS_PROP = "ro.hardware.fp.udfps"
|
||||
private const val UDFPS_LOCATION_X_Y_PROP = "persist.vendor.sys.fp.udfps.location.X_Y"
|
||||
private const val UDFPS_SIZE_W_H_PROP = "persist.vendor.sys.fp.udfps.size.width_height"
|
||||
|
||||
private val invalidFpVendors = arrayOf(
|
||||
"",
|
||||
"none",
|
||||
)
|
||||
}
|
||||
}
|
||||
10
README.md
Normal file
10
README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# hardware/xiaomi
|
||||
|
||||
## Soong options
|
||||
|
||||
| Namespace | Variable | Description | Default |
|
||||
| --------- | -------- | ----------- | ------- |
|
||||
| XIAOMI_BIOMETRICS_FINGERPRINT | RUN_32BIT | Opt to run service in 32-bit mode only | false |
|
||||
| XIAOMI_TOUCH | HIGH_TOUCH_POLLING_PATH | HighTouchPollingRate feature control path | |
|
||||
| XIAOMI_TOUCH | KEY_DISABLER_CONTROL_PATH | KeyDisabler feature control path | |
|
||||
| XIAOMI_TOUCH | KEY_SWAPPER_CONTROL_PATH | KeySwapper feature control path | |
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (C) 2024 The LineageOS Project
|
||||
// Copyright (C) 2024-2025 The LineageOS Project
|
||||
// 2024 Paranoid Android
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
@@ -8,9 +8,9 @@
|
||||
soong_config_module_type {
|
||||
name: "xiaomi_hardware_biometrics_config_default",
|
||||
module_type: "cc_defaults",
|
||||
config_namespace: "xiaomi_hardware_biometrics",
|
||||
config_namespace: "XIAOMI_BIOMETRICS_FINGERPRINT",
|
||||
bool_variables: [
|
||||
"run_32bit",
|
||||
"RUN_32BIT",
|
||||
],
|
||||
properties: ["compile_multilib"],
|
||||
}
|
||||
@@ -19,7 +19,7 @@ xiaomi_hardware_biometrics_config_default {
|
||||
name: "xiaomi_hardware_biometrics_config_default",
|
||||
|
||||
soong_config_variables: {
|
||||
run_32bit: {
|
||||
RUN_32BIT: {
|
||||
conditions_default: {
|
||||
compile_multilib: "64",
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The LineageOS Project
|
||||
* Copyright (C) 2024-2025 The LineageOS Project
|
||||
* 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
@@ -12,8 +12,11 @@
|
||||
#include "util/Util.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
using ::android::base::SetProperty;
|
||||
|
||||
namespace aidl::android::hardware::biometrics::fingerprint {
|
||||
|
||||
namespace {
|
||||
@@ -67,15 +70,19 @@ Fingerprint::Fingerprint(std::shared_ptr<FingerprintConfig> config) : mConfig(st
|
||||
}
|
||||
ALOGI("Opened fingerprint HAL, class: %s, module_id: %s", class_name.c_str(),
|
||||
class_module_id.c_str());
|
||||
SetProperty("persist.vendor.sys.fp.vendor", class_name);
|
||||
break;
|
||||
}
|
||||
if (!mDevice) {
|
||||
ALOGE("Can't open any fingerprint HAL module");
|
||||
SetProperty("persist.vendor.sys.fp.vendor", "none");
|
||||
}
|
||||
}
|
||||
|
||||
std::string sensorTypeProp = mConfig->get<std::string>("type");
|
||||
if (sensorTypeProp == "udfps" || sensorTypeProp == "udfps_optical") {
|
||||
SetProperty("ro.hardware.fp.udfps", "true");
|
||||
|
||||
if (sensorTypeProp == "udfps") {
|
||||
mSensorType = FingerprintSensorType::UNDER_DISPLAY_ULTRASONIC;
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The LineageOS Project
|
||||
* Copyright (C) 2024-2025 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -364,9 +364,15 @@ void Session::notify(const fingerprint_msg_t* msg) {
|
||||
HardwareAuthToken authToken;
|
||||
translate(hat, authToken);
|
||||
|
||||
if (mUdfpsHandler) {
|
||||
mUdfpsHandler->onAuthenticationSucceeded();
|
||||
}
|
||||
mCb->onAuthenticationSucceeded(msg->data.authenticated.finger.fid, authToken);
|
||||
mLockoutTracker.reset(true);
|
||||
} else {
|
||||
if (mUdfpsHandler) {
|
||||
mUdfpsHandler->onAuthenticationFailed();
|
||||
}
|
||||
mCb->onAuthenticationFailed();
|
||||
mLockoutTracker.addFailedAttempt();
|
||||
checkSensorLockout();
|
||||
|
||||
@@ -5,4 +5,6 @@ service vendor.fingerprint-default /vendor/bin/hw/android.hardware.biometrics.fi
|
||||
class late_start
|
||||
user system
|
||||
group system input uhid
|
||||
capabilities SYS_NICE
|
||||
shutdown critical
|
||||
task_profiles ProcessCapacityHigh MaxPerformance
|
||||
|
||||
48
aidl/health/Android.bp
Normal file
48
aidl/health/Android.bp
Normal file
@@ -0,0 +1,48 @@
|
||||
cc_defaults {
|
||||
name: "android.hardware.health-service.xiaomi-defaults",
|
||||
relative_install_path: "hw",
|
||||
vintf_fragments: ["android.hardware.health-service.xiaomi.xml"],
|
||||
vendor: true,
|
||||
recovery_available: true,
|
||||
|
||||
defaults: [
|
||||
"libhealth_aidl_impl_user",
|
||||
],
|
||||
|
||||
include_dirs: [
|
||||
"system/core/healthd",
|
||||
"system/core/healthd/include",
|
||||
"system/core/healthd/include_charger"
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libhealth_aidl_impl",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"main.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.health-service.xiaomi",
|
||||
recovery: false,
|
||||
vendor: true,
|
||||
defaults: ["android.hardware.health-service.xiaomi-defaults"],
|
||||
init_rc: ["android.hardware.health-service.xiaomi.rc"],
|
||||
overrides: ["charger","android.hardware.health-service.qti"],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.health-service.xiaomi_recovery",
|
||||
vendor: false,
|
||||
recovery: true,
|
||||
defaults: ["android.hardware.health-service.xiaomi-defaults"],
|
||||
init_rc: ["android.hardware.health-service.xiaomi_recovery.rc"],
|
||||
overrides: ["charger.recovery", "android.hardware.health-service.qti_recovery"],
|
||||
}
|
||||
14
aidl/health/android.hardware.health-service.xiaomi.rc
Normal file
14
aidl/health/android.hardware.health-service.xiaomi.rc
Normal file
@@ -0,0 +1,14 @@
|
||||
service vendor.health-default /vendor/bin/hw/android.hardware.health-service.xiaomi
|
||||
class hal
|
||||
user system
|
||||
group system
|
||||
capabilities WAKE_ALARM BLOCK_SUSPEND
|
||||
file /dev/kmsg w
|
||||
|
||||
service vendor.charger /vendor/bin/hw/android.hardware.health-service.xiaomi --charger
|
||||
class charger
|
||||
seclabel u:r:charger_vendor:s0
|
||||
user system
|
||||
group system wakelock input graphics
|
||||
capabilities SYS_BOOT WAKE_ALARM BLOCK_SUSPEND
|
||||
file /dev/kmsg w
|
||||
12
aidl/health/android.hardware.health-service.xiaomi.xml
Normal file
12
aidl/health/android.hardware.health-service.xiaomi.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<!--
|
||||
Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
-->
|
||||
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.health</name>
|
||||
<version>3</version>
|
||||
<fqname>IHealth/default</fqname>
|
||||
</hal>
|
||||
</manifest>
|
||||
@@ -0,0 +1,7 @@
|
||||
service vendor.health-recovery /system/bin/hw/android.hardware.health-service.xiaomi_recovery
|
||||
class hal
|
||||
seclabel u:r:hal_health_default:s0
|
||||
user system
|
||||
group system
|
||||
capabilities WAKE_ALARM BLOCK_SUSPEND
|
||||
file /dev/kmsg w
|
||||
123
aidl/health/main.cpp
Normal file
123
aidl/health/main.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#define LOG_TAG "android.hardware.health-service.xiaomi"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/binder_interface_utils.h>
|
||||
#include <health/utils.h>
|
||||
#include <health-impl/ChargerUtils.h>
|
||||
#include <health-impl/Health.h>
|
||||
#include <cutils/klog.h>
|
||||
|
||||
using aidl::android::hardware::health::HalHealthLoop;
|
||||
using aidl::android::hardware::health::Health;
|
||||
|
||||
#if !CHARGER_FORCE_NO_UI
|
||||
using aidl::android::hardware::health::charger::ChargerCallback;
|
||||
using aidl::android::hardware::health::charger::ChargerModeMain;
|
||||
namespace aidl::android::hardware::health {
|
||||
class ChargerCallbackImpl : public ChargerCallback {
|
||||
public:
|
||||
ChargerCallbackImpl(const std::shared_ptr<Health>& service) : ChargerCallback(service) {}
|
||||
bool ChargerEnableSuspend() override { return true; }
|
||||
};
|
||||
} //namespace aidl::android::hardware::health
|
||||
#endif
|
||||
|
||||
namespace aidl::android::hardware::health {
|
||||
static constexpr int kChargeCounterMultiplier = 1000; // mAh to uAh
|
||||
static constexpr int kChargeTimeToFullMultiplier = 60; // mins to secs
|
||||
class HealthImpl : public Health {
|
||||
public:
|
||||
using Health::Health;
|
||||
virtual ~HealthImpl() {}
|
||||
ndk::ScopedAStatus getChargeCounterUah(int32_t* out) override;
|
||||
protected:
|
||||
void UpdateHealthInfo(HealthInfo* health_info) override;
|
||||
};
|
||||
void HealthImpl::UpdateHealthInfo(HealthInfo* health_info) {
|
||||
if (health_info->batteryChargeTimeToFullNowSeconds == 65535) {
|
||||
health_info->batteryChargeTimeToFullNowSeconds = -1;
|
||||
} else {
|
||||
health_info->batteryChargeTimeToFullNowSeconds *= kChargeTimeToFullMultiplier;
|
||||
}
|
||||
health_info->batteryChargeCounterUah *= kChargeCounterMultiplier;
|
||||
}
|
||||
ndk::ScopedAStatus HealthImpl::getChargeCounterUah(int32_t* out) {
|
||||
*out *= kChargeCounterMultiplier;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
} // namespace aidl::android::hardware::health
|
||||
|
||||
static constexpr const char* gInstanceName = "default";
|
||||
static constexpr std::string_view gChargerArg{"--charger"};
|
||||
|
||||
constexpr char ucsiPSYName[]{"ucsi-source-psy-soc:qcom,pmic_glink:qcom,ucsi1"};
|
||||
|
||||
#define RETRY_COUNT 100
|
||||
|
||||
void qti_healthd_board_init(struct healthd_config *hc)
|
||||
{
|
||||
int fd;
|
||||
unsigned char retries = RETRY_COUNT;
|
||||
int ret = 0;
|
||||
unsigned char buf;
|
||||
|
||||
hc->ignorePowerSupplyNames.push_back(android::String8(ucsiPSYName));
|
||||
retry:
|
||||
if (!retries) {
|
||||
KLOG_ERROR(LOG_TAG, "Cannot open battery/capacity, fd=%d\n", fd);
|
||||
return;
|
||||
}
|
||||
|
||||
fd = open("/sys/class/power_supply/battery/capacity", 0440);
|
||||
if (fd >= 0) {
|
||||
KLOG_INFO(LOG_TAG, "opened battery/capacity after %d retries\n", RETRY_COUNT - retries);
|
||||
while (retries) {
|
||||
ret = read(fd, &buf, 1);
|
||||
if(ret >= 0) {
|
||||
KLOG_INFO(LOG_TAG, "Read Batt Capacity after %d retries ret : %d\n", RETRY_COUNT - retries, ret);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
retries--;
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
KLOG_ERROR(LOG_TAG, "Failed to read Battery Capacity ret=%d\n", ret);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
retries--;
|
||||
usleep(100000);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef __ANDROID_RECOVERY__
|
||||
android::base::InitLogging(argv, android::base::KernelLogger);
|
||||
#endif
|
||||
auto config = std::make_unique<healthd_config>();
|
||||
qti_healthd_board_init(config.get());
|
||||
::android::hardware::health::InitHealthdConfig(config.get());
|
||||
auto binder = ndk::SharedRefBase::make<aidl::android::hardware::health::HealthImpl>(gInstanceName, std::move(config));
|
||||
|
||||
if (argc >= 2 && argv[1] == gChargerArg) {
|
||||
#if !CHARGER_FORCE_NO_UI
|
||||
KLOG_INFO(LOG_TAG, "Starting charger mode with UI.");
|
||||
auto charger_callback = std::make_shared<aidl::android::hardware::health::ChargerCallbackImpl>(binder);
|
||||
return ChargerModeMain(binder, charger_callback);
|
||||
#endif
|
||||
KLOG_INFO(LOG_TAG, "Starting charger mode without UI.");
|
||||
} else {
|
||||
KLOG_INFO(LOG_TAG, "Starting health HAL.");
|
||||
}
|
||||
|
||||
auto hal_health_loop = std::make_shared<HalHealthLoop>(binder, binder);
|
||||
return hal_health_loop->StartLoop();
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2024 The LineageOS Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.ir-service.xiaomi",
|
||||
relative_install_path: "hw",
|
||||
vendor: true,
|
||||
init_rc: ["android.hardware.ir-service.xiaomi.rc"],
|
||||
vintf_fragments: ["android.hardware.ir-service.xiaomi.xml"],
|
||||
srcs: [
|
||||
"ConsumerIr.cpp",
|
||||
"service.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"android.hardware.ir-V1-ndk",
|
||||
],
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2024 The LineageOS Project
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_TAG "ConsumerIr"
|
||||
|
||||
#include "ConsumerIr.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/lirc.h>
|
||||
#include <string>
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace ir {
|
||||
|
||||
static const std::string kIrDevice = "/dev/lirc0";
|
||||
|
||||
static vector<ConsumerIrFreqRange> kRangeVec{
|
||||
{.minHz = 30000, .maxHz = 60000},
|
||||
};
|
||||
|
||||
::ndk::ScopedAStatus ConsumerIr::getCarrierFreqs(vector<ConsumerIrFreqRange>* _aidl_return) {
|
||||
*_aidl_return = kRangeVec;
|
||||
|
||||
return ::ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
::ndk::ScopedAStatus ConsumerIr::transmit(int32_t carrierFreqHz, const vector<int32_t>& pattern) {
|
||||
size_t entries = pattern.size();
|
||||
|
||||
if (entries == 0) {
|
||||
return ::ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
int fd = open(kIrDevice.c_str(), O_RDWR);
|
||||
if (fd < 0) {
|
||||
LOG(ERROR) << "Failed to open " << kIrDevice << ", error " << fd;
|
||||
|
||||
return ::ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
|
||||
int rc = ioctl(fd, LIRC_SET_SEND_CARRIER, &carrierFreqHz);
|
||||
if (rc < 0) {
|
||||
LOG(ERROR) << "Failed to set carrier " << carrierFreqHz << ", error: " << errno;
|
||||
|
||||
close(fd);
|
||||
|
||||
return ::ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
if ((entries & 1) != 0) {
|
||||
rc = write(fd, pattern.data(), entries * sizeof(int32_t));
|
||||
} else {
|
||||
rc = write(fd, pattern.data(), (entries - 1) * sizeof(int32_t));
|
||||
usleep(pattern[entries - 1]);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
LOG(ERROR) << "Failed to write pattern, " << entries << " entries, error: " << errno;
|
||||
|
||||
close(fd);
|
||||
|
||||
return ::ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return ::ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The LineageOS Project
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/ir/BnConsumerIr.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace ir {
|
||||
|
||||
class ConsumerIr : public BnConsumerIr {
|
||||
public:
|
||||
::ndk::ScopedAStatus getCarrierFreqs(
|
||||
::std::vector<::aidl::android::hardware::ir::ConsumerIrFreqRange>* _aidl_return)
|
||||
override;
|
||||
::ndk::ScopedAStatus transmit(int32_t carrierFreqHz,
|
||||
const ::std::vector<int32_t>& pattern) override;
|
||||
};
|
||||
|
||||
} // namespace ir
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
||||
@@ -1,14 +0,0 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 The LineageOS Project
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
on early-boot
|
||||
# IR device
|
||||
chown system system /dev/lirc0
|
||||
|
||||
service vendor.ir-default /vendor/bin/hw/android.hardware.ir-service.xiaomi
|
||||
class hal
|
||||
user system
|
||||
group system
|
||||
shutdown critical
|
||||
@@ -1,11 +0,0 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2024 The LineageOS Project
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.ir</name>
|
||||
<version>1</version>
|
||||
<fqname>IConsumerIr/default</fqname>
|
||||
</hal>
|
||||
</manifest>
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The LineageOS Project
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "ConsumerIr.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.h>
|
||||
|
||||
using aidl::android::hardware::ir::ConsumerIr;
|
||||
|
||||
int main() {
|
||||
ABinderProcess_setThreadPoolMaxThreadCount(0);
|
||||
std::shared_ptr<ConsumerIr> hal = ::ndk::SharedRefBase::make<ConsumerIr>();
|
||||
|
||||
const std::string instance = std::string(ConsumerIr::descriptor) + "/default";
|
||||
binder_status_t status = AServiceManager_addService(hal->asBinder().get(), instance.c_str());
|
||||
CHECK_EQ(status, STATUS_OK);
|
||||
|
||||
ABinderProcess_joinThreadPool();
|
||||
return EXIT_FAILURE; // should not reach
|
||||
}
|
||||
@@ -19,6 +19,12 @@ package {
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
vintf_fragment {
|
||||
name: "android.hardware.sensors.xiaomi-multihal.xml",
|
||||
src: "android.hardware.sensors.xiaomi-multihal.xml",
|
||||
vendor: true,
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.sensors-service.xiaomi-multihal",
|
||||
vendor: true,
|
||||
@@ -33,12 +39,12 @@ cc_binary {
|
||||
"android.hardware.sensors@2.X-shared-utils",
|
||||
],
|
||||
init_rc: ["android.hardware.sensors-service.xiaomi-multihal.rc"],
|
||||
vintf_fragments: ["android.hardware.sensors.xiaomi-multihal.xml"],
|
||||
vintf_fragment_modules: ["android.hardware.sensors.xiaomi-multihal.xml"],
|
||||
shared_libs: [
|
||||
"android.hardware.sensors@2.0-ScopedWakelock",
|
||||
"android.hardware.sensors@2.0",
|
||||
"android.hardware.sensors@2.1",
|
||||
"android.hardware.sensors-V2-ndk",
|
||||
"android.hardware.sensors-V3-ndk",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libfmq",
|
||||
|
||||
@@ -147,9 +147,9 @@ Return<void> HalProxy::getSensorsList_2_1(ISensorsV2_1::getSensorsList_2_1_cb _h
|
||||
Return<void> HalProxy::getSensorsList(ISensorsV2_0::getSensorsList_cb _hidl_cb) {
|
||||
std::vector<V1_0::SensorInfo> sensors;
|
||||
for (const auto& iter : mSensors) {
|
||||
if (iter.second.type != SensorType::HINGE_ANGLE) {
|
||||
sensors.push_back(convertToOldSensorInfo(iter.second));
|
||||
}
|
||||
if (iter.second.type != SensorType::HINGE_ANGLE) {
|
||||
sensors.push_back(convertToOldSensorInfo(iter.second));
|
||||
}
|
||||
}
|
||||
_hidl_cb(sensors);
|
||||
return Void();
|
||||
@@ -612,7 +612,8 @@ void HalProxy::handlePendingWrites() {
|
||||
static_cast<uint32_t>(EventQueueFlagBits::EVENTS_READ),
|
||||
static_cast<uint32_t>(EventQueueFlagBits::READ_AND_PROCESS),
|
||||
kPendingWriteTimeoutNs, mEventQueueFlag)) {
|
||||
ALOGE("Dropping %zu events after blockingWrite failed.", numToWrite);
|
||||
ALOGE("Dropping %zu events after blockingWrite failed (is system_server running?).",
|
||||
numToWrite);
|
||||
if (numWakeupEvents > 0) {
|
||||
if (pendingWriteEvents.size() > eventQueueSize) {
|
||||
decrementRefCountAndMaybeReleaseWakelock(
|
||||
@@ -735,8 +736,7 @@ void HalProxy::decrementRefCountAndMaybeReleaseWakelock(size_t delta,
|
||||
if (!mThreadsRun.load()) return;
|
||||
std::lock_guard<std::recursive_mutex> lockGuard(mWakelockMutex);
|
||||
if (delta > mWakelockRefCount) {
|
||||
ALOGE("Decrementing wakelock ref count by %zu when count is %zu",
|
||||
delta, mWakelockRefCount);
|
||||
ALOGE("Decrementing wakelock ref count by %zu when count is %zu", delta, mWakelockRefCount);
|
||||
}
|
||||
if (timeoutStart == -1) timeoutStart = mWakelockTimeoutResetTime;
|
||||
if (mWakelockRefCount == 0 || timeoutStart < mWakelockTimeoutResetTime) return;
|
||||
|
||||
@@ -74,8 +74,7 @@ std::vector<V2_1::Event> HalProxyCallbackBase::processEvents(const std::vector<V
|
||||
}
|
||||
const V2_1::SensorInfo& sensor = mCallback->getSensorInfo(event.sensorHandle);
|
||||
|
||||
if (sensor.type == V2_1::SensorType::PICK_UP_GESTURE
|
||||
&& event.u.scalar != 1) {
|
||||
if (sensor.type == V2_1::SensorType::PICK_UP_GESTURE && event.u.scalar != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
on boot
|
||||
setprop vendor.sensors.dynamic_sensor_op_timeout_ms 1600
|
||||
|
||||
service vendor.sensors-hal-multihal /vendor/bin/hw/android.hardware.sensors-service.xiaomi-multihal
|
||||
class hal
|
||||
user system
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.sensors</name>
|
||||
<version>2</version>
|
||||
<version>3</version>
|
||||
<fqname>ISensors/default</fqname>
|
||||
</hal>
|
||||
</manifest>
|
||||
|
||||
48
aidl/touch/Android.bp
Normal file
48
aidl/touch/Android.bp
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2025 The LineageOS Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
cc_binary {
|
||||
name: "vendor.lineage.touch-service.xiaomi",
|
||||
init_rc: ["vendor.lineage.touch-service.xiaomi.rc"],
|
||||
vintf_fragments: select(soong_config_variable("XIAOMI_TOUCH", "HIGH_TOUCH_POLLING_PATH"), {
|
||||
any: ["vendor.lineage.touch-service.xiaomi-htpr.xml"],
|
||||
default: [],
|
||||
}) + select(soong_config_variable("XIAOMI_TOUCH", "KEY_DISABLER_CONTROL_PATH"), {
|
||||
any: ["vendor.lineage.touch-service.xiaomi-kd.xml"],
|
||||
default: [],
|
||||
}) + select(soong_config_variable("XIAOMI_TOUCH", "KEY_SWAPPER_CONTROL_PATH"), {
|
||||
any: ["vendor.lineage.touch-service.xiaomi-ks.xml"],
|
||||
default: [],
|
||||
}),
|
||||
relative_install_path: "hw",
|
||||
proprietary: true,
|
||||
cppflags: select(soong_config_variable("XIAOMI_TOUCH", "HIGH_TOUCH_POLLING_PATH"), {
|
||||
any @ flag_val: ["-DHTPR_CONTROL_PATH=\"" + flag_val + "\""],
|
||||
default: [],
|
||||
}) + select(soong_config_variable("XIAOMI_TOUCH", "KEY_DISABLER_CONTROL_PATH"), {
|
||||
any @ flag_val: ["-DKD_CONTROL_PATH=\"" + flag_val + "\""],
|
||||
default: [],
|
||||
}) + select(soong_config_variable("XIAOMI_TOUCH", "KEY_SWAPPER_CONTROL_PATH"), {
|
||||
any @ flag_val: ["-DKS_CONTROL_PATH=\"" + flag_val + "\""],
|
||||
default: [],
|
||||
}),
|
||||
srcs: select(soong_config_variable("XIAOMI_TOUCH", "HIGH_TOUCH_POLLING_PATH"), {
|
||||
any: ["HighTouchPollingRate.cpp"],
|
||||
default: [],
|
||||
}) + select(soong_config_variable("XIAOMI_TOUCH", "KEY_DISABLER_CONTROL_PATH"), {
|
||||
any: ["KeyDisabler.cpp"],
|
||||
default: [],
|
||||
}) + select(soong_config_variable("XIAOMI_TOUCH", "KEY_SWAPPER_CONTROL_PATH"), {
|
||||
any: ["KeySwapper.cpp"],
|
||||
default: [],
|
||||
}) + ["service.cpp"],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"vendor.lineage.touch-V1-ndk",
|
||||
],
|
||||
}
|
||||
46
aidl/touch/HighTouchPollingRate.cpp
Normal file
46
aidl/touch/HighTouchPollingRate.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The LineageOS Project
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_TAG "vendor.lineage.touch-service.xiaomi"
|
||||
|
||||
#include "HighTouchPollingRate.h"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
using ::android::base::ReadFileToString;
|
||||
using ::android::base::Trim;
|
||||
using ::android::base::WriteStringToFile;
|
||||
|
||||
namespace aidl {
|
||||
namespace vendor {
|
||||
namespace lineage {
|
||||
namespace touch {
|
||||
|
||||
ndk::ScopedAStatus HighTouchPollingRate::getEnabled(bool* _aidl_return) {
|
||||
std::string buf;
|
||||
if (!ReadFileToString(HTPR_CONTROL_PATH, &buf)) {
|
||||
LOG(ERROR) << "Failed to read current HighTouchPollingRate state";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
*_aidl_return = Trim(buf) == "1";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus HighTouchPollingRate::setEnabled(bool enabled) {
|
||||
if (!WriteStringToFile(enabled ? "1" : "0", HTPR_CONTROL_PATH)) {
|
||||
LOG(ERROR) << "Failed to write HighTouchPollingRate state";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace touch
|
||||
} // namespace lineage
|
||||
} // namespace vendor
|
||||
} // namespace aidl
|
||||
24
aidl/touch/HighTouchPollingRate.h
Normal file
24
aidl/touch/HighTouchPollingRate.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The LineageOS Project
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/vendor/lineage/touch/BnHighTouchPollingRate.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace vendor {
|
||||
namespace lineage {
|
||||
namespace touch {
|
||||
|
||||
class HighTouchPollingRate : public BnHighTouchPollingRate {
|
||||
public:
|
||||
ndk::ScopedAStatus getEnabled(bool* _aidl_return) override;
|
||||
ndk::ScopedAStatus setEnabled(bool enabled) override;
|
||||
};
|
||||
|
||||
} // namespace touch
|
||||
} // namespace lineage
|
||||
} // namespace vendor
|
||||
} // namespace aidl
|
||||
46
aidl/touch/KeyDisabler.cpp
Normal file
46
aidl/touch/KeyDisabler.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The LineageOS Project
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_TAG "vendor.lineage.touch-service.xiaomi"
|
||||
|
||||
#include "KeyDisabler.h"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
using ::android::base::ReadFileToString;
|
||||
using ::android::base::Trim;
|
||||
using ::android::base::WriteStringToFile;
|
||||
|
||||
namespace aidl {
|
||||
namespace vendor {
|
||||
namespace lineage {
|
||||
namespace touch {
|
||||
|
||||
ndk::ScopedAStatus KeyDisabler::getEnabled(bool* _aidl_return) {
|
||||
std::string buf;
|
||||
if (!ReadFileToString(KD_CONTROL_PATH, &buf)) {
|
||||
LOG(ERROR) << "Failed to read current KeyDisabler state";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
*_aidl_return = Trim(buf) == "0";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus KeyDisabler::setEnabled(bool enabled) {
|
||||
if (!WriteStringToFile(enabled ? "0" : "1", KD_CONTROL_PATH, true)) {
|
||||
LOG(ERROR) << "Failed to write KeyDisabler state";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace touch
|
||||
} // namespace lineage
|
||||
} // namespace vendor
|
||||
} // namespace aidl
|
||||
24
aidl/touch/KeyDisabler.h
Normal file
24
aidl/touch/KeyDisabler.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The LineageOS Project
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/vendor/lineage/touch/BnKeyDisabler.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace vendor {
|
||||
namespace lineage {
|
||||
namespace touch {
|
||||
|
||||
class KeyDisabler : public BnKeyDisabler {
|
||||
public:
|
||||
ndk::ScopedAStatus getEnabled(bool* _aidl_return) override;
|
||||
ndk::ScopedAStatus setEnabled(bool enabled) override;
|
||||
};
|
||||
|
||||
} // namespace touch
|
||||
} // namespace lineage
|
||||
} // namespace vendor
|
||||
} // namespace aidl
|
||||
46
aidl/touch/KeySwapper.cpp
Normal file
46
aidl/touch/KeySwapper.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The LineageOS Project
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_TAG "vendor.lineage.touch-service.xiaomi"
|
||||
|
||||
#include "KeySwapper.h"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
using ::android::base::ReadFileToString;
|
||||
using ::android::base::Trim;
|
||||
using ::android::base::WriteStringToFile;
|
||||
|
||||
namespace aidl {
|
||||
namespace vendor {
|
||||
namespace lineage {
|
||||
namespace touch {
|
||||
|
||||
ndk::ScopedAStatus KeySwapper::getEnabled(bool* _aidl_return) {
|
||||
std::string buf;
|
||||
if (!ReadFileToString(KS_CONTROL_PATH, &buf)) {
|
||||
LOG(ERROR) << "Failed to read current KeySwapper state";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
*_aidl_return = Trim(buf) == "1";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus KeySwapper::setEnabled(bool enabled) {
|
||||
if (!WriteStringToFile(enabled ? "1" : "0", KS_CONTROL_PATH, true)) {
|
||||
LOG(ERROR) << "Failed to write KeySwapper state";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace touch
|
||||
} // namespace lineage
|
||||
} // namespace vendor
|
||||
} // namespace aidl
|
||||
24
aidl/touch/KeySwapper.h
Normal file
24
aidl/touch/KeySwapper.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The LineageOS Project
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/vendor/lineage/touch/BnKeySwapper.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace vendor {
|
||||
namespace lineage {
|
||||
namespace touch {
|
||||
|
||||
class KeySwapper : public BnKeySwapper {
|
||||
public:
|
||||
ndk::ScopedAStatus getEnabled(bool* _aidl_return) override;
|
||||
ndk::ScopedAStatus setEnabled(bool enabled) override;
|
||||
};
|
||||
|
||||
} // namespace touch
|
||||
} // namespace lineage
|
||||
} // namespace vendor
|
||||
} // namespace aidl
|
||||
48
aidl/touch/service.cpp
Normal file
48
aidl/touch/service.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The LineageOS Project
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_TAG "vendor.lineage.touch-service.xiaomi"
|
||||
|
||||
#include "HighTouchPollingRate.h"
|
||||
#include "KeyDisabler.h"
|
||||
#include "KeySwapper.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.h>
|
||||
|
||||
using aidl::vendor::lineage::touch::HighTouchPollingRate;
|
||||
using aidl::vendor::lineage::touch::KeyDisabler;
|
||||
using aidl::vendor::lineage::touch::KeySwapper;
|
||||
|
||||
int main() {
|
||||
binder_status_t status = STATUS_OK;
|
||||
|
||||
ABinderProcess_setThreadPoolMaxThreadCount(0);
|
||||
|
||||
#ifdef HTPR_CONTROL_PATH
|
||||
std::shared_ptr<HighTouchPollingRate> htpr = ndk::SharedRefBase::make<HighTouchPollingRate>();
|
||||
const std::string htpr_instance = std::string(HighTouchPollingRate::descriptor) + "/default";
|
||||
status = AServiceManager_addService(htpr->asBinder().get(), htpr_instance.c_str());
|
||||
CHECK_EQ(status, STATUS_OK) << "Failed to add service " << htpr_instance << " " << status;
|
||||
#endif
|
||||
|
||||
#ifdef KD_CONTROL_PATH
|
||||
std::shared_ptr<KeyDisabler> kd = ndk::SharedRefBase::make<KeyDisabler>();
|
||||
const std::string kd_instance = std::string(KeyDisabler::descriptor) + "/default";
|
||||
status = AServiceManager_addService(kd->asBinder().get(), kd_instance.c_str());
|
||||
CHECK_EQ(status, STATUS_OK) << "Failed to add service " << kd_instance << " " << status;
|
||||
#endif
|
||||
|
||||
#ifdef KS_CONTROL_PATH
|
||||
std::shared_ptr<KeySwapper> ks = ndk::SharedRefBase::make<KeySwapper>();
|
||||
const std::string ks_instance = std::string(KeySwapper::descriptor) + "/default";
|
||||
status = AServiceManager_addService(ks->asBinder().get(), ks_instance.c_str());
|
||||
CHECK_EQ(status, STATUS_OK) << "Failed to add service " << ks_instance << " " << status;
|
||||
#endif
|
||||
|
||||
ABinderProcess_joinThreadPool();
|
||||
return EXIT_FAILURE; // should not reach
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="hidl">
|
||||
<hal format="aidl">
|
||||
<name>vendor.lineage.touch</name>
|
||||
<transport>hwbinder</transport>
|
||||
<version>1.0</version>
|
||||
<version>1</version>
|
||||
<interface>
|
||||
<name>IHighTouchPollingRate</name>
|
||||
<instance>default</instance>
|
||||
10
aidl/touch/vendor.lineage.touch-service.xiaomi-kd.xml
Normal file
10
aidl/touch/vendor.lineage.touch-service.xiaomi-kd.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>vendor.lineage.touch</name>
|
||||
<version>1</version>
|
||||
<interface>
|
||||
<name>IKeyDisabler</name>
|
||||
<instance>default</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
</manifest>
|
||||
10
aidl/touch/vendor.lineage.touch-service.xiaomi-ks.xml
Normal file
10
aidl/touch/vendor.lineage.touch-service.xiaomi-ks.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>vendor.lineage.touch</name>
|
||||
<version>1</version>
|
||||
<interface>
|
||||
<name>IKeySwapper</name>
|
||||
<instance>default</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
</manifest>
|
||||
4
aidl/touch/vendor.lineage.touch-service.xiaomi.rc
Normal file
4
aidl/touch/vendor.lineage.touch-service.xiaomi.rc
Normal file
@@ -0,0 +1,4 @@
|
||||
service vendor.touch-hal /vendor/bin/hw/vendor.lineage.touch-service.xiaomi
|
||||
class hal
|
||||
user system
|
||||
group system
|
||||
37
dolby/Android.bp
Normal file
37
dolby/Android.bp
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright (C) 2017-2021 The LineageOS Project
|
||||
// (C) 2023-24 Paranoid Android
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
android_app {
|
||||
name: "XiaomiDolby",
|
||||
|
||||
srcs: ["src/**/*.kt"],
|
||||
resource_dirs: ["res"],
|
||||
certificate: "platform",
|
||||
platform_apis: true,
|
||||
system_ext_specific: true,
|
||||
privileged: true,
|
||||
|
||||
overrides: ["MusicFX", "AudioFX"],
|
||||
static_libs: [
|
||||
"SettingsLib",
|
||||
"SpaLib",
|
||||
"androidx.activity_activity-compose",
|
||||
"androidx.compose.material3_material3",
|
||||
"androidx.compose.runtime_runtime",
|
||||
"androidx.preference_preference",
|
||||
"org.lineageos.settings.resources",
|
||||
],
|
||||
|
||||
required: ["preinstalled-packages-platform-dolby.xml"],
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "preinstalled-packages-platform-dolby.xml",
|
||||
src: "preinstalled-packages-platform-dolby.xml",
|
||||
sub_dir: "sysconfig",
|
||||
system_ext_specific: true,
|
||||
}
|
||||
79
dolby/AndroidManifest.xml
Normal file
79
dolby/AndroidManifest.xml
Normal file
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023-24 Paranoid Android
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="co.aospa.dolby.xiaomi"
|
||||
android:sharedUserId="android.uid.system">
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<application
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:allowBackup="false"
|
||||
android:label="@string/dolby_title"
|
||||
android:persistent="true">
|
||||
|
||||
<receiver
|
||||
android:name=".BootCompletedReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter android:priority="1000">
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name=".DolbyActivity"
|
||||
android:label="@string/dolby_title"
|
||||
android:theme="@style/Theme.SubSettingsBase.Expressive"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.settings.action.IA_SETTINGS" />
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.CATEGORY_CONTENT_MUSIC" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<meta-data android:name="com.android.settings.category"
|
||||
android:value="com.android.settings.category.ia.sound" />
|
||||
<meta-data android:name="com.android.settings.summary_uri"
|
||||
android:value="content://co.aospa.dolby.xiaomi.summary/dolby" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".geq.EqualizerActivity"
|
||||
android:label="@string/dolby_preset"
|
||||
android:theme="@style/Theme.SubSettingsBase.Expressive"
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".DolbyTileService"
|
||||
android:icon="@drawable/ic_dolby_qs"
|
||||
android:label="@string/dolby_title"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name=".SummaryProvider"
|
||||
android:authorities="co.aospa.dolby.xiaomi.summary">
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
26
dolby/preinstalled-packages-platform-dolby.xml
Normal file
26
dolby/preinstalled-packages-platform-dolby.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2024 crDroid Android 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.
|
||||
-->
|
||||
<config>
|
||||
|
||||
<!-- Dolby -->
|
||||
<install-in-user-type package="co.aospa.dolby.xiaomi">
|
||||
<install-in user-type="FULL" />
|
||||
<install-in user-type="PROFILE" />
|
||||
<do-not-install-in user-type="android.os.usertype.profile.CLONE" />
|
||||
<do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
|
||||
</install-in-user-type>
|
||||
</config>
|
||||
8
dolby/res/drawable/ic_dolby.xml
Normal file
8
dolby/res/drawable/ic_dolby.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?android:attr/colorControlNormal">
|
||||
<path android:fillColor="#000000" android:pathData="M1,4.0214C2.2767,4.0743 3.5798,3.9866 4.8252,4.2063C8.8352,4.9133 11.4129,8.3489 11.0507,12.3402C10.7124,16.0695 7.3661,18.9511 3.3484,18.9651C2.5657,18.9678 1.7827,18.9441 1,18.9324L1,4.0214Z" android:strokeColor="#00000000" android:strokeWidth="1" android:fillType="evenOdd"/>
|
||||
<group>
|
||||
<clip-path android:pathData="M12.9332,4l10.0668,0l0,15l-10.0668,0z"/>
|
||||
<path android:fillColor="#000000" android:pathData="M23,4.0924L23,18.8825C19.4973,19.298 16.399,18.6968 14.3366,15.6947C12.5148,13.043 12.4594,10.2265 14.2129,7.5241C16.244,4.394 19.3953,3.7204 23,4.0924" android:strokeColor="#00000000" android:strokeWidth="1" android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</vector>
|
||||
8
dolby/res/drawable/ic_dolby_qs.xml
Normal file
8
dolby/res/drawable/ic_dolby_qs.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:fillColor="#191c1e" android:pathData="M1,4.0214C2.2767,4.0743 3.5798,3.9866 4.8252,4.2063C8.8352,4.9133 11.4129,8.3489 11.0507,12.3402C10.7124,16.0695 7.3661,18.9511 3.3484,18.9651C2.5657,18.9678 1.7827,18.9441 1,18.9324L1,4.0214Z" android:strokeColor="#00000000" android:strokeWidth="1" android:fillType="evenOdd"/>
|
||||
<group>
|
||||
<clip-path android:pathData="M12.9332,4l10.0668,0l0,15l-10.0668,0z"/>
|
||||
<path android:fillColor="#191c1e" android:pathData="M23,4.0924L23,18.8825C19.4973,19.298 16.399,18.6968 14.3366,15.6947C12.5148,13.043 12.4594,10.2265 14.2129,7.5241C16.244,4.394 19.3953,3.7204 23,4.0924" android:strokeColor="#00000000" android:strokeWidth="1" android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</vector>
|
||||
24
dolby/res/drawable/ic_ieq_balanced.xml
Normal file
24
dolby/res/drawable/ic_ieq_balanced.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="192dp"
|
||||
android:height="192dp"
|
||||
android:viewportWidth="192"
|
||||
android:viewportHeight="192"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 69.584 66.547 L 63.792 70.797 L 38.646 71.697 L 13.5 72.597 L 13.5 89.723 C 13.5 99.143 13.865 111.999 14.313 118.292 L 15.124 129.734 L 178.5 129.734 L 178.5 102.234 L 121.248 102.234 L 99.203 82.234 C 87.078 71.234 76.756 62.248 76.266 62.266 C 75.776 62.282 72.769 64.209 69.584 66.547"
|
||||
android:strokeWidth="1.25"/>
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 69.125 67.065 L 62.875 71.921 L 38.188 71.99 L 13.5 72.06 L 13.504 81.122 C 13.506 86.106 13.876 99.044 14.325 109.872 L 15.141 129.56 L 178.5 129.56 L 178.5 103.31 L 122.126 103.31 L 99.675 82.997 C 87.326 71.826 76.808 62.577 76.299 62.446 C 75.791 62.315 72.563 64.394 69.125 67.065"
|
||||
android:strokeWidth="1.25"/>
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 69.854 67.249 L 63.237 72.187 L 13.311 72.187 L 14.119 84.374 C 14.562 91.077 14.929 104.016 14.932 113.124 L 14.939 129.687 L 178.689 129.687 L 178.689 103.437 L 150.251 103.428 L 121.814 103.418 L 99.143 82.865 L 76.471 62.313 L 69.854 67.249"
|
||||
android:strokeWidth="1.25"/>
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 69.141 67.266 L 63.221 71.609 L 45.888 71.609 C 36.354 71.609 25.238 71.956 21.184 72.378 L 13.813 73.147 L 14.115 93.316 C 14.281 104.408 14.748 117.001 15.151 121.297 L 15.886 129.109 L 178.188 129.109 L 178.188 102.859 L 120.703 102.859 L 98.696 82.859 C 86.594 71.859 76.324 62.873 75.876 62.891 C 75.429 62.907 72.399 64.876 69.141 67.266"
|
||||
android:strokeWidth="1.25"/>
|
||||
</vector>
|
||||
24
dolby/res/drawable/ic_ieq_detailed.xml
Normal file
24
dolby/res/drawable/ic_ieq_detailed.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="192dp"
|
||||
android:height="192dp"
|
||||
android:viewportWidth="192"
|
||||
android:viewportHeight="192"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 126.625 63.749 C 120.886 65.812 115.12 69.774 103.5 79.643 C 85.566 94.873 77.207 98.716 62.179 98.637 C 51.344 98.582 42.841 96.091 34.946 90.659 C 29.282 86.763 22.25 77.097 22.25 73.209 C 22.25 71.466 21.259 70.954 17.875 70.954 L 13.5 70.954 L 13.5 129.704 L 178.5 129.704 L 178.5 86.329 L 174.915 81.381 C 169.567 73.999 161.404 67.534 153.865 64.714 C 146.447 61.939 132.988 61.463 126.625 63.749"
|
||||
android:strokeWidth="1.25"/>
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 128.493 63.479 C 122.229 65.384 117.026 68.819 104.59 79.26 C 89.19 92.189 83.945 95.359 73.54 98.024 C 60.438 101.381 44.281 98.268 33.966 90.4 C 28.19 85.994 22.25 77.679 22.25 73.999 C 22.25 71.475 21.634 71.061 17.875 71.061 L 13.5 71.061 L 13.5 129.811 L 178.5 129.811 L 178.494 108.249 L 178.488 86.686 L 173.807 80.541 C 163.119 66.51 143.193 59.008 128.493 63.479"
|
||||
android:strokeWidth="1.25"/>
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 129.75 63.348 C 122.865 65.451 115.829 70.014 103.442 80.413 C 96.136 86.547 86.969 92.947 82.48 95.048 C 60.304 105.426 32.044 97.046 23.267 77.492 C 21.242 72.979 20.328 72.179 17.191 72.179 L 13.5 72.179 L 13.5 129.679 L 178.5 129.679 L 178.456 108.742 C 178.41 86.981 178.329 86.587 172.313 78.669 C 163.463 67.022 142.469 59.466 129.75 63.348"
|
||||
android:strokeWidth="1.25"/>
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 131.184 63.546 C 123.825 64.992 117.559 68.919 103.5 80.894 C 96.969 86.458 87.98 92.824 83.526 95.043 C 75.941 98.821 74.591 99.077 62.25 99.077 C 49.875 99.077 48.564 98.827 40.714 94.962 C 31.655 90.503 24.469 83.298 22.19 76.396 C 21.04 72.909 20.19 72.202 17.152 72.202 L 13.5 72.202 L 13.5 129.702 L 178.5 129.702 L 178.5 87.577 L 174.965 82.702 C 170.49 76.532 163.525 70.403 157.488 67.323 C 153.545 65.312 139.684 61.884 137.333 62.339 C 136.944 62.414 134.176 62.957 131.184 63.546"
|
||||
android:strokeWidth="1.25"/>
|
||||
</vector>
|
||||
13
dolby/res/drawable/ic_ieq_off.xml
Normal file
13
dolby/res/drawable/ic_ieq_off.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="192dp"
|
||||
android:height="192dp"
|
||||
android:viewportWidth="192"
|
||||
android:viewportHeight="192"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 13.5 96 L 13.5 103.5 L 178.5 103.5 L 178.5 88.5 L 13.5 88.5 L 13.5 96"
|
||||
android:strokeWidth="1.25"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
28
dolby/res/drawable/ic_ieq_warm.xml
Normal file
28
dolby/res/drawable/ic_ieq_warm.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="192dp"
|
||||
android:height="192dp"
|
||||
android:viewportWidth="192"
|
||||
android:viewportHeight="192"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 48.789 64.919 C 45.865 66.366 40.885 70.429 37.721 73.948 C 28.465 84.243 13.479 116.446 13.479 126.041 L 13.479 129.786 L 95.979 129.786 C 141.354 129.786 178.479 129.486 178.479 129.12 C 178.479 128.753 177.131 125.8 175.483 122.558 C 170.413 112.579 162.723 104.006 155.705 100.509 C 149.673 97.503 148.053 97.286 131.582 97.286 C 105.944 97.286 102.89 95.881 86.335 76.454 C 74.977 63.124 61.056 58.848 48.789 64.919"
|
||||
android:strokeWidth="1.25"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 49.712 64.871 C 40.942 68.842 33.167 78.157 25.45 93.941 C 18.063 109.047 13.462 121.585 13.462 126.604 L 13.462 129.769 L 178.496 129.769 L 176.673 125.407 C 171.895 113.971 159.81 101.317 151.203 98.739 C 148.476 97.921 138.591 97.264 128.942 97.256 C 107.671 97.241 102.106 95.265 93.347 84.617 C 76.128 63.684 64.227 58.297 49.712 64.871"
|
||||
android:strokeWidth="1.25"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 52.781 63.452 C 47.321 65.482 40.256 70.907 35.792 76.496 C 27.971 86.29 13.406 119.09 13.406 126.91 L 13.406 129.502 L 178.552 129.502 L 174.586 122.06 C 169.31 112.162 163.213 105.23 156.113 101.056 C 150.453 97.729 149.705 97.625 130.807 97.53 C 107.81 97.414 103.646 96.04 94.012 85.387 C 80.912 70.9 77.898 68.045 73.171 65.632 C 67.157 62.565 57.846 61.569 52.781 63.452"
|
||||
android:strokeWidth="1.25"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 56.078 63.069 C 44.607 65.852 35.711 75.314 26.246 94.802 C 19.707 108.263 16.537 116.731 14.817 125.334 L 14.005 129.397 L 95.98 129.397 C 165.857 129.397 177.953 129.136 177.953 127.627 C 177.953 124.146 165.22 106.857 160.048 103.314 C 152.515 98.156 150.572 97.791 129.828 97.641 C 111.478 97.507 110.935 97.424 104.33 93.776 C 99.905 91.331 95.316 87.208 91 81.798 C 80.578 68.737 74.351 64.324 64.203 62.812 C 61.797 62.453 58.141 62.568 56.078 63.069"
|
||||
android:strokeWidth="1.25"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
12
dolby/res/drawable/ic_launcher_background.xml
Normal file
12
dolby/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector android:height="108.0dip" android:width="108.0dip" android:viewportWidth="108.0" android:viewportHeight="108.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
|
||||
<group android:scaleX="1.3544992" android:scaleY="1.3544992" android:translateX="-23.22" android:translateY="-23.22">
|
||||
<group>
|
||||
<clip-path android:pathData="M57.01,57.01m-54,0a54,54 0,1 1,108 0a54,54 0,1 1,-108 0" />
|
||||
<path android:fillColor="#ffeaedef" android:pathData="M-1.9,-1.9h117.82v117.82h-117.82z" />
|
||||
<path android:fillColor="@drawable/ic_launcher_background__0" android:pathData="M-1.9,115.92l0,-117.82l117.82,0l-117.82,117.82z" />
|
||||
<path android:fillColor="@drawable/ic_launcher_background__1" android:pathData="M-1.9,-1.9h117.82v117.82h-117.82z" android:strokeAlpha="0.2" android:fillAlpha="0.2" />
|
||||
</group>
|
||||
</group>
|
||||
</vector>
|
||||
6
dolby/res/drawable/ic_launcher_background__0.xml
Normal file
6
dolby/res/drawable/ic_launcher_background__0.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<gradient android:angle="0.0" android:type="linear" android:startX="57.01" android:startY="56.4" android:endX="57.01" android:endY="-1.58"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
|
||||
<item android:color="#ffffffff" android:offset="0.0" />
|
||||
<item android:color="#fff3f5f6" android:offset="1.0" />
|
||||
</gradient>
|
||||
6
dolby/res/drawable/ic_launcher_background__1.xml
Normal file
6
dolby/res/drawable/ic_launcher_background__1.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<gradient android:angle="0.0" android:type="linear" android:startX="-1.9" android:startY="115.92" android:endX="115.92" android:endY="-1.9"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
|
||||
<item android:color="#ff80d0ce" android:offset="0.0" />
|
||||
<item android:color="#ff9fa8da" android:offset="1.0" />
|
||||
</gradient>
|
||||
8
dolby/res/drawable/ic_launcher_foreground.xml
Normal file
8
dolby/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector android:height="108.0dip" android:width="108.0dip" android:viewportWidth="108.0" android:viewportHeight="108.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<group android:scaleX="0.84" android:scaleY="0.84" android:translateX="23.76" android:translateY="23.76">
|
||||
<path android:fillColor="#ff465461" android:pathData="M12,19.13h5a16.87,16.87 0,0 1,0 33.74H12Z" />
|
||||
<path android:fillColor="#ff465461" android:pathData="M60,52.87H55a16.87,16.87 0,0 1,0 -33.74h5Z" />
|
||||
</group>
|
||||
</vector>
|
||||
4
dolby/res/drawable/ic_launcher_mono.xml
Normal file
4
dolby/res/drawable/ic_launcher_mono.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="108dp" android:width="108dp" android:viewportWidth="108" android:viewportHeight="108">
|
||||
<path android:fillColor="#000000" android:pathData="M36.6305484,38 C45.6078241,38.1592687 52.837468,45.7082857 52.837468,54.9974325 C52.837468,64.2865794 45.6078241,71.8355964 36.6307694,71.9948651 L32,72 L32,38 L36.6305484,38 Z M36.999,43.017 L36.999,66.977 L37.1225728,66.9701894 C42.9872179,66.559014 47.6837958,61.5395304 47.8337726,55.3050128 L47.837468,54.9974325 C47.837468,48.6211219 43.0833381,43.4425908 37.1223719,43.0246757 L36.999,43.017 Z M71.3694516,38 C62.3921759,38.1592687 55.162532,45.7082857 55.162532,54.9974325 C55.162532,64.2865794 62.3921759,71.8355964 71.3692306,71.9948651 L76,72 L76,38 L71.3694516,38 Z M71.000532,43.017 L71.000532,66.977 L70.8774272,66.9701894 C65.0127821,66.559014 60.3162042,61.5395304 60.1662274,55.3050128 L60.162532,54.9974325 C60.162532,48.6211219 64.9166619,43.4425908 70.8776281,43.0246757 L71.000532,43.017 Z" android:strokeWidth="1"/>
|
||||
</vector>
|
||||
11
dolby/res/drawable/ic_profile_custom.xml
Executable file
11
dolby/res/drawable/ic_profile_custom.xml
Executable file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="72.0"
|
||||
android:viewportHeight="72.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M45,9L45,27L51,27L51,21L63,21L63,15L51,15L51,9L45,9M9,15L9,21L39,21L39,15L9,15M21,27L21,33L9,33L9,39L21,39L21,45L27,45L27,27L21,27M33,33L33,39L63,39L63,33L33,33M33,45L33,63L39,63L39,57L63,57L63,51L39,51L39,45L33,45M9,51L9,57L27,57L27,51L9,51z"/>
|
||||
</vector>
|
||||
11
dolby/res/drawable/ic_profile_dynamic.xml
Executable file
11
dolby/res/drawable/ic_profile_dynamic.xml
Executable file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="72.0"
|
||||
android:viewportHeight="72.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M21,6L21,39L30,39L30,66C34.0185,62.1036 36.3516,56.7839 39.2006,52C43.3436,45.0435 48.7076,37.7933 51,30L40,30C43.3656,21.916 48.5711,14.4315 51,6L21,6z"/>
|
||||
</vector>
|
||||
11
dolby/res/drawable/ic_profile_movie.xml
Executable file
11
dolby/res/drawable/ic_profile_movie.xml
Executable file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="72.0"
|
||||
android:viewportHeight="72.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M7.84,13.82C9.62,11.74 12.52,12.02 14.98,11.95C16.97,15.97 19,19.98 21,24C24,24 27,24 30,24C28,20 26,16 24,12C26,12 28,12 30,12C32,16 34,20 36,24C39,24 42,24 45,24C43,20 41,16 39,12C41,12 43,12 45,12C47,16 49,20 51,24C54,24 57,24 60,24C58,20 56,16 54,12C58,12 62,12 66,12C65.99,25.69 66.01,39.38 66,53.07C66.42,56.49 63.62,59.98 60.12,59.96C44.39,60.06 28.66,59.97 12.93,60C9.52,60.43 6.02,57.62 6.04,54.12C5.93,42.75 6.06,31.37 6,19.99C5.95,17.82 6.08,15.36 7.84,13.82Z"/>
|
||||
</vector>
|
||||
11
dolby/res/drawable/ic_profile_music.xml
Executable file
11
dolby/res/drawable/ic_profile_music.xml
Executable file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="72.0"
|
||||
android:viewportHeight="72.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M36,9L36,40C32.245,38.8901 27.6726,38.2912 24.0154,40.179C17.5734,43.5044 15.8784,53.3215 20.4336,58.8912C25.822,65.4796 38.0937,64.1632 41.2577,55.9961C42.655,52.3894 42,47.7931 42,44L42,21L54,21L54,9L36,9z"/>
|
||||
</vector>
|
||||
9
dolby/res/drawable/reset_settings_24px.xml
Normal file
9
dolby/res/drawable/reset_settings_24px.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="?android:attr/colorControlNormal"
|
||||
android:pathData="M520,630L520,570L680,570L680,630L520,630ZM580,840L580,790L520,790L520,730L580,730L580,680L640,680L640,840L580,840ZM680,790L680,730L840,730L840,790L680,790ZM720,680L720,520L780,520L780,570L840,570L840,630L780,630L780,680L720,680ZM831,400L748,400Q722,312 649,256Q576,200 480,200Q363,200 281.5,281.5Q200,363 200,480Q200,552 232.5,612Q265,672 320,710L320,600L400,600L400,840L160,840L160,760L254,760Q192,710 156,637.5Q120,565 120,480Q120,405 148.5,339.5Q177,274 225.5,225.5Q274,177 339.5,148.5Q405,120 480,120Q609,120 706.5,199.5Q804,279 831,400Z" />
|
||||
</vector>
|
||||
9
dolby/res/drawable/save_as_24px.xml
Normal file
9
dolby/res/drawable/save_as_24px.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="?android:attr/colorControlNormal"
|
||||
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L680,120L840,280L840,492Q821,484 800.5,481.5Q780,479 760,482L760,313L647,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760L440,760L440,764L440,840L200,840ZM200,200L200,313L200,482Q200,485 200,494.5Q200,504 200,519L200,760L200,760L200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200ZM520,920L520,797L741,577Q750,568 761,564Q772,560 783,560Q795,560 806,564.5Q817,569 826,578L863,615Q871,624 875.5,635Q880,646 880,657Q880,668 876,679.5Q872,691 863,700L643,920L520,920ZM820,657L820,657L783,620L783,620L820,657ZM580,860L618,860L739,738L721,719L702,701L580,822L580,860ZM721,719L702,701L702,701L739,738L739,738L721,719ZM240,400L600,400L600,240L240,240L240,400ZM480,720Q481,720 482,720Q483,720 484,720L600,605Q600,603 600,602.5Q600,602 600,600Q600,550 565,515Q530,480 480,480Q430,480 395,515Q360,550 360,600Q360,650 395,685Q430,720 480,720Z"/>
|
||||
</vector>
|
||||
41
dolby/res/layout/ieq_icon_layout.xml
Normal file
41
dolby/res/layout/ieq_icon_layout.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2021 The Android Open Source Project
|
||||
(C) 2024 Paranoid Android
|
||||
|
||||
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.
|
||||
-->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@android:id/widget_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="56dp"
|
||||
android:gravity="end|center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingRight="0dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<androidx.preference.internal.PreferenceImageView
|
||||
android:id="@+id/ieq_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:maxWidth="56dp"
|
||||
app:maxHeight="56dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
7
dolby/res/mipmap-anydpi/ic_launcher.xml
Normal file
7
dolby/res/mipmap-anydpi/ic_launcher.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_mono" />
|
||||
</adaptive-icon>
|
||||
70
dolby/res/values-ru/strings.xml
Normal file
70
dolby/res/values-ru/strings.xml
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023-24 Paranoid Android
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Dolby Atmos -->
|
||||
<string name="dolby_title">Dolby Atmos</string>
|
||||
<string name="dolby_enable">Использовать Dolby Atmos</string>
|
||||
<string name="dolby_profile_title">Выберите профиль</string>
|
||||
<string name="dolby_preset">Графический эквалайзер</string>
|
||||
<string name="dolby_off">Выключено</string>
|
||||
<string name="dolby_on">Включено</string>
|
||||
<string name="dolby_low">Низкий</string>
|
||||
<string name="dolby_medium">Средний</string>
|
||||
<string name="dolby_high">Высокий</string>
|
||||
<string name="dolby_max">Максимальный</string>
|
||||
<string name="dolby_unknown">Неизвестно</string>
|
||||
<string name="dolby_on_with_profile">Включено (%1$s)</string>
|
||||
<string name="dolby_category_settings">Настройки</string>
|
||||
<string name="dolby_bass_enhancer">Усилитель басов</string>
|
||||
<string name="dolby_dialogue_enhancer">Усиление диалогов</string>
|
||||
<string name="dolby_spk_virtualizer">Виртуализация динамиков</string>
|
||||
<string name="dolby_hp_virtualizer">Виртуализация наушников</string>
|
||||
<string name="dolby_stereo_widening">Расширение стерео</string>
|
||||
<string name="dolby_volume_leveler">Выравниватель громкости</string>
|
||||
<string name="dolby_connect_headphones">Подключите наушники</string>
|
||||
<string name="dolby_reset_profile">Сбросить до настроек по умолчанию</string>
|
||||
<string name="dolby_reset_profile_toast">Настройки для профиля %1$s успешно сброшены</string>
|
||||
|
||||
<!-- Dolby profiles -->
|
||||
<string name="dolby_profile_dynamic">Динамический</string>
|
||||
<string name="dolby_profile_video">Кино/Видео</string>
|
||||
<string name="dolby_profile_music">Музыка</string>
|
||||
<string name="dolby_profile_voice">Голос</string>
|
||||
|
||||
<!-- Dolby equalizer presets -->
|
||||
<string name="dolby_preset_default">Плоский (выключено)</string>
|
||||
<string name="dolby_preset_rock">Рок</string>
|
||||
<string name="dolby_preset_jazz">Джаз</string>
|
||||
<string name="dolby_preset_pop">Поп</string>
|
||||
<string name="dolby_preset_classical">Классика</string>
|
||||
<string name="dolby_preset_hiphop">Хип-хоп</string>
|
||||
<string name="dolby_preset_blues">Блюз</string>
|
||||
<string name="dolby_preset_electronic">Электронная музыка</string>
|
||||
<string name="dolby_preset_country">Кантри</string>
|
||||
<string name="dolby_preset_dance">Танцевальная музыка</string>
|
||||
<string name="dolby_preset_metal">Метал</string>
|
||||
|
||||
<!-- Dolby equalizer UI -->
|
||||
<string name="dolby_geq_slider_label_gain">Усиление</string>
|
||||
<string name="dolby_geq_preset">Предустановка</string>
|
||||
<string name="dolby_geq_preset_name">Название предустановки</string>
|
||||
<string name="dolby_geq_new_preset">Новая предустановка</string>
|
||||
<string name="dolby_geq_rename_preset">Переименовать предустановку</string>
|
||||
<string name="dolby_geq_delete_preset">Удалить предустановку</string>
|
||||
<string name="dolby_geq_delete_preset_prompt">Вы хотите удалить эту предустановку?</string>
|
||||
<string name="dolby_geq_reset_gains">Сбросить усиление</string>
|
||||
<string name="dolby_geq_reset_gains_prompt">Вы хотите сбросить эту предустановку до настроек по умолчанию?</string>
|
||||
<string name="dolby_geq_preset_name_exists">Имя предустановки уже существует!</string>
|
||||
<string name="dolby_geq_preset_name_too_long">Имя предустановки слишком длинное!</string>
|
||||
|
||||
<!-- Dolby intelligent EQ -->
|
||||
<string name="dolby_ieq">Интеллектуальный эквалайзер</string>
|
||||
<string name="dolby_balanced">Сбалансированный</string>
|
||||
<string name="dolby_warm">Тёплый</string>
|
||||
<string name="dolby_detailed">Детализированный</string>
|
||||
|
||||
</resources>
|
||||
102
dolby/res/values/arrays.xml
Normal file
102
dolby/res/values/arrays.xml
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023-24 Paranoid Android
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Dolby Atmos -->
|
||||
<string-array name="dolby_profile_entries">
|
||||
<item>@string/dolby_profile_dynamic</item>
|
||||
<item>@string/dolby_profile_movie</item>
|
||||
<item>@string/dolby_profile_music</item>
|
||||
<item>@string/dolby_profile_custom</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dolby_profile_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dolby_preset_entries" translatable="false">
|
||||
<item>@string/dolby_preset_default</item>
|
||||
<item>@string/dolby_preset_rock</item>
|
||||
<item>@string/dolby_preset_jazz</item>
|
||||
<item>@string/dolby_preset_pop</item>
|
||||
<item>@string/dolby_preset_classical</item>
|
||||
<item>@string/dolby_preset_hiphop</item>
|
||||
<item>@string/dolby_preset_blues</item>
|
||||
<item>@string/dolby_preset_electronic</item>
|
||||
<item>@string/dolby_preset_metal</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dolby_preset_values">
|
||||
<!--
|
||||
<item>0,0,0,0,0,0,0,0,0,0</item>
|
||||
<item>4,1,-2,-0.25,0,-2,0,-2,0.5,4</item>
|
||||
<item>0,0,0,-1,-1,-3,-0.5,0,0,0</item>
|
||||
<item>-2,-0.5,-5,-1,0,0,-0.5,-3,-0.5,0</item>
|
||||
<item>0,0,0,0,0.5,3,1,6,2,6</item>
|
||||
<item>3,0,-3,-0.5,-0.5,-3,-0.5,0,0,2</item>
|
||||
<item>2,2,-6,-2,3,1,0,1,0,2</item>
|
||||
<item>3,1,-1,0,-0.5,-3,-0.5,0,0,0</item>
|
||||
<item>2,0,0,-1.25,-1,-4,0,0,0,0</item>
|
||||
-->
|
||||
<item>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</item>
|
||||
<item>60,36,12,-12,-36,-24,-12,-8,-4,-20,-36,-20,-4,-20,-36,-16,4,32,60,60</item>
|
||||
<item>8,8,8,8,8,0,-8,-8,-8,-24,-40,-20,0,4,8,8,8,8,8,8</item>
|
||||
<item>-13,-1,11,-25,-61,-29,3,11,19,19,19,15,11,-9,-29,-9,11,15,19,19</item>
|
||||
<item>-32,-32,-32,-32,-32,-32,-32,-28,-24,-4,16,0,-16,24,64,32,0,32,64,64</item>
|
||||
<item>52,28,4,-20,-44,-24,-4,-4,-4,-24,-44,-24,-4,0,4,4,4,20,36,36</item>
|
||||
<item>28,28,28,-36,-100,-68,-36,4,44,28,12,4,-4,4,12,4,-4,12,28,28</item>
|
||||
<item>50,34,18,2,-14,-6,2,-2,-6,-26,-46,-26,-6,-2,2,2,2,2,2,2</item>
|
||||
<item>40,24,8,8,8,-4,-16,-12,-8,-32,-56,-24,8,8,8,8,8,8,8,8</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dolby_dialogue_entries">
|
||||
<item>@string/dolby_off</item>
|
||||
<item>@string/dolby_low</item>
|
||||
<item>@string/dolby_medium</item>
|
||||
<item>@string/dolby_high</item>
|
||||
<item>@string/dolby_max</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dolby_dialogue_values">
|
||||
<item>0</item>
|
||||
<item>2</item>
|
||||
<item>6</item>
|
||||
<item>9</item>
|
||||
<item>12</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dolby_stereo_entries">
|
||||
<item>@string/dolby_low</item>
|
||||
<item>@string/dolby_medium</item>
|
||||
<item>@string/dolby_high</item>
|
||||
<item>@string/dolby_max</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dolby_stereo_values">
|
||||
<item>4</item>
|
||||
<item>24</item>
|
||||
<item>44</item>
|
||||
<item>64</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dolby_ieq_entries">
|
||||
<item>@string/dolby_off</item>
|
||||
<item>@string/dolby_balanced</item>
|
||||
<item>@string/dolby_warm</item>
|
||||
<item>@string/dolby_detailed</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dolby_ieq_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
70
dolby/res/values/strings.xml
Normal file
70
dolby/res/values/strings.xml
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023-24 Paranoid Android
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Dolby Atmos -->
|
||||
<string name="dolby_title">Dolby Atmos</string>
|
||||
<string name="dolby_enable">Use Dolby Atmos</string>
|
||||
<string name="dolby_profile_title">Choose a profile</string>
|
||||
<string name="dolby_preset">Graphic equalizer</string>
|
||||
<string name="dolby_off">Off</string>
|
||||
<string name="dolby_on">On</string>
|
||||
<string name="dolby_low">Low</string>
|
||||
<string name="dolby_medium">Medium</string>
|
||||
<string name="dolby_high">High</string>
|
||||
<string name="dolby_max">Max</string>
|
||||
<string name="dolby_unknown">Unknown</string>
|
||||
<string name="dolby_on_with_profile">On (%1$s)</string>
|
||||
<string name="dolby_category_settings">Settings</string>
|
||||
<string name="dolby_bass_enhancer">Bass enhancer</string>
|
||||
<string name="dolby_dialogue_enhancer">Dialogue enhancer</string>
|
||||
<string name="dolby_spk_virtualizer">Speaker virtualization</string>
|
||||
<string name="dolby_hp_virtualizer">Headphone virtualization</string>
|
||||
<string name="dolby_stereo_widening">Stereo widening</string>
|
||||
<string name="dolby_volume_leveler">Volume leveler</string>
|
||||
<string name="dolby_connect_headphones">Connect headphones</string>
|
||||
<string name="dolby_reset_profile">Reset to defaults</string>
|
||||
<string name="dolby_reset_profile_toast">Successfully reset settings for %1$s profile</string>
|
||||
|
||||
<!-- Dolby profiles -->
|
||||
<string name="dolby_profile_dynamic">Dynamic</string>
|
||||
<string name="dolby_profile_movie">Movie</string>
|
||||
<string name="dolby_profile_music">Music</string>
|
||||
<string name="dolby_profile_custom">Custom</string>
|
||||
|
||||
<!-- Dolby equalizer presets -->
|
||||
<string name="dolby_preset_default">Flat (off)</string>
|
||||
<string name="dolby_preset_rock">Rock</string>
|
||||
<string name="dolby_preset_jazz">Jazz</string>
|
||||
<string name="dolby_preset_pop">Pop</string>
|
||||
<string name="dolby_preset_classical">Classical</string>
|
||||
<string name="dolby_preset_hiphop">Hip Hop</string>
|
||||
<string name="dolby_preset_blues">Blues</string>
|
||||
<string name="dolby_preset_electronic">Electronic</string>
|
||||
<string name="dolby_preset_country">Country</string>
|
||||
<string name="dolby_preset_dance">Dance</string>
|
||||
<string name="dolby_preset_metal">Metal</string>
|
||||
|
||||
<!-- Dolby equalizer UI -->
|
||||
<string name="dolby_geq_slider_label_gain">Gain</string>
|
||||
<string name="dolby_geq_preset">Preset</string>
|
||||
<string name="dolby_geq_preset_name">Preset name</string>
|
||||
<string name="dolby_geq_new_preset">New preset</string>
|
||||
<string name="dolby_geq_rename_preset">Rename preset</string>
|
||||
<string name="dolby_geq_delete_preset">Delete preset</string>
|
||||
<string name="dolby_geq_delete_preset_prompt">Do you want to delete this preset?</string>
|
||||
<string name="dolby_geq_reset_gains">Reset gains</string>
|
||||
<string name="dolby_geq_reset_gains_prompt">Do you want to reset this preset to defaults?</string>
|
||||
<string name="dolby_geq_preset_name_exists">Preset name already exists!</string>
|
||||
<string name="dolby_geq_preset_name_too_long">Preset name is too long!</string>
|
||||
|
||||
<!-- Dolby intelligent EQ -->
|
||||
<string name="dolby_ieq">Intelligent equalizer</string>
|
||||
<string name="dolby_balanced">Balanced</string>
|
||||
<string name="dolby_warm">Warm</string>
|
||||
<string name="dolby_detailed">Detailed</string>
|
||||
|
||||
</resources>
|
||||
76
dolby/res/xml/dolby_settings.xml
Normal file
76
dolby/res/xml/dolby_settings.xml
Normal file
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023-24 Paranoid Android
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:title="@string/dolby_title">
|
||||
|
||||
<com.android.settingslib.widget.MainSwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="dolby_enable"
|
||||
android:title="@string/dolby_enable" />
|
||||
|
||||
<ListPreference
|
||||
android:key="dolby_profile"
|
||||
android:entries="@array/dolby_profile_entries"
|
||||
android:entryValues="@array/dolby_profile_values"
|
||||
android:defaultValue="0"
|
||||
android:title="@string/dolby_profile_title"
|
||||
android:summary="%s" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/dolby_category_settings">
|
||||
|
||||
<Preference
|
||||
android:key="dolby_preset"
|
||||
android:title="@string/dolby_preset">
|
||||
<intent
|
||||
android:action="android.intent.action.MAIN"
|
||||
android:targetPackage="co.aospa.dolby.xiaomi"
|
||||
android:targetClass="co.aospa.dolby.xiaomi.geq.EqualizerActivity" />
|
||||
</Preference>
|
||||
|
||||
<co.aospa.dolby.xiaomi.preference.DolbyIeqPreference
|
||||
android:key="dolby_ieq"
|
||||
android:entries="@array/dolby_ieq_entries"
|
||||
android:entryValues="@array/dolby_ieq_values"
|
||||
android:title="@string/dolby_ieq" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="dolby_spk_virtualizer"
|
||||
android:title="@string/dolby_spk_virtualizer" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="dolby_virtualizer"
|
||||
android:title="@string/dolby_hp_virtualizer" />
|
||||
|
||||
<ListPreference
|
||||
android:key="dolby_stereo"
|
||||
android:entries="@array/dolby_stereo_entries"
|
||||
android:entryValues="@array/dolby_stereo_values"
|
||||
android:title="@string/dolby_stereo_widening"
|
||||
android:dependency="dolby_virtualizer" />
|
||||
|
||||
<ListPreference
|
||||
android:key="dolby_dialogue"
|
||||
android:entries="@array/dolby_dialogue_entries"
|
||||
android:entryValues="@array/dolby_dialogue_values"
|
||||
android:title="@string/dolby_dialogue_enhancer" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="dolby_bass"
|
||||
android:title="@string/dolby_bass_enhancer" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="dolby_volume"
|
||||
android:title="@string/dolby_volume_leveler" />
|
||||
|
||||
<Preference
|
||||
android:key="dolby_reset"
|
||||
android:title="@string/dolby_reset_profile" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
27
dolby/src/co/aospa/dolby/xiaomi/BootCompletedReceiver.kt
Normal file
27
dolby/src/co/aospa/dolby/xiaomi/BootCompletedReceiver.kt
Normal 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()
|
||||
}
|
||||
}
|
||||
25
dolby/src/co/aospa/dolby/xiaomi/DolbyActivity.kt
Normal file
25
dolby/src/co/aospa/dolby/xiaomi/DolbyActivity.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023-24 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi
|
||||
|
||||
import android.os.Bundle
|
||||
import co.aospa.dolby.xiaomi.preference.DolbySettingsFragment
|
||||
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity
|
||||
|
||||
class DolbyActivity : CollapsingToolbarBaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
com.android.settingslib.collapsingtoolbar.R.id.content_frame,
|
||||
DolbySettingsFragment(),
|
||||
)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
136
dolby/src/co/aospa/dolby/xiaomi/DolbyAudioEffect.kt
Normal file
136
dolby/src/co/aospa/dolby/xiaomi/DolbyAudioEffect.kt
Normal 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(profile: Int = this.profile) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
60
dolby/src/co/aospa/dolby/xiaomi/DolbyConstants.kt
Normal file
60
dolby/src/co/aospa/dolby/xiaomi/DolbyConstants.kt
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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),
|
||||
IEQ_PRESET(104),
|
||||
DIALOGUE_ENHANCER_ENABLE(105),
|
||||
DIALOGUE_ENHANCER_AMOUNT(108),
|
||||
GEQ_BAND_GAINS(110, 20),
|
||||
BASS_ENHANCER_ENABLE(111),
|
||||
STEREO_WIDENING_AMOUNT(113);
|
||||
|
||||
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_IEQ = "dolby_ieq"
|
||||
const val PREF_HP_VIRTUALIZER = "dolby_virtualizer"
|
||||
const val PREF_SPK_VIRTUALIZER = "dolby_spk_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"
|
||||
|
||||
val PROFILE_SPECIFIC_PREFS = setOf(
|
||||
PREF_PRESET,
|
||||
PREF_IEQ,
|
||||
PREF_HP_VIRTUALIZER,
|
||||
PREF_SPK_VIRTUALIZER,
|
||||
PREF_STEREO,
|
||||
PREF_DIALOGUE,
|
||||
PREF_BASS,
|
||||
PREF_VOLUME
|
||||
)
|
||||
|
||||
fun dlog(tag: String, msg: String) {
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Log.d(tag, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
323
dolby/src/co/aospa/dolby/xiaomi/DolbyController.kt
Normal file
323
dolby/src/co/aospa/dolby/xiaomi/DolbyController.kt
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
|
||||
init {
|
||||
dlog(TAG, "initialized")
|
||||
}
|
||||
|
||||
fun onBootCompleted() {
|
||||
dlog(TAG, "onBootCompleted")
|
||||
|
||||
// Restore our main settings
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
dsOn = prefs.getBoolean(DolbyConstants.PREF_ENABLE, true)
|
||||
|
||||
context.resources.getStringArray(R.array.dolby_profile_values)
|
||||
.map { it.toInt() }
|
||||
.forEach { profile ->
|
||||
// Reset dolby first to prevent it from loading bad settings
|
||||
dolbyEffect.resetProfileSpecificSettings(profile)
|
||||
// Now restore our profile-specific settings
|
||||
restoreSettings(profile)
|
||||
}
|
||||
|
||||
// Finally restore the current profile.
|
||||
setCurrentProfile()
|
||||
}
|
||||
|
||||
private fun restoreSettings(profile: Int) {
|
||||
dlog(TAG, "restoreSettings(profile=$profile)")
|
||||
val prefs = context.getSharedPreferences("profile_$profile", Context.MODE_PRIVATE)
|
||||
setPreset(
|
||||
prefs.getString(DolbyConstants.PREF_PRESET, getPreset(profile))!!,
|
||||
profile
|
||||
)
|
||||
setIeqPreset(
|
||||
prefs.getString(
|
||||
DolbyConstants.PREF_IEQ,
|
||||
getIeqPreset(profile).toString()
|
||||
)!!.toInt(),
|
||||
profile
|
||||
)
|
||||
setHeadphoneVirtEnabled(
|
||||
prefs.getBoolean(DolbyConstants.PREF_HP_VIRTUALIZER, getHeadphoneVirtEnabled(profile)),
|
||||
profile
|
||||
)
|
||||
setSpeakerVirtEnabled(
|
||||
prefs.getBoolean(DolbyConstants.PREF_SPK_VIRTUALIZER, getSpeakerVirtEnabled(profile)),
|
||||
profile
|
||||
)
|
||||
setStereoWideningAmount(
|
||||
prefs.getString(
|
||||
DolbyConstants.PREF_STEREO,
|
||||
getStereoWideningAmount(profile).toString()
|
||||
)!!.toInt(),
|
||||
profile
|
||||
)
|
||||
setDialogueEnhancerAmount(
|
||||
prefs.getString(
|
||||
DolbyConstants.PREF_DIALOGUE,
|
||||
getDialogueEnhancerAmount(profile).toString()
|
||||
)!!.toInt(),
|
||||
profile
|
||||
)
|
||||
setBassEnhancerEnabled(
|
||||
prefs.getBoolean(DolbyConstants.PREF_BASS, getBassEnhancerEnabled(profile)),
|
||||
profile
|
||||
)
|
||||
setVolumeLevelerEnabled(
|
||||
prefs.getBoolean(DolbyConstants.PREF_VOLUME, getVolumeLevelerEnabled(profile)),
|
||||
profile
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkEffect() {
|
||||
if (!dolbyEffect.hasControl()) {
|
||||
Log.w(TAG, "lost control, recreating effect")
|
||||
dolbyEffect.release()
|
||||
dolbyEffect = DolbyAudioEffect(EFFECT_PRIORITY, audioSession = 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setCurrentProfile() {
|
||||
dlog(TAG, "setCurrentProfile")
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
profile = prefs.getString(DolbyConstants.PREF_PROFILE, "0" /*dynamic*/)!!.toInt()
|
||||
}
|
||||
|
||||
fun setDsOnAndPersist(dsOn: Boolean) {
|
||||
this.dsOn = dsOn
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||
.putBoolean(DolbyConstants.PREF_ENABLE, dsOn)
|
||||
.apply()
|
||||
}
|
||||
|
||||
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() {
|
||||
dlog(TAG, "resetProfileSpecificSettings")
|
||||
checkEffect()
|
||||
dolbyEffect.resetProfileSpecificSettings()
|
||||
context.deleteSharedPreferences("profile_$profile")
|
||||
}
|
||||
|
||||
fun getPreset(profile: Int = this.profile): String {
|
||||
val gains = dolbyEffect.getDapParameter(DsParam.GEQ_BAND_GAINS, profile)
|
||||
return gains.joinToString(separator = ",").also {
|
||||
dlog(TAG, "getPreset: $it")
|
||||
}
|
||||
}
|
||||
|
||||
fun setPreset(value: String, profile: Int = this.profile) {
|
||||
dlog(TAG, "setPreset: $value")
|
||||
checkEffect()
|
||||
val gains = value.split(",")
|
||||
.map { it.toInt() }
|
||||
.toIntArray()
|
||||
dolbyEffect.setDapParameter(DsParam.GEQ_BAND_GAINS, gains, profile)
|
||||
}
|
||||
|
||||
fun getPresetName(): String {
|
||||
val presets = context.resources.getStringArray(R.array.dolby_preset_values)
|
||||
val presetIndex = presets.indexOf(getPreset())
|
||||
return if (presetIndex == -1) {
|
||||
"Custom"
|
||||
} else {
|
||||
context.resources.getStringArray(
|
||||
R.array.dolby_preset_entries
|
||||
)[presetIndex]
|
||||
}
|
||||
}
|
||||
|
||||
fun getHeadphoneVirtEnabled(profile: Int = this.profile) =
|
||||
dolbyEffect.getDapParameterBool(DsParam.HEADPHONE_VIRTUALIZER, profile).also {
|
||||
dlog(TAG, "getHeadphoneVirtEnabled: $it")
|
||||
}
|
||||
|
||||
fun setHeadphoneVirtEnabled(value: Boolean, profile: Int = this.profile) {
|
||||
dlog(TAG, "setHeadphoneVirtEnabled: $value")
|
||||
checkEffect()
|
||||
dolbyEffect.setDapParameter(DsParam.HEADPHONE_VIRTUALIZER, value, profile)
|
||||
}
|
||||
|
||||
fun getSpeakerVirtEnabled(profile: Int = this.profile) =
|
||||
dolbyEffect.getDapParameterBool(DsParam.SPEAKER_VIRTUALIZER, profile).also {
|
||||
dlog(TAG, "getSpeakerVirtEnabled: $it")
|
||||
}
|
||||
|
||||
fun setSpeakerVirtEnabled(value: Boolean, profile: Int = this.profile) {
|
||||
dlog(TAG, "setSpeakerVirtEnabled: $value")
|
||||
checkEffect()
|
||||
dolbyEffect.setDapParameter(DsParam.SPEAKER_VIRTUALIZER, value, profile)
|
||||
}
|
||||
|
||||
fun getBassEnhancerEnabled(profile: Int = this.profile) =
|
||||
dolbyEffect.getDapParameterBool(DsParam.BASS_ENHANCER_ENABLE, profile).also {
|
||||
dlog(TAG, "getBassEnhancerEnabled: $it")
|
||||
}
|
||||
|
||||
fun setBassEnhancerEnabled(value: Boolean, profile: Int = this.profile) {
|
||||
dlog(TAG, "setBassEnhancerEnabled: $value")
|
||||
checkEffect()
|
||||
dolbyEffect.setDapParameter(DsParam.BASS_ENHANCER_ENABLE, value, profile)
|
||||
}
|
||||
|
||||
fun getVolumeLevelerEnabled(profile: Int = this.profile) =
|
||||
dolbyEffect.getDapParameterBool(DsParam.VOLUME_LEVELER_ENABLE, profile).also {
|
||||
dlog(TAG, "getVolumeLevelerEnabled: $it")
|
||||
}
|
||||
|
||||
fun setVolumeLevelerEnabled(value: Boolean, profile: Int = this.profile) {
|
||||
dlog(TAG, "setVolumeLevelerEnabled: $value")
|
||||
checkEffect()
|
||||
dolbyEffect.setDapParameter(DsParam.VOLUME_LEVELER_ENABLE, value, profile)
|
||||
}
|
||||
|
||||
fun getStereoWideningAmount(profile: Int = this.profile) =
|
||||
dolbyEffect.getDapParameterInt(DsParam.STEREO_WIDENING_AMOUNT, profile).also {
|
||||
dlog(TAG, "getStereoWideningAmount: $it")
|
||||
}
|
||||
|
||||
fun setStereoWideningAmount(value: Int, profile: Int = this.profile) {
|
||||
dlog(TAG, "setStereoWideningAmount: $value")
|
||||
checkEffect()
|
||||
dolbyEffect.setDapParameter(DsParam.STEREO_WIDENING_AMOUNT, value, profile)
|
||||
}
|
||||
|
||||
fun getDialogueEnhancerAmount(profile: Int = this.profile): Int {
|
||||
val enabled = dolbyEffect.getDapParameterBool(DsParam.DIALOGUE_ENHANCER_ENABLE, profile)
|
||||
val amount = if (enabled) {
|
||||
dolbyEffect.getDapParameterInt(DsParam.DIALOGUE_ENHANCER_AMOUNT, profile)
|
||||
} else 0
|
||||
dlog(TAG, "getDialogueEnhancerAmount: enabled=$enabled amount=$amount")
|
||||
return amount
|
||||
}
|
||||
|
||||
fun setDialogueEnhancerAmount(value: Int, profile: Int = this.profile) {
|
||||
dlog(TAG, "setDialogueEnhancerAmount: $value")
|
||||
checkEffect()
|
||||
dolbyEffect.setDapParameter(DsParam.DIALOGUE_ENHANCER_ENABLE, (value > 0), profile)
|
||||
dolbyEffect.setDapParameter(DsParam.DIALOGUE_ENHANCER_AMOUNT, value, profile)
|
||||
}
|
||||
|
||||
fun getIeqPreset(profile: Int = this.profile) =
|
||||
dolbyEffect.getDapParameterInt(DsParam.IEQ_PRESET, profile).also {
|
||||
dlog(TAG, "getIeqPreset: $it")
|
||||
}
|
||||
|
||||
fun setIeqPreset(value: Int, profile: Int = this.profile) {
|
||||
dlog(TAG, "setIeqPreset: $value")
|
||||
checkEffect()
|
||||
dolbyEffect.setDapParameter(DsParam.IEQ_PRESET, value, profile)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "DolbyController"
|
||||
private const val EFFECT_PRIORITY = 100
|
||||
|
||||
@Volatile
|
||||
private var instance: DolbyController? = null
|
||||
|
||||
fun getInstance(context: Context) =
|
||||
instance ?: synchronized(this) {
|
||||
instance ?: DolbyController(context).also { instance = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
36
dolby/src/co/aospa/dolby/xiaomi/DolbyTileService.kt
Normal file
36
dolby/src/co/aospa/dolby/xiaomi/DolbyTileService.kt
Normal 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.setDsOnAndPersist(!isDsOn) // toggle
|
||||
qsTile.apply {
|
||||
state = if (isDsOn) Tile.STATE_INACTIVE else Tile.STATE_ACTIVE
|
||||
updateTile()
|
||||
}
|
||||
super.onClick()
|
||||
}
|
||||
}
|
||||
71
dolby/src/co/aospa/dolby/xiaomi/SummaryProvider.kt
Normal file
71
dolby/src/co/aospa/dolby/xiaomi/SummaryProvider.kt
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 context = context!!
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
53
dolby/src/co/aospa/dolby/xiaomi/geq/EqualizerActivity.kt
Normal file
53
dolby/src/co/aospa/dolby/xiaomi/geq/EqualizerActivity.kt
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import co.aospa.dolby.xiaomi.R
|
||||
import co.aospa.dolby.xiaomi.geq.ui.EqualizerScreen
|
||||
import co.aospa.dolby.xiaomi.geq.ui.EqualizerViewModel
|
||||
import com.android.settingslib.spa.framework.compose.localNavController
|
||||
import com.android.settingslib.spa.framework.theme.SettingsTheme
|
||||
import com.android.settingslib.spa.widget.scaffold.SettingsScaffold
|
||||
|
||||
class EqualizerActivity : ComponentActivity() {
|
||||
|
||||
private val viewModel: EqualizerViewModel by viewModels { EqualizerViewModel.Factory }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
SettingsTheme {
|
||||
MainContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MainContent() {
|
||||
val navController = rememberNavController()
|
||||
CompositionLocalProvider(navController.localNavController()) {
|
||||
SettingsScaffold(
|
||||
title = stringResource(id = R.string.dolby_preset)
|
||||
) { paddingValues ->
|
||||
EqualizerScreen(
|
||||
viewModel = viewModel,
|
||||
modifier = Modifier.padding(paddingValues)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
dolby/src/co/aospa/dolby/xiaomi/geq/data/BandGain.kt
Normal file
12
dolby/src/co/aospa/dolby/xiaomi/geq/data/BandGain.kt
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.data
|
||||
|
||||
data class BandGain(
|
||||
val band: Int,
|
||||
var gain: Int = 0
|
||||
)
|
||||
183
dolby/src/co/aospa/dolby/xiaomi/geq/data/EqualizerRepository.kt
Normal file
183
dolby/src/co/aospa/dolby/xiaomi/geq/data/EqualizerRepository.kt
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.data
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_PRESET
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.dlog
|
||||
import co.aospa.dolby.xiaomi.DolbyController
|
||||
import co.aospa.dolby.xiaomi.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class EqualizerRepository(
|
||||
private val context: Context
|
||||
) {
|
||||
|
||||
private val dolbyController by lazy { DolbyController.getInstance(context) }
|
||||
|
||||
// Preset is saved as a string of comma separated gains in SharedPreferences
|
||||
// and is unique to each profile ID
|
||||
private val profile = dolbyController.profile
|
||||
private val profileSharedPrefs by lazy {
|
||||
context.getSharedPreferences(
|
||||
"profile_$profile",
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
}
|
||||
|
||||
private val presetsSharedPrefs by lazy {
|
||||
context.getSharedPreferences(
|
||||
"presets",
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
}
|
||||
|
||||
val builtInPresets: List<Preset> by lazy {
|
||||
val names = context.resources.getStringArray(
|
||||
R.array.dolby_preset_entries
|
||||
)
|
||||
val presets = context.resources.getStringArray(
|
||||
R.array.dolby_preset_values
|
||||
)
|
||||
List(names.size) { index ->
|
||||
Preset(
|
||||
name = names[index],
|
||||
bandGains = deserializeGains(presets[index]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val defaultPreset by lazy { builtInPresets[0] } // Flat
|
||||
|
||||
// User defined presets are stored in a SharedPreferences as
|
||||
// key - preset name
|
||||
// value - comma separated string of gains
|
||||
val userPresets: Flow<List<Preset>> = callbackFlow {
|
||||
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
|
||||
dlog(TAG, "presetsSharedPrefs changed")
|
||||
trySend(
|
||||
presetsSharedPrefs.all.map { (key, value) ->
|
||||
Preset(
|
||||
name = key,
|
||||
bandGains = deserializeGains(value.toString()),
|
||||
isUserDefined = true
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
presetsSharedPrefs.registerOnSharedPreferenceChangeListener(listener)
|
||||
dlog(TAG, "presetsSharedPrefs registered listener")
|
||||
// trigger an initial emission
|
||||
listener.onSharedPreferenceChanged(presetsSharedPrefs, null)
|
||||
|
||||
awaitClose {
|
||||
presetsSharedPrefs.unregisterOnSharedPreferenceChangeListener(listener)
|
||||
dlog(TAG, "presetsSharedPrefs unregistered listener")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getBandGains(): List<BandGain> = withContext(Dispatchers.IO) {
|
||||
val gains = profileSharedPrefs.getString(PREF_PRESET, dolbyController.getPreset())
|
||||
return@withContext if (gains.isNullOrEmpty()) {
|
||||
defaultPreset.bandGains
|
||||
} else {
|
||||
deserializeGains(gains)
|
||||
}.also {
|
||||
dlog(TAG, "getBandGains: $it")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setBandGains(bandGains: List<BandGain>) = withContext(Dispatchers.IO) {
|
||||
dlog(TAG, "setBandGains($bandGains)")
|
||||
val gains = serializeGains(bandGains)
|
||||
dolbyController.setPreset(gains)
|
||||
profileSharedPrefs.edit()
|
||||
.putString(PREF_PRESET, gains)
|
||||
.apply()
|
||||
}
|
||||
|
||||
suspend fun addPreset(preset: Preset) = withContext(Dispatchers.IO) {
|
||||
dlog(TAG, "addPreset($preset)")
|
||||
presetsSharedPrefs.edit()
|
||||
.putString(preset.name, serializeGains(preset.bandGains))
|
||||
.apply()
|
||||
}
|
||||
|
||||
suspend fun removePreset(preset: Preset) = withContext(Dispatchers.IO) {
|
||||
dlog(TAG, "removePreset($preset)")
|
||||
presetsSharedPrefs.edit()
|
||||
.remove(preset.name)
|
||||
.apply()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val TAG = "EqRepository"
|
||||
|
||||
val tenBandFreqs = intArrayOf(
|
||||
32,
|
||||
64,
|
||||
125,
|
||||
250,
|
||||
500,
|
||||
1000,
|
||||
2000,
|
||||
4000,
|
||||
8000,
|
||||
16000
|
||||
)
|
||||
|
||||
fun deserializeGains(bandGains: String): List<BandGain> {
|
||||
val gains: List<Int> =
|
||||
bandGains.split(",").runCatching {
|
||||
require(size == 20) {
|
||||
"Preset must have 20 elements, has only $size!"
|
||||
}
|
||||
map { it.toInt() }
|
||||
.twentyToTenBandGains()
|
||||
}.onFailure { exception ->
|
||||
Log.e(TAG, "Failed to parse preset", exception)
|
||||
}.getOrDefault(
|
||||
// fallback to flat
|
||||
List<Int>(10) { 0 }
|
||||
)
|
||||
return List(10) { index ->
|
||||
BandGain(
|
||||
band = tenBandFreqs[index],
|
||||
gain = gains[index]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun serializeGains(bandGains: List<BandGain>): String {
|
||||
return bandGains.map { it.gain }
|
||||
.tenToTwentyBandGains()
|
||||
.joinToString(",")
|
||||
}
|
||||
|
||||
// we show only 10 bands in UI however backend requires 20 bands
|
||||
fun List<Int>.tenToTwentyBandGains() =
|
||||
List<Int>(20) { index ->
|
||||
if (index % 2 == 1 && index < 19) {
|
||||
// every odd element is the average of its surrounding elements
|
||||
(this[(index - 1) / 2] + this[(index + 1) / 2]) / 2
|
||||
} else {
|
||||
this[index / 2]
|
||||
}
|
||||
}
|
||||
|
||||
fun List<Int>.twentyToTenBandGains() =
|
||||
// skip every odd element
|
||||
filterIndexed { index, _ -> index % 2 == 0 }
|
||||
}
|
||||
}
|
||||
14
dolby/src/co/aospa/dolby/xiaomi/geq/data/Preset.kt
Normal file
14
dolby/src/co/aospa/dolby/xiaomi/geq/data/Preset.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.data
|
||||
|
||||
data class Preset(
|
||||
var name: String,
|
||||
val bandGains: List<BandGain>,
|
||||
var isUserDefined: Boolean = false,
|
||||
var isMutated: Boolean = false
|
||||
)
|
||||
101
dolby/src/co/aospa/dolby/xiaomi/geq/ui/BandGainSlider.kt
Normal file
101
dolby/src/co/aospa/dolby/xiaomi/geq/ui/BandGainSlider.kt
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.layout
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import co.aospa.dolby.xiaomi.geq.data.BandGain
|
||||
|
||||
@Composable
|
||||
fun BandGainSlider(
|
||||
bandGain: BandGain,
|
||||
onValueChangeFinished: (Int) -> Unit
|
||||
) {
|
||||
// Gain range is of -1->1 in UI, -100->100 in backend, but actually is -10->10 dB.
|
||||
|
||||
// Ensure we update the slider when gain is changed,
|
||||
// for eg. when changing the preset
|
||||
var sliderPosition by remember(bandGain.gain) {
|
||||
mutableFloatStateOf(bandGain.gain / 100f)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
SliderText(
|
||||
"%.1f".format(sliderPosition * 10f)
|
||||
)
|
||||
Slider(
|
||||
value = sliderPosition,
|
||||
onValueChange = { sliderPosition = it },
|
||||
onValueChangeFinished = {
|
||||
onValueChangeFinished((sliderPosition * 100f).toInt())
|
||||
},
|
||||
valueRange = -1f..1f,
|
||||
modifier = Modifier
|
||||
.graphicsLayer {
|
||||
rotationZ = 270f
|
||||
transformOrigin = TransformOrigin(0f, 0f)
|
||||
}
|
||||
.layout { measurable, constraints ->
|
||||
val placeable = measurable.measure(
|
||||
Constraints(
|
||||
minWidth = constraints.minHeight,
|
||||
maxWidth = constraints.maxHeight,
|
||||
minHeight = constraints.minWidth,
|
||||
maxHeight = constraints.maxHeight,
|
||||
)
|
||||
)
|
||||
layout(placeable.height, placeable.width) {
|
||||
placeable.place(-placeable.width, 0)
|
||||
}
|
||||
}
|
||||
// horizontal and vertical dimensions are inverted due to rotation
|
||||
.width(200.dp)
|
||||
.height(40.dp)
|
||||
.padding(8.dp)
|
||||
)
|
||||
SliderText(
|
||||
with(bandGain.band) {
|
||||
if (this >= 1000) {
|
||||
"${this / 1000}k"
|
||||
} else {
|
||||
"$this"
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SliderText(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
modifier = modifier,
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import co.aospa.dolby.xiaomi.R
|
||||
|
||||
@Composable
|
||||
fun BandGainSliderLabels() {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.End,
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
) {
|
||||
LabelText(
|
||||
stringResource(id = R.string.dolby_geq_slider_label_gain)
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.height(200.dp),
|
||||
horizontalAlignment = Alignment.End
|
||||
) {
|
||||
LabelText(
|
||||
"+10 dB",
|
||||
modifier = Modifier.padding(
|
||||
top = 10.dp
|
||||
)
|
||||
)
|
||||
Spacer(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
LabelText("0 dB")
|
||||
Spacer(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
LabelText(
|
||||
"-10 dB",
|
||||
modifier = Modifier.padding(
|
||||
bottom = 10.dp
|
||||
)
|
||||
)
|
||||
}
|
||||
LabelText("Hz")
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LabelText(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
modifier = modifier,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
58
dolby/src/co/aospa/dolby/xiaomi/geq/ui/ConfirmationDialog.kt
Normal file
58
dolby/src/co/aospa/dolby/xiaomi/geq/ui/ConfirmationDialog.kt
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.ui
|
||||
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
||||
@Composable
|
||||
fun ConfirmationDialog(
|
||||
text: String,
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
var showDialog by remember { mutableStateOf(true) }
|
||||
if (!showDialog) {
|
||||
onDismiss()
|
||||
return
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDialog = false },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
showDialog = false
|
||||
onConfirm()
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
stringResource(id = android.R.string.ok)
|
||||
)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { showDialog = false }
|
||||
) {
|
||||
Text(
|
||||
stringResource(id = android.R.string.cancel)
|
||||
)
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Text(text)
|
||||
}
|
||||
)
|
||||
}
|
||||
39
dolby/src/co/aospa/dolby/xiaomi/geq/ui/EqualizerBands.kt
Normal file
39
dolby/src/co/aospa/dolby/xiaomi/geq/ui/EqualizerBands.kt
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun EqualizerBands(viewModel: EqualizerViewModel) {
|
||||
val preset by viewModel.preset.collectAsState()
|
||||
val bandGains = preset.bandGains
|
||||
|
||||
LazyRow(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
item {
|
||||
BandGainSliderLabels()
|
||||
}
|
||||
items(bandGains.size) { index ->
|
||||
BandGainSlider(
|
||||
bandGains[index],
|
||||
onValueChangeFinished = {
|
||||
viewModel.setGain(index, it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
42
dolby/src/co/aospa/dolby/xiaomi/geq/ui/EqualizerScreen.kt
Normal file
42
dolby/src/co/aospa/dolby/xiaomi/geq/ui/EqualizerScreen.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.android.settingslib.spa.framework.theme.settingsBackground
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
|
||||
@Composable
|
||||
fun EqualizerScreen(
|
||||
viewModel: EqualizerViewModel,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(SettingsDimension.itemPadding)
|
||||
.then(modifier),
|
||||
color = MaterialTheme.colorScheme.settingsBackground
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Top,
|
||||
modifier = Modifier.fillMaxHeight()
|
||||
) {
|
||||
PresetSelector(viewModel = viewModel)
|
||||
EqualizerBands(viewModel = viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
175
dolby/src/co/aospa/dolby/xiaomi/geq/ui/EqualizerViewModel.kt
Normal file
175
dolby/src/co/aospa/dolby/xiaomi/geq/ui/EqualizerViewModel.kt
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import co.aospa.dolby.xiaomi.geq.data.EqualizerRepository
|
||||
import co.aospa.dolby.xiaomi.geq.data.Preset
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.dlog
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
const val TAG = "EqViewModel"
|
||||
|
||||
class EqualizerViewModel(
|
||||
private val repository: EqualizerRepository
|
||||
) : ViewModel() {
|
||||
|
||||
private val _presets = MutableStateFlow(repository.builtInPresets)
|
||||
val presets = _presets.asStateFlow()
|
||||
|
||||
private val _preset = MutableStateFlow(repository.defaultPreset)
|
||||
val preset = _preset.asStateFlow()
|
||||
|
||||
private var presetRestored = false
|
||||
|
||||
init {
|
||||
// Update the list of presets: combined list of user defined presets if any,
|
||||
// and then the built in presets.
|
||||
repository.userPresets
|
||||
.onEach { presets ->
|
||||
dlog(TAG, "updated userPresets: $presets")
|
||||
_presets.value = mutableListOf<Preset>().apply {
|
||||
addAll(presets)
|
||||
addAll(repository.builtInPresets)
|
||||
}.toList()
|
||||
|
||||
// We can restore the active preset only after the presets list is populated,
|
||||
// since we do not save the preset name but only its gains.
|
||||
if (!presetRestored) {
|
||||
val bandGains = repository.getBandGains()
|
||||
_preset.value = _presets.value.find {
|
||||
bandGains == it.bandGains
|
||||
} ?: Preset(
|
||||
name = "Custom",
|
||||
bandGains = bandGains
|
||||
)
|
||||
dlog(TAG, "restored preset: ${_preset.value}")
|
||||
presetRestored = true
|
||||
}
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
// Update the preset in repository everytime we set it here
|
||||
_preset
|
||||
.drop(1) // skip the initial value
|
||||
.onEach {
|
||||
// wait till the active preset is restored
|
||||
if (!presetRestored) {
|
||||
return@onEach
|
||||
}
|
||||
dlog(TAG, "updated preset: $it")
|
||||
repository.setBandGains(it.bandGains)
|
||||
if (it.isUserDefined) {
|
||||
repository.addPreset(it)
|
||||
}
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
dlog(TAG, "reset()")
|
||||
if (_preset.value.isUserDefined) {
|
||||
// Reset gains to 0
|
||||
_preset.value = _preset.value.copy(
|
||||
bandGains = repository.defaultPreset.bandGains
|
||||
)
|
||||
} else {
|
||||
// Switch to flat preset
|
||||
_preset.value = repository.defaultPreset
|
||||
}
|
||||
}
|
||||
|
||||
fun setPreset(preset: Preset) {
|
||||
dlog(TAG, "setPreset($preset)")
|
||||
_preset.value = preset
|
||||
}
|
||||
|
||||
fun setGain(index: Int, gain: Int) {
|
||||
dlog(TAG, "setGain($index, $gain)")
|
||||
_preset.value = _preset.value.run {
|
||||
copy(
|
||||
name = if (!isUserDefined) "Custom" else name,
|
||||
bandGains = bandGains
|
||||
.toMutableList()
|
||||
// create a new object to ensure the flow emits an update.
|
||||
.apply { this[index] = this[index].copy(gain = gain) }
|
||||
.toList(),
|
||||
isMutated = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns string containing the error message if it failed, otherwise null
|
||||
private fun validatePresetName(name: String): PresetNameValidationError? {
|
||||
// Ensure we don't have another preset with the same name
|
||||
return if (
|
||||
_presets.value
|
||||
.any { it.name.equals(name.trim(), ignoreCase = true) }
|
||||
) {
|
||||
PresetNameValidationError.NAME_EXISTS
|
||||
} else if (name.length > 50) {
|
||||
PresetNameValidationError.NAME_TOO_LONG
|
||||
} else null
|
||||
}
|
||||
|
||||
fun createNewPreset(name: String): PresetNameValidationError? {
|
||||
dlog(TAG, "createNewPreset($name)")
|
||||
validatePresetName(name)?.let {
|
||||
dlog(TAG, "createNewPreset failed: $it")
|
||||
return it
|
||||
}
|
||||
_preset.value = _preset.value.copy(
|
||||
name = name.trim(),
|
||||
isUserDefined = true,
|
||||
isMutated = false
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
fun renamePreset(preset: Preset, name: String): PresetNameValidationError? {
|
||||
dlog(TAG, "renamePreset($preset, $name)")
|
||||
// create a preset with the new name and same gains
|
||||
createNewPreset(name = name)?.let {
|
||||
dlog(TAG, "renamePreset failed")
|
||||
return it
|
||||
}
|
||||
// and delete the old one.
|
||||
deletePreset(preset, shouldReset = false)
|
||||
return null
|
||||
}
|
||||
|
||||
fun deletePreset(preset: Preset, shouldReset: Boolean = true) {
|
||||
dlog(TAG, "deletePreset($preset)")
|
||||
viewModelScope.launch {
|
||||
repository.removePreset(preset)
|
||||
}
|
||||
if (shouldReset) {
|
||||
_preset.value = repository.defaultPreset
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Factory = viewModelFactory {
|
||||
initializer {
|
||||
EqualizerViewModel(
|
||||
repository = EqualizerRepository(
|
||||
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]!!
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
94
dolby/src/co/aospa/dolby/xiaomi/geq/ui/PresetNameDialog.kt
Normal file
94
dolby/src/co/aospa/dolby/xiaomi/geq/ui/PresetNameDialog.kt
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import co.aospa.dolby.xiaomi.R
|
||||
|
||||
@Composable
|
||||
fun PresetNameDialog(
|
||||
title: String,
|
||||
presetName: String = "",
|
||||
onPresetNameSet: (String) -> PresetNameValidationError?,
|
||||
onDismissDialog: () -> Unit
|
||||
) {
|
||||
var showDialog by remember { mutableStateOf(true) }
|
||||
if (!showDialog) {
|
||||
onDismissDialog()
|
||||
return
|
||||
}
|
||||
var text by remember { mutableStateOf(presetName) }
|
||||
var error by remember { mutableStateOf<PresetNameValidationError?>(null) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDialog = false },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onPresetNameSet(text)?.let {
|
||||
// validation failed
|
||||
error = it
|
||||
return@TextButton
|
||||
}
|
||||
// succeeded
|
||||
showDialog = false
|
||||
error = null
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
stringResource(id = android.R.string.ok)
|
||||
)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { showDialog = false }
|
||||
) {
|
||||
Text(
|
||||
stringResource(id = android.R.string.cancel)
|
||||
)
|
||||
}
|
||||
},
|
||||
title = { Text(title) },
|
||||
text = {
|
||||
Column {
|
||||
OutlinedTextField(
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
label = {
|
||||
Text(
|
||||
stringResource(id = R.string.dolby_geq_preset_name)
|
||||
)
|
||||
},
|
||||
isError = error != null,
|
||||
singleLine = true
|
||||
)
|
||||
error?.let {
|
||||
Text(
|
||||
text = it.toErrorMessage(),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.padding(top = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import co.aospa.dolby.xiaomi.R
|
||||
|
||||
enum class PresetNameValidationError {
|
||||
NAME_EXISTS,
|
||||
NAME_TOO_LONG;
|
||||
|
||||
@Composable
|
||||
fun toErrorMessage() =
|
||||
stringResource(
|
||||
id = when (this) {
|
||||
NAME_EXISTS -> R.string.dolby_geq_preset_name_exists
|
||||
NAME_TOO_LONG -> R.string.dolby_geq_preset_name_too_long
|
||||
}
|
||||
)
|
||||
}
|
||||
176
dolby/src/co/aospa/dolby/xiaomi/geq/ui/PresetSelector.kt
Normal file
176
dolby/src/co/aospa/dolby/xiaomi/geq/ui/PresetSelector.kt
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.focusProperties
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import co.aospa.dolby.xiaomi.R
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PresetSelector(viewModel: EqualizerViewModel) {
|
||||
val presets by viewModel.presets.collectAsState()
|
||||
val currentPreset by viewModel.preset.collectAsState()
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
var showNewPresetDialog by remember { mutableStateOf(false) }
|
||||
var showRenamePresetDialog by remember { mutableStateOf(false) }
|
||||
var showDeleteConfirmDialog by remember { mutableStateOf(false) }
|
||||
var showResetConfirmDialog by remember { mutableStateOf(false) }
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 24.dp),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = { expanded = !expanded },
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.weight(1f)
|
||||
) {
|
||||
TextField(
|
||||
value = currentPreset.name,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
label = {
|
||||
Text(
|
||||
stringResource(id = R.string.dolby_geq_preset)
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
trailingIcon = {
|
||||
ExposedDropdownMenuDefaults.TrailingIcon(
|
||||
expanded = expanded
|
||||
)
|
||||
},
|
||||
colors = ExposedDropdownMenuDefaults.textFieldColors(),
|
||||
modifier = Modifier.menuAnchor()
|
||||
// prevent keyboard from popping up
|
||||
.focusProperties { canFocus = false }
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false }
|
||||
) {
|
||||
presets.forEach { preset ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = preset.name) },
|
||||
onClick = {
|
||||
viewModel.setPreset(preset)
|
||||
expanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TooltipIconButton(
|
||||
icon = ImageVector.vectorResource(
|
||||
id = R.drawable.save_as_24px
|
||||
),
|
||||
text = stringResource(id = R.string.dolby_geq_new_preset),
|
||||
onClick = { showNewPresetDialog = true }
|
||||
)
|
||||
|
||||
if (currentPreset.isUserDefined) {
|
||||
TooltipIconButton(
|
||||
icon = Icons.Default.Edit,
|
||||
text = stringResource(id = R.string.dolby_geq_rename_preset),
|
||||
onClick = { showRenamePresetDialog = true }
|
||||
)
|
||||
TooltipIconButton(
|
||||
icon = Icons.Default.Delete,
|
||||
text = stringResource(id = R.string.dolby_geq_delete_preset),
|
||||
onClick = { showDeleteConfirmDialog = true }
|
||||
)
|
||||
}
|
||||
|
||||
TooltipIconButton(
|
||||
icon = ImageVector.vectorResource(
|
||||
id = R.drawable.reset_settings_24px
|
||||
),
|
||||
text = stringResource(id = R.string.dolby_geq_reset_gains),
|
||||
onClick = {
|
||||
if (currentPreset.isUserDefined) {
|
||||
showResetConfirmDialog = true
|
||||
} else {
|
||||
viewModel.reset()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Dialogs
|
||||
|
||||
if (showNewPresetDialog) {
|
||||
PresetNameDialog(
|
||||
title = stringResource(id = R.string.dolby_geq_new_preset),
|
||||
onPresetNameSet = {
|
||||
return@PresetNameDialog viewModel.createNewPreset(name = it)
|
||||
},
|
||||
onDismissDialog = { showNewPresetDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
if (showRenamePresetDialog) {
|
||||
PresetNameDialog(
|
||||
title = stringResource(id = R.string.dolby_geq_rename_preset),
|
||||
presetName = currentPreset.name,
|
||||
onPresetNameSet = {
|
||||
return@PresetNameDialog viewModel.renamePreset(
|
||||
preset = currentPreset,
|
||||
name = it
|
||||
)
|
||||
},
|
||||
onDismissDialog = { showRenamePresetDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
if (showDeleteConfirmDialog) {
|
||||
ConfirmationDialog(
|
||||
text = stringResource(id = R.string.dolby_geq_delete_preset_prompt),
|
||||
onConfirm = { viewModel.deletePreset(currentPreset) },
|
||||
onDismiss = { showDeleteConfirmDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
if (showResetConfirmDialog) {
|
||||
ConfirmationDialog(
|
||||
text = stringResource(id = R.string.dolby_geq_reset_gains_prompt),
|
||||
onConfirm = { viewModel.reset() },
|
||||
onDismiss = { showResetConfirmDialog = false }
|
||||
)
|
||||
}
|
||||
}
|
||||
46
dolby/src/co/aospa/dolby/xiaomi/geq/ui/TooltipIconButton.kt
Normal file
46
dolby/src/co/aospa/dolby/xiaomi/geq/ui/TooltipIconButton.kt
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.geq.ui
|
||||
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TooltipBox
|
||||
import androidx.compose.material3.TooltipDefaults
|
||||
import androidx.compose.material3.rememberTooltipState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TooltipIconButton(
|
||||
icon: ImageVector,
|
||||
text: String,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
TooltipBox(
|
||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
||||
tooltip = {
|
||||
Text(text)
|
||||
},
|
||||
state = rememberTooltipState()
|
||||
) {
|
||||
IconButton(
|
||||
onClick = onClick
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = text,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import co.aospa.dolby.xiaomi.R
|
||||
|
||||
// Preference with icon on the right side
|
||||
class DolbyIeqPreference(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
) : ListPreference(context, attrs) {
|
||||
|
||||
init {
|
||||
widgetLayoutResource = R.layout.ieq_icon_layout
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
super.onBindViewHolder(holder)
|
||||
val iconView = holder.findViewById(R.id.ieq_icon)!! as ImageView
|
||||
val icon = AppCompatResources.getDrawable(context, getIeqIconResId())
|
||||
iconView.setImageDrawable(icon)
|
||||
}
|
||||
|
||||
private fun getIeqIconResId(): Int {
|
||||
val ieqValue = value?.toIntOrNull() ?: 0
|
||||
return when (ieqValue) {
|
||||
0 -> R.drawable.ic_ieq_off
|
||||
1 -> R.drawable.ic_ieq_balanced
|
||||
2 -> R.drawable.ic_ieq_warm
|
||||
3 -> R.drawable.ic_ieq_detailed
|
||||
else -> 0 // should never hit this!
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceDataStore
|
||||
import androidx.preference.PreferenceManager
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants
|
||||
|
||||
class DolbyPreferenceStore(
|
||||
private val context: Context
|
||||
) : PreferenceDataStore() {
|
||||
|
||||
private val defaultSharedPrefs by lazy {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
}
|
||||
|
||||
private lateinit var profileSharedPrefs: SharedPreferences
|
||||
|
||||
var profile = 0
|
||||
set(value) {
|
||||
field = value
|
||||
profileSharedPrefs = context.getSharedPreferences(
|
||||
"profile_$value",
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
}
|
||||
|
||||
private fun getSharedPreferences(key: String) =
|
||||
if (DolbyConstants.PROFILE_SPECIFIC_PREFS.contains(key)) {
|
||||
profileSharedPrefs
|
||||
} else {
|
||||
defaultSharedPrefs
|
||||
}
|
||||
|
||||
override fun putBoolean(key: String, value: Boolean) =
|
||||
getSharedPreferences(key).edit()
|
||||
.putBoolean(key, value)
|
||||
.apply()
|
||||
|
||||
override fun getBoolean(key: String, defValue: Boolean) =
|
||||
getSharedPreferences(key).getBoolean(key, defValue)
|
||||
|
||||
override fun putInt(key: String, value: Int) =
|
||||
getSharedPreferences(key).edit()
|
||||
.putInt(key, value)
|
||||
.apply()
|
||||
|
||||
override fun getInt(key: String, defValue: Int) =
|
||||
getSharedPreferences(key).getInt(key, defValue)
|
||||
|
||||
override fun putString(key: String, value: String?) =
|
||||
getSharedPreferences(key).edit()
|
||||
.putString(key, value)
|
||||
.apply()
|
||||
|
||||
override fun getString(key: String, defValue: String?) =
|
||||
getSharedPreferences(key).getString(key, defValue)
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (C) 2023-24 Paranoid Android
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package co.aospa.dolby.xiaomi.preference
|
||||
|
||||
import android.content.Context
|
||||
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.os.Looper
|
||||
import android.view.View
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.Toast
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_BASS
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_DIALOGUE
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_ENABLE
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_HP_VIRTUALIZER
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_IEQ
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_PRESET
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_PROFILE
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_RESET
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_SPK_VIRTUALIZER
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_STEREO
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.PREF_VOLUME
|
||||
import co.aospa.dolby.xiaomi.DolbyConstants.Companion.dlog
|
||||
import co.aospa.dolby.xiaomi.DolbyController
|
||||
import co.aospa.dolby.xiaomi.R
|
||||
import com.android.settingslib.widget.MainSwitchPreference
|
||||
import com.android.settingslib.widget.SettingsBasePreferenceFragment
|
||||
|
||||
class DolbySettingsFragment : SettingsBasePreferenceFragment(),
|
||||
Preference.OnPreferenceChangeListener, CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
private val appContext: Context
|
||||
get() = requireContext().applicationContext
|
||||
|
||||
private val switchBar by lazy { findPreference<MainSwitchPreference>(PREF_ENABLE)!! }
|
||||
private val profilePref by lazy { findPreference<ListPreference>(PREF_PROFILE)!! }
|
||||
private val presetPref by lazy { findPreference<Preference>(PREF_PRESET)!! }
|
||||
private val ieqPref by lazy { findPreference<DolbyIeqPreference>(PREF_IEQ)!! }
|
||||
private val stereoPref by lazy { findPreference<ListPreference>(PREF_STEREO)!! }
|
||||
private val dialoguePref by lazy { findPreference<ListPreference>(PREF_DIALOGUE)!! }
|
||||
private val bassPref by lazy { findPreference<SwitchPreferenceCompat>(PREF_BASS)!! }
|
||||
private val hpVirtPref by lazy { findPreference<SwitchPreferenceCompat>(PREF_HP_VIRTUALIZER)!! }
|
||||
private val spkVirtPref by lazy { findPreference<SwitchPreferenceCompat>(PREF_SPK_VIRTUALIZER)!! }
|
||||
private val volumePref by lazy { findPreference<SwitchPreferenceCompat>(PREF_VOLUME)!! }
|
||||
private val resetPref by lazy { findPreference<Preference>(PREF_RESET)!! }
|
||||
|
||||
private val dolbyController by lazy(LazyThreadSafetyMode.NONE) {
|
||||
DolbyController.getInstance(appContext)
|
||||
}
|
||||
private val audioManager by lazy(LazyThreadSafetyMode.NONE) {
|
||||
appContext.getSystemService(AudioManager::class.java)
|
||||
}
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
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")
|
||||
setPreferencesFromResource(R.xml.dolby_settings, rootKey)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val profile = dolbyController.profile
|
||||
preferenceManager.preferenceDataStore = DolbyPreferenceStore(appContext).also {
|
||||
it.profile = profile
|
||||
}
|
||||
|
||||
val dsOn = dolbyController.dsOn
|
||||
switchBar.addOnSwitchChangeListener(this)
|
||||
switchBar.isChecked = dsOn
|
||||
|
||||
profilePref.onPreferenceChangeListener = this
|
||||
updateProfileIcon(profile)
|
||||
profilePref.isEnabled = dsOn
|
||||
profilePref.apply {
|
||||
if (entryValues.contains(profile.toString())) {
|
||||
summary = "%s"
|
||||
value = profile.toString()
|
||||
} else {
|
||||
summary = getString(R.string.dolby_unknown)
|
||||
}
|
||||
}
|
||||
|
||||
hpVirtPref.onPreferenceChangeListener = this
|
||||
spkVirtPref.onPreferenceChangeListener = this
|
||||
stereoPref.onPreferenceChangeListener = this
|
||||
dialoguePref.onPreferenceChangeListener = this
|
||||
bassPref.onPreferenceChangeListener = this
|
||||
volumePref.onPreferenceChangeListener = this
|
||||
ieqPref.onPreferenceChangeListener = this
|
||||
|
||||
resetPref.setOnPreferenceClickListener {
|
||||
dolbyController.resetProfileSpecificSettings()
|
||||
updateProfileSpecificPrefs()
|
||||
Toast.makeText(
|
||||
appContext,
|
||||
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 onResume() {
|
||||
super.onResume()
|
||||
updateProfileSpecificPrefs()
|
||||
}
|
||||
|
||||
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
|
||||
dlog(TAG, "onPreferenceChange: key=${preference.key} value=$newValue")
|
||||
when (preference.key) {
|
||||
PREF_PROFILE -> {
|
||||
val profile = newValue.toString().toInt()
|
||||
dolbyController.profile = profile
|
||||
(preferenceManager.preferenceDataStore as DolbyPreferenceStore).profile = profile
|
||||
updateProfileIcon(profile)
|
||||
updateProfileSpecificPrefs()
|
||||
}
|
||||
PREF_SPK_VIRTUALIZER -> dolbyController.setSpeakerVirtEnabled(newValue as Boolean)
|
||||
PREF_HP_VIRTUALIZER -> dolbyController.setHeadphoneVirtEnabled(newValue as Boolean)
|
||||
PREF_STEREO -> dolbyController.setStereoWideningAmount(newValue.toString().toInt())
|
||||
PREF_DIALOGUE -> dolbyController.setDialogueEnhancerAmount(newValue.toString().toInt())
|
||||
PREF_BASS -> dolbyController.setBassEnhancerEnabled(newValue as Boolean)
|
||||
PREF_VOLUME -> dolbyController.setVolumeLevelerEnabled(newValue as Boolean)
|
||||
PREF_IEQ -> dolbyController.setIeqPreset(newValue.toString().toInt())
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
|
||||
dlog(TAG, "onCheckedChanged($isChecked)")
|
||||
dolbyController.dsOn = isChecked
|
||||
profilePref.isEnabled = isChecked
|
||||
updateProfileSpecificPrefs()
|
||||
}
|
||||
|
||||
private fun updateSpeakerState() {
|
||||
val devices = audioManager
|
||||
?.getDevicesForAttributes(ATTRIBUTES_MEDIA)
|
||||
.orEmpty()
|
||||
val firstType = devices.firstOrNull()?.type
|
||||
isOnSpeaker = (firstType == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)
|
||||
}
|
||||
|
||||
private fun updateProfileSpecificPrefs() {
|
||||
val unknownRes = getString(R.string.dolby_unknown)
|
||||
val headphoneRes = 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.isEnabled = enable
|
||||
spkVirtPref.isEnabled = enable
|
||||
ieqPref.isEnabled = enable
|
||||
dialoguePref.isEnabled = enable
|
||||
volumePref.isEnabled = enable
|
||||
bassPref.isEnabled = enable
|
||||
resetPref.isEnabled = enable
|
||||
hpVirtPref.isEnabled = enable && !isOnSpeaker
|
||||
stereoPref.isEnabled = enable && !isOnSpeaker
|
||||
|
||||
if (!enable) return
|
||||
|
||||
presetPref.summary = dolbyController.getPresetName()
|
||||
|
||||
val ieqValue = dolbyController.getIeqPreset(currentProfile)
|
||||
ieqPref.apply {
|
||||
if (entryValues.contains(ieqValue.toString())) {
|
||||
summary = "%s"
|
||||
value = ieqValue.toString()
|
||||
} else {
|
||||
summary = unknownRes
|
||||
}
|
||||
}
|
||||
|
||||
val deValue = dolbyController.getDialogueEnhancerAmount(currentProfile).toString()
|
||||
dialoguePref.apply {
|
||||
if (entryValues.contains(deValue)) {
|
||||
summary = "%s"
|
||||
value = deValue
|
||||
} else {
|
||||
summary = unknownRes
|
||||
}
|
||||
}
|
||||
|
||||
spkVirtPref.isChecked = dolbyController.getSpeakerVirtEnabled(currentProfile)
|
||||
volumePref.isChecked = dolbyController.getVolumeLevelerEnabled(currentProfile)
|
||||
bassPref.isChecked = dolbyController.getBassEnhancerEnabled(currentProfile)
|
||||
|
||||
// below prefs are not enabled on loudspeaker
|
||||
if (isOnSpeaker) {
|
||||
stereoPref.summary = headphoneRes
|
||||
hpVirtPref.summary = headphoneRes
|
||||
return
|
||||
}
|
||||
|
||||
val swValue = dolbyController.getStereoWideningAmount(currentProfile).toString()
|
||||
stereoPref.apply {
|
||||
if (entryValues.contains(swValue)) {
|
||||
summary = "%s"
|
||||
value = swValue
|
||||
} else {
|
||||
summary = unknownRes
|
||||
}
|
||||
}
|
||||
|
||||
hpVirtPref.apply {
|
||||
isChecked = dolbyController.getHeadphoneVirtEnabled(currentProfile)
|
||||
summary = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateProfileIcon(profile: Int) {
|
||||
when (profile) {
|
||||
0 -> profilePref.setIcon(R.drawable.ic_profile_dynamic)
|
||||
1 -> profilePref.setIcon(R.drawable.ic_profile_movie)
|
||||
2 -> profilePref.setIcon(R.drawable.ic_profile_music)
|
||||
3 -> profilePref.setIcon(R.drawable.ic_profile_custom)
|
||||
else -> profilePref.setIcon(R.drawable.ic_dolby)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "DolbySettingsFragment"
|
||||
private val ATTRIBUTES_MEDIA = AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The LineageOS Project
|
||||
* Copyright (C) 2022,2025 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -13,12 +13,14 @@ class UdfpsHandler {
|
||||
public:
|
||||
virtual ~UdfpsHandler() = default;
|
||||
|
||||
virtual void init(fingerprint_device_t* device) = 0;
|
||||
virtual void onFingerDown(uint32_t x, uint32_t y, float minor, float major) = 0;
|
||||
virtual void onFingerUp() = 0;
|
||||
virtual void init(fingerprint_device_t* device) {};
|
||||
virtual void onFingerDown(uint32_t x, uint32_t y, float minor, float major) {};
|
||||
virtual void onFingerUp() {};
|
||||
|
||||
virtual void onAcquired(int32_t result, int32_t vendorCode) = 0;
|
||||
virtual void cancel() = 0;
|
||||
virtual void onAcquired(int32_t result, int32_t vendorCode) {};
|
||||
virtual void onAuthenticationSucceeded() {};
|
||||
virtual void onAuthenticationFailed() {};
|
||||
virtual void cancel() {};
|
||||
};
|
||||
|
||||
struct UdfpsHandlerFactory {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
//
|
||||
// Copyright (C) 2022 The LineageOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
soong_config_module_type {
|
||||
name: "xiaomi_touch_hal_cc_defaults",
|
||||
module_type: "cc_defaults",
|
||||
config_namespace: "XIAOMI_TOUCH",
|
||||
value_variables: ["HIGH_TOUCH_POLLING_PATH"],
|
||||
properties: ["cppflags"],
|
||||
}
|
||||
|
||||
xiaomi_touch_hal_cc_defaults {
|
||||
name: "xiaomi_touch_hal_defaults",
|
||||
soong_config_variables: {
|
||||
HIGH_TOUCH_POLLING_PATH: {
|
||||
cppflags: ["-DHIGH_TOUCH_POLLING_PATH=\"%s\""],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "vendor.lineage.touch@1.0-service.xiaomi",
|
||||
defaults: [
|
||||
"hidl_defaults",
|
||||
"xiaomi_touch_hal_defaults",
|
||||
],
|
||||
vintf_fragments: ["vendor.lineage.touch@1.0-service.xiaomi.xml"],
|
||||
init_rc: ["vendor.lineage.touch@1.0-service.xiaomi.rc"],
|
||||
relative_install_path: "hw",
|
||||
proprietary: true,
|
||||
srcs: [
|
||||
"HighTouchPollingRate.cpp",
|
||||
"service.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder",
|
||||
"libhidlbase",
|
||||
"libutils",
|
||||
"vendor.lineage.touch@1.0",
|
||||
],
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user