mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
Autoconnection wip2
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
package eu.darken.capod.common.bluetooth
|
||||
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.os.ParcelUuid
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class BluetoothDevice2(
|
||||
val device: BluetoothDevice
|
||||
) : Parcelable {
|
||||
|
||||
val name: String?
|
||||
get() = device.name
|
||||
|
||||
fun hasFeature(uuid: ParcelUuid): Boolean = device.hasFeature(uuid)
|
||||
}
|
||||
@@ -96,7 +96,7 @@ class BluetoothManager2 @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun connectedDevicesForProfile(profile: Int): Flow<Set<BluetoothDevice2>> = callbackFlow {
|
||||
private fun connectedDevicesForProfile(profile: Int): Flow<Set<BluetoothDevice>> = callbackFlow {
|
||||
log(TAG) { "connectedDevices(profile=$profile) starting" }
|
||||
trySend(getBluetoothProfile(profile).connectedDevices)
|
||||
|
||||
@@ -118,9 +118,7 @@ class BluetoothManager2 @Inject constructor(
|
||||
log(TAG, ERROR) { "Bluetooth event without action, how did we get this?" }
|
||||
return
|
||||
}
|
||||
val device = intent.getParcelableExtra<BluetoothDevice?>(BluetoothDevice.EXTRA_DEVICE)?.let {
|
||||
BluetoothDevice2(it)
|
||||
}
|
||||
val device = intent.getParcelableExtra<BluetoothDevice?>(BluetoothDevice.EXTRA_DEVICE)
|
||||
if (device == null) {
|
||||
log(TAG, ERROR) { "Connection event is missing EXTRA_DEVICE: ${intent.extras}" }
|
||||
return
|
||||
@@ -150,7 +148,7 @@ class BluetoothManager2 @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun connectedDevices(): Flow<List<BluetoothDevice2>> = isBluetoothEnabled
|
||||
fun connectedDevices(): Flow<List<BluetoothDevice>> = isBluetoothEnabled
|
||||
.flatMapLatest { connectedDevicesForProfile(BluetoothProfile.HEADSET) }
|
||||
.map { devices ->
|
||||
devices.filter { device ->
|
||||
@@ -160,14 +158,15 @@ class BluetoothManager2 @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun bondedDevices(): List<BluetoothDevice2> = adapter.bondedDevices
|
||||
.map { BluetoothDevice2(it) }
|
||||
fun bondedDevices(): Set<BluetoothDevice> = adapter.bondedDevices
|
||||
|
||||
suspend fun nudgeConnection(device: BluetoothDevice2): Boolean = try {
|
||||
suspend fun nudgeConnection(device: BluetoothDevice): Boolean = try {
|
||||
log(TAG) { "Nudging Android connection to $device" }
|
||||
val connectMethod = BluetoothHeadset::class.java.getDeclaredMethod(
|
||||
"connect", BluetoothDevice::class.java
|
||||
).apply { isAccessible = true }
|
||||
connectMethod.invoke(getBluetoothProfile().profile, device.device)
|
||||
connectMethod.invoke(getBluetoothProfile().profile, device)
|
||||
log(TAG) { "Nudged connection to $device" }
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
log(TAG, WARN) { "BluetoothHeadset.connect(device) is unavailable:\n${e.asLog()}" }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package eu.darken.capod.common.bluetooth
|
||||
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.bluetooth.BluetoothProfile
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
@@ -15,8 +16,8 @@ data class BluetoothProfile2(
|
||||
return profileProxy
|
||||
}
|
||||
|
||||
val connectedDevices: Set<BluetoothDevice2>
|
||||
get() = profile.connectedDevices.map { BluetoothDevice2(it) }.toSet()
|
||||
val connectedDevices: Set<BluetoothDevice>
|
||||
get() = profile.connectedDevices.toSet()
|
||||
|
||||
val isConnected: Boolean
|
||||
get() = isConnectedAtomic.get()
|
||||
|
||||
@@ -10,7 +10,9 @@ import androidx.annotation.MenuRes
|
||||
import androidx.annotation.XmlRes
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import eu.darken.androidstarter.common.preferences.Settings
|
||||
import eu.darken.capod.main.ui.settings.SettingsFragment
|
||||
|
||||
@@ -67,4 +69,17 @@ abstract class PreferenceFragment2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> LiveData<T>.observe2(
|
||||
crossinline callback: (T) -> Unit
|
||||
) {
|
||||
observe(viewLifecycleOwner) { callback.invoke(it) }
|
||||
}
|
||||
|
||||
inline fun <T, reified VB : ViewBinding?> LiveData<T>.observe2(
|
||||
ui: VB,
|
||||
crossinline callback: VB.(T) -> Unit
|
||||
) {
|
||||
observe(viewLifecycleOwner) { callback.invoke(ui, it) }
|
||||
}
|
||||
}
|
||||
@@ -33,16 +33,6 @@ class GeneralSettings @Inject constructor(
|
||||
moshi
|
||||
)
|
||||
|
||||
val autoPause = preferences.createFlowPreference(
|
||||
"core.eardetection.autopause.enabled",
|
||||
false
|
||||
)
|
||||
|
||||
val autoPlay = preferences.createFlowPreference(
|
||||
"core.eardetection.autoplay.enabled",
|
||||
false
|
||||
)
|
||||
|
||||
val showAll = preferences.createFlowPreference(
|
||||
"core.showall.enabled",
|
||||
false
|
||||
@@ -53,11 +43,14 @@ class GeneralSettings @Inject constructor(
|
||||
0.25f
|
||||
)
|
||||
|
||||
val mainDeviceAddress = preferences.createFlowPreference<String?>(
|
||||
"core.maindevice.address",
|
||||
null
|
||||
)
|
||||
|
||||
override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper(
|
||||
monitorMode,
|
||||
scannerMode,
|
||||
autoPause,
|
||||
autoPlay,
|
||||
showAll,
|
||||
minimumSignalQuality,
|
||||
debugSettings.isAutoReportEnabled,
|
||||
|
||||
@@ -7,19 +7,15 @@ import eu.darken.capod.R
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class MonitorMode(
|
||||
val identifier: String,
|
||||
@StringRes val labelRes: Int
|
||||
) {
|
||||
@Json(name = "monitor.mode.manual") MANUAL(
|
||||
"monitor.mode.manual",
|
||||
R.string.settings_monitor_mode_manual_label
|
||||
),
|
||||
@Json(name = "monitor.mode.automatic") AUTOMATIC(
|
||||
"monitor.mode.automatic",
|
||||
R.string.settings_monitor_mode_automatic_label
|
||||
),
|
||||
@Json(name = "monitor.mode.always") ALWAYS(
|
||||
"monitor.mode.always",
|
||||
R.string.settings_monitor_mode_always_label
|
||||
),
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import eu.darken.capod.main.core.MonitorMode
|
||||
import eu.darken.capod.main.core.PermissionTool
|
||||
import eu.darken.capod.monitor.core.*
|
||||
import eu.darken.capod.monitor.ui.MonitorNotifications
|
||||
import eu.darken.capod.reactions.core.ReactionsHub
|
||||
import eu.darken.capod.reaction.core.ReactionHub
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -37,7 +37,7 @@ class MonitorWorker @AssistedInject constructor(
|
||||
private val generalSettings: GeneralSettings,
|
||||
private val permissionTool: PermissionTool,
|
||||
private val podMonitor: PodMonitor,
|
||||
private val reactionsHub: ReactionsHub,
|
||||
private val reactionHub: ReactionHub,
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
) : CoroutineWorker(context, params) {
|
||||
|
||||
@@ -138,7 +138,7 @@ class MonitorWorker @AssistedInject constructor(
|
||||
}
|
||||
.launchIn(workerScope)
|
||||
|
||||
reactionsHub.monitor()
|
||||
reactionHub.monitor()
|
||||
.catch {
|
||||
log(TAG, WARN) { "Pod reactions failed:\n${it.asLog()}" }
|
||||
}
|
||||
|
||||
+7
-3
@@ -1,13 +1,16 @@
|
||||
package eu.darken.capod.reactions.core
|
||||
package eu.darken.capod.reaction.core
|
||||
|
||||
import dagger.Reusable
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.setupCommonEventHandlers
|
||||
import eu.darken.capod.reaction.core.autoconnect.AutoConnect
|
||||
import eu.darken.capod.reaction.core.playpause.PlayPause
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import javax.inject.Inject
|
||||
|
||||
@Reusable
|
||||
class ReactionsHub @Inject constructor(
|
||||
class ReactionHub @Inject constructor(
|
||||
private val playPause: PlayPause,
|
||||
private val autoConnect: AutoConnect,
|
||||
) {
|
||||
@@ -18,8 +21,9 @@ class ReactionsHub @Inject constructor(
|
||||
) { _, _ ->
|
||||
Unit
|
||||
}
|
||||
.setupCommonEventHandlers(TAG) { "monitor" }
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Reactions", "Hub")
|
||||
private val TAG = logTag("Reaction", "Hub")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package eu.darken.capod.reaction.core
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceDataStore
|
||||
import com.squareup.moshi.Moshi
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import eu.darken.androidstarter.common.preferences.Settings
|
||||
import eu.darken.capod.common.preferences.PreferenceStoreMapper
|
||||
import eu.darken.capod.common.preferences.createFlowPreference
|
||||
import eu.darken.capod.reaction.core.autoconnect.AutoConnectCondition
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ReactionSettings @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
moshi: Moshi,
|
||||
) : Settings() {
|
||||
|
||||
override val preferences: SharedPreferences =
|
||||
context.getSharedPreferences("settings_reaction", Context.MODE_PRIVATE)
|
||||
|
||||
val autoPause = preferences.createFlowPreference(
|
||||
"reaction.autopause.enabled",
|
||||
false
|
||||
)
|
||||
|
||||
val autoPlay = preferences.createFlowPreference(
|
||||
"reaction.autoplay.enabled",
|
||||
false
|
||||
)
|
||||
|
||||
val autoConnect = preferences.createFlowPreference(
|
||||
"reaction.autoconnect.enabled",
|
||||
false
|
||||
)
|
||||
|
||||
val autoConnectCondition = preferences.createFlowPreference(
|
||||
"reaction.autoconnect.condition",
|
||||
AutoConnectCondition.WHEN_SEEN,
|
||||
moshi
|
||||
)
|
||||
|
||||
override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper(
|
||||
autoPause,
|
||||
autoPlay,
|
||||
autoConnect,
|
||||
autoConnectCondition,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package eu.darken.capod.reaction.core.autoconnect
|
||||
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.setupCommonEventHandlers
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AutoConnect @Inject constructor(
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
private val podMonitor: PodMonitor,
|
||||
) {
|
||||
|
||||
fun monitor(): Flow<Unit> = podMonitor.mainDevice
|
||||
.distinctUntilChangedBy { it?.identifier }
|
||||
.onEach { mainPodDevice ->
|
||||
if (mainPodDevice == null) {
|
||||
log(TAG) { "MainPodDevice is null" }
|
||||
return@onEach
|
||||
}
|
||||
|
||||
val podName = "AirPod"
|
||||
|
||||
val bondedDevice = bluetoothManager.bondedDevices()
|
||||
.firstOrNull { it.name?.contains("AirPod") ?: false }
|
||||
if (bondedDevice == null) {
|
||||
log(TAG) { "No bonded device matches $podName" }
|
||||
return@onEach
|
||||
} else {
|
||||
log(TAG) { "Found target device: $bondedDevice" }
|
||||
}
|
||||
|
||||
val connectedDevice = bluetoothManager.connectedDevices()
|
||||
.firstOrNull()
|
||||
?.firstOrNull { it.name?.contains("podName") ?: false }
|
||||
if (connectedDevice?.address == bondedDevice.address) {
|
||||
log(TAG) { "We are already connected to the target device: $connectedDevice" }
|
||||
return@onEach
|
||||
}
|
||||
|
||||
bluetoothManager.nudgeConnection(bondedDevice)
|
||||
}
|
||||
.map { Unit }
|
||||
.setupCommonEventHandlers(TAG) { "monitor" }
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Reaction", "AutoConnect")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package eu.darken.capod.reaction.core.autoconnect
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import eu.darken.capod.R
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class AutoConnectCondition(
|
||||
val identifier: String,
|
||||
@StringRes val labelRes: Int
|
||||
) {
|
||||
@Json(name = "autoconnect.condition.seen") WHEN_SEEN(
|
||||
"monitor.mode.manual",
|
||||
R.string.settings_monitor_mode_manual_label
|
||||
),
|
||||
@Json(name = "autoconnect.condition.case") CASE_OPEN(
|
||||
"autoconnect.condition.case",
|
||||
R.string.settings_monitor_mode_automatic_label
|
||||
),
|
||||
@Json(name = "autoconnect.condition.inear") IN_EAR(
|
||||
"autoconnect.condition.inear",
|
||||
R.string.settings_monitor_mode_always_label
|
||||
),
|
||||
}
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
package eu.darken.capod.reactions.core
|
||||
package eu.darken.capod.reaction.core.playpause
|
||||
|
||||
import dagger.Reusable
|
||||
import eu.darken.capod.common.MediaControl
|
||||
@@ -7,9 +7,9 @@ import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.setupCommonEventHandlers
|
||||
import eu.darken.capod.common.flow.withPrevious
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
import eu.darken.capod.pods.core.HasEarDetection
|
||||
import eu.darken.capod.reaction.core.ReactionSettings
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
@@ -20,7 +20,7 @@ import javax.inject.Inject
|
||||
class PlayPause @Inject constructor(
|
||||
private val podMonitor: PodMonitor,
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
private val generalSettings: GeneralSettings,
|
||||
private val reactionSettings: ReactionSettings,
|
||||
private val mediaControl: MediaControl,
|
||||
) {
|
||||
|
||||
@@ -34,7 +34,7 @@ class PlayPause @Inject constructor(
|
||||
podMonitor.mainDevice
|
||||
}
|
||||
}
|
||||
.setupCommonEventHandlers(TAG) { "podReactions" }
|
||||
.setupCommonEventHandlers(TAG) { "monitor" }
|
||||
.distinctUntilChanged()
|
||||
.withPrevious()
|
||||
.onEach { (previous, current) ->
|
||||
@@ -42,9 +42,9 @@ class PlayPause @Inject constructor(
|
||||
log(TAG) { "previous=${previous.isBeingWorn}, current=${current.isBeingWorn}" }
|
||||
log(TAG) { "previous-id=${previous.identifier}, current-id=${current.identifier}" }
|
||||
if (previous.identifier == current.identifier && previous.isBeingWorn != current.isBeingWorn) {
|
||||
if (current.isBeingWorn && generalSettings.autoPlay.value && !mediaControl.isPlaying) {
|
||||
if (current.isBeingWorn && reactionSettings.autoPlay.value && !mediaControl.isPlaying) {
|
||||
mediaControl.sendPlay()
|
||||
} else if (!current.isBeingWorn && generalSettings.autoPause.value && mediaControl.isPlaying) {
|
||||
} else if (!current.isBeingWorn && reactionSettings.autoPause.value && mediaControl.isPlaying) {
|
||||
mediaControl.sendPause()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package eu.darken.capod.reaction.ui.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.Keep
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.preference.ListPreference
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.uix.PreferenceFragment2
|
||||
import eu.darken.capod.reaction.core.ReactionSettings
|
||||
import eu.darken.capod.reaction.core.autoconnect.AutoConnectCondition
|
||||
import javax.inject.Inject
|
||||
|
||||
@Keep
|
||||
@AndroidEntryPoint
|
||||
class ReactionSettingsFragment : PreferenceFragment2() {
|
||||
|
||||
private val vm: ReactionSettingsFragmentVM by viewModels()
|
||||
|
||||
@Inject lateinit var reactionSettings: ReactionSettings
|
||||
|
||||
override val settings: ReactionSettings
|
||||
get() = reactionSettings
|
||||
|
||||
override val preferenceFile: Int = R.xml.preferences_reactions
|
||||
|
||||
private val autoConnectConditionPref by lazy { findPreference<ListPreference>(settings.autoConnectCondition.key)!! }
|
||||
|
||||
override fun onPreferencesCreated() {
|
||||
autoConnectConditionPref.apply {
|
||||
entries = AutoConnectCondition.values().map { getString(it.labelRes) }.toTypedArray()
|
||||
entryValues = AutoConnectCondition.values().map { settings.autoConnectCondition.rawWriter(it) as String }
|
||||
.toTypedArray()
|
||||
}
|
||||
super.onPreferencesCreated()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
settings.autoConnect.flow.asLiveData().observe2 {
|
||||
autoConnectConditionPref.isEnabled = it
|
||||
}
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package eu.darken.capod.reaction.ui.settings
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.uix.ViewModel3
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ReactionSettingsFragmentVM @Inject constructor(
|
||||
private val handle: SavedStateHandle,
|
||||
private val dispatcherProvider: DispatcherProvider,
|
||||
) : ViewModel3(dispatcherProvider) {
|
||||
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Settings", "Reaction", "VM")
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package eu.darken.capod.reactions.core
|
||||
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AutoConnect @Inject constructor(
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
private val podMonitor: PodMonitor,
|
||||
) {
|
||||
|
||||
fun monitor(): Flow<Unit> = podMonitor.mainDevice
|
||||
.map {
|
||||
val podDevice = bluetoothManager2.bondedDevices().firstOrNull { it.name?.contains("AirPod") ?: false }
|
||||
bluetoothManager2.nudgeConnection(podDevice!!)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Reactions", "AutoConnect")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M7,12l-2,-2 -2,2 2,2 2,-2zM17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88zM19,10l-2,2 2,2 2,-2 -2,-2z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M13,13v8h8v-8h-8zM3,21h8v-8L3,13v8zM3,3v8h8L11,3L3,3zM16.66,1.69L11,7.34 16.66,13l5.66,-5.66 -5.66,-5.65z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6,13c-2.2,0 -4,1.8 -4,4s1.8,4 4,4s4,-1.8 4,-4S8.2,13 6,13zM12,3C9.8,3 8,4.8 8,7s1.8,4 4,4s4,-1.8 4,-4S14.2,3 12,3zM18,13c-2.2,0 -4,1.8 -4,4s1.8,4 4,4s4,-1.8 4,-4S20.2,13 18,13z" />
|
||||
</vector>
|
||||
@@ -117,4 +117,11 @@
|
||||
<string name="pods_microphone_label">Microphone</string>
|
||||
<string name="settings_signal_minimum_label">Minimum signal quality</string>
|
||||
<string name="settings_signal_minimum_description">The minimum signal quality that a device needs to have to be considered yours.</string>
|
||||
<string name="settings_autoconnect_label">Auto connect</string>
|
||||
<string name="settings_autoconnect_description">Seeing the device and being connected is not the same. If Android does not automatically connect, we can ask it too.</string>
|
||||
<string name="settings_autoconnect_condition_label">Auto connect condition</string>
|
||||
<string name="settings_autoconnect_condition_description">When should we auto connect to your device?</string>
|
||||
<string name="settings_reaction_label">Reactions</string>
|
||||
<string name="settings_reaction_description">React to events and behaviors.</string>
|
||||
<string name="settings_category_yourdevice_label">Your device</string>
|
||||
</resources>
|
||||
@@ -14,31 +14,27 @@
|
||||
android:summary="@string/settings_scanner_mode_description"
|
||||
android:title="@string/settings_scanner_mode_label" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:icon="@drawable/ic_baseline_play_circle_24"
|
||||
android:key="core.eardetection.autoplay.enabled"
|
||||
android:summary="@string/settings_autoplay_description"
|
||||
android:title="@string/settings_autopplay_label" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:icon="@drawable/ic_baseline_pause_circle_24"
|
||||
android:key="core.eardetection.autopause.enabled"
|
||||
android:summary="@string/settings_autopause_description"
|
||||
android:title="@string/settings_autopause_label" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:icon="@drawable/ic_baseline_devices_other_24"
|
||||
android:key="core.showall.enabled"
|
||||
android:summary="@string/settings_showall_description"
|
||||
android:title="@string/settings_showall_label" />
|
||||
|
||||
<eu.darken.capod.common.preferences.PercentSliderPreference
|
||||
android:icon="@drawable/ic_baseline_signal_cellular_alt_24"
|
||||
android:key="core.signal.minimum"
|
||||
android:summary="@string/settings_signal_minimum_description"
|
||||
android:title="@string/settings_signal_minimum_label"
|
||||
app:pspMax="0.9"
|
||||
app:pspMin="0.1" />
|
||||
<PreferenceCategory android:title="@string/settings_category_yourdevice_label">
|
||||
|
||||
<eu.darken.capod.common.preferences.PercentSliderPreference
|
||||
android:icon="@drawable/ic_baseline_signal_cellular_alt_24"
|
||||
android:key="core.signal.minimum"
|
||||
android:summary="@string/settings_signal_minimum_description"
|
||||
android:title="@string/settings_signal_minimum_label"
|
||||
app:pspMax="0.9"
|
||||
app:pspMin="0.1" />
|
||||
<ListPreference
|
||||
android:icon="@drawable/ic_baseline_settings_bluetooth_24"
|
||||
android:key="core.scanner.mode"
|
||||
android:summary="@string/settings_scanner_mode_description"
|
||||
android:title="@string/settings_scanner_mode_label" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/settings_category_other_label">
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
app:summary="@string/settings_general_description"
|
||||
app:title="@string/settings_general_label" />
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/ic_baseline_widgets_24"
|
||||
app:fragment="eu.darken.capod.reaction.ui.settings.ReactionSettingsFragment"
|
||||
app:summary="@string/settings_reaction_description"
|
||||
app:title="@string/settings_reaction_label" />
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/ic_baseline_support_agent_24"
|
||||
app:fragment="eu.darken.capod.main.ui.settings.support.SupportFragment"
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<CheckBoxPreference
|
||||
android:icon="@drawable/ic_baseline_play_circle_24"
|
||||
android:key="reaction.autoplay.enabled"
|
||||
android:summary="@string/settings_autoplay_description"
|
||||
android:title="@string/settings_autopplay_label" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:icon="@drawable/ic_baseline_pause_circle_24"
|
||||
android:key="reaction.autopause.enabled"
|
||||
android:summary="@string/settings_autopause_description"
|
||||
android:title="@string/settings_autopause_label" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:icon="@drawable/ic_baseline_bluetooth_connected_24"
|
||||
android:key="reaction.autoconnect.enabled"
|
||||
android:summary="@string/settings_autoconnect_description"
|
||||
android:title="@string/settings_autoconnect_label" />
|
||||
|
||||
<ListPreference
|
||||
android:icon="@drawable/ic_baseline_workspaces_24"
|
||||
android:key="reaction.autoconnect.condition"
|
||||
android:summary="@string/settings_autoconnect_condition_description"
|
||||
android:title="@string/settings_autoconnect_condition_label" />
|
||||
|
||||
</PreferenceScreen>
|
||||
Reference in New Issue
Block a user