mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-15 02:36:12 -04:00
Add auto connection feature, if the BLE beacon is spotted, try to establish a connection.
This commit is contained in:
@@ -3,6 +3,7 @@ package eu.darken.capod.common
|
||||
import android.media.AudioManager
|
||||
import android.os.SystemClock
|
||||
import android.view.KeyEvent
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -18,18 +19,18 @@ class MediaControl @Inject constructor(
|
||||
get() = audioManager.isMusicActive
|
||||
|
||||
suspend fun sendPlay() {
|
||||
log(TAG) { "sendPlay()" }
|
||||
log(TAG, INFO) { "sendPlay()" }
|
||||
if (audioManager.isMusicActive) {
|
||||
log(TAG) { "Music is already playing, not sending play" }
|
||||
log(TAG, INFO) { "Music is already playing, not sending play" }
|
||||
return
|
||||
}
|
||||
sendKey(KeyEvent.KEYCODE_MEDIA_PLAY)
|
||||
}
|
||||
|
||||
suspend fun sendPause() {
|
||||
log(TAG) { "sendPause()" }
|
||||
log(TAG, INFO) { "sendPause()" }
|
||||
if (!audioManager.isMusicActive) {
|
||||
log(TAG) { "Music is not playing, not sending pause" }
|
||||
log(TAG, INFO) { "Music is not playing, not sending pause" }
|
||||
return
|
||||
}
|
||||
sendKey(KeyEvent.KEYCODE_MEDIA_PAUSE)
|
||||
|
||||
@@ -162,10 +162,13 @@ class BluetoothManager2 @Inject constructor(
|
||||
|
||||
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)
|
||||
|
||||
log(TAG) { "Nudged connection to $device" }
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -19,15 +19,20 @@ open class PreferenceStoreMapper(
|
||||
}
|
||||
|
||||
override fun getString(key: String, defValue: String?): String? {
|
||||
return flowPreferences.singleOrNull { it.key == key }?.let { flowPref ->
|
||||
val pref = flowPreferences.singleOrNull { it.key == key }
|
||||
?: throw NotImplementedError("getString(key=$key, defValue=$defValue)")
|
||||
|
||||
return pref.let { flowPref ->
|
||||
flowPref.valueRaw as String?
|
||||
} ?: throw NotImplementedError("getString(key=$key, defValue=$defValue)")
|
||||
}
|
||||
}
|
||||
|
||||
override fun putString(key: String, value: String?) {
|
||||
flowPreferences.singleOrNull { it.key == key }?.let { flowPref ->
|
||||
val pref = flowPreferences.singleOrNull { it.key == key }
|
||||
?: throw NotImplementedError("putString(key=$key, defValue=$value)")
|
||||
pref.let { flowPref ->
|
||||
flowPref.valueRaw = value
|
||||
} ?: throw NotImplementedError("putString(key=$key, defValue=$value)")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getInt(key: String?, defValue: Int): Int {
|
||||
|
||||
@@ -53,6 +53,7 @@ class GeneralSettings @Inject constructor(
|
||||
scannerMode,
|
||||
showAll,
|
||||
minimumSignalQuality,
|
||||
mainDeviceAddress,
|
||||
debugSettings.isAutoReportEnabled,
|
||||
)
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package eu.darken.capod.main.ui.settings.general
|
||||
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.darken.capod.R
|
||||
|
||||
|
||||
class DeviceSelectionDialogFactory constructor(private val context: Context) {
|
||||
|
||||
fun create(
|
||||
devices: List<BluetoothDevice>,
|
||||
current: BluetoothDevice?,
|
||||
callback: (BluetoothDevice?) -> Unit
|
||||
): AlertDialog {
|
||||
return MaterialAlertDialogBuilder(context).apply {
|
||||
setTitle(R.string.settings_maindevice_address_label)
|
||||
|
||||
val pairing = devices
|
||||
.map { (it.name ?: "?") to it.address }
|
||||
.plus(context.getString(R.string.settings_maindevice_address_none) to "")
|
||||
|
||||
setSingleChoiceItems(
|
||||
pairing.map { it.first }.toTypedArray(),
|
||||
pairing.indexOfFirst { it.second == current?.address ?: "" },
|
||||
DialogInterface.OnClickListener { dialog, which ->
|
||||
val selected = devices.firstOrNull { it.address == pairing[which].second }
|
||||
callback(selected)
|
||||
dialog.dismiss()
|
||||
}
|
||||
)
|
||||
|
||||
}.create()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package eu.darken.capod.main.ui.settings.general
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.Keep
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.preference.ListPreference
|
||||
@@ -28,6 +30,7 @@ class GeneralSettingsFragment : PreferenceFragment2() {
|
||||
|
||||
private val monitorModePref by lazy { findPreference<ListPreference>(generalSettings.monitorMode.key)!! }
|
||||
private val scanModePref by lazy { findPreference<ListPreference>(generalSettings.scannerMode.key)!! }
|
||||
private val mainDeviceAddressPref by lazy { findPreference<Preference>(generalSettings.mainDeviceAddress.key)!! }
|
||||
|
||||
override fun onPreferencesCreated() {
|
||||
monitorModePref.apply {
|
||||
@@ -41,6 +44,23 @@ class GeneralSettingsFragment : PreferenceFragment2() {
|
||||
super.onPreferencesCreated()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
vm.bondedDevices.observe2 { devices ->
|
||||
mainDeviceAddressPref.setOnPreferenceClickListener {
|
||||
val dialog = DeviceSelectionDialogFactory(requireContext()).create(
|
||||
devices,
|
||||
devices.firstOrNull { it.address == generalSettings.mainDeviceAddress.value }
|
||||
) { selected ->
|
||||
generalSettings.mainDeviceAddress.value = selected?.address
|
||||
}
|
||||
dialog.show()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onDisplayPreferenceDialog(preference: Preference) {
|
||||
if (PercentSliderPreference.onDisplayPreferenceDialog(this, preference)) return
|
||||
|
||||
|
||||
+6
@@ -2,17 +2,23 @@ package eu.darken.capod.main.ui.settings.general
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.uix.ViewModel3
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class GeneralSettingsFragmentVM @Inject constructor(
|
||||
private val handle: SavedStateHandle,
|
||||
private val dispatcherProvider: DispatcherProvider,
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
) : ViewModel3(dispatcherProvider) {
|
||||
|
||||
val bondedDevices = flow {
|
||||
emit(bluetoothManager.bondedDevices().toList())
|
||||
}.asLiveData2()
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Settings", "General", "VM")
|
||||
|
||||
@@ -60,7 +60,7 @@ class MonitorWorker @AssistedInject constructor(
|
||||
val start = System.currentTimeMillis()
|
||||
log(TAG, VERBOSE) { "Executing $inputData now (runAttemptCount=$runAttemptCount)" }
|
||||
|
||||
doDoWork()
|
||||
doDoWork()
|
||||
|
||||
val duration = System.currentTimeMillis() - start
|
||||
|
||||
@@ -121,8 +121,9 @@ class MonitorWorker @AssistedInject constructor(
|
||||
}
|
||||
MonitorMode.ALWAYS -> emptyFlow()
|
||||
MonitorMode.AUTOMATIC -> flow {
|
||||
if (devices.isNotEmpty()) {
|
||||
log(TAG) { "Pods are connected, aborting any timeout." }
|
||||
val mainAddress = generalSettings.mainDeviceAddress.value
|
||||
if (devices.any { it.address == mainAddress }) {
|
||||
log(TAG) { "MainDevice is connected ($mainAddress), aborting any timeout." }
|
||||
} else {
|
||||
log(TAG) { "No Pods are connected, canceling worker soon." }
|
||||
delay(30 * 1000)
|
||||
|
||||
@@ -4,7 +4,11 @@ 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.main.core.GeneralSettings
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
import eu.darken.capod.pods.core.HasEarDetection
|
||||
import eu.darken.capod.pods.core.apple.DualApplePods
|
||||
import eu.darken.capod.reaction.core.ReactionSettings
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -13,38 +17,60 @@ import javax.inject.Singleton
|
||||
class AutoConnect @Inject constructor(
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
private val podMonitor: PodMonitor,
|
||||
private val generalSettings: GeneralSettings,
|
||||
private val reactionSettings: ReactionSettings
|
||||
) {
|
||||
|
||||
fun monitor(): Flow<Unit> = podMonitor.mainDevice
|
||||
.distinctUntilChangedBy { it?.identifier }
|
||||
.onEach { mainPodDevice ->
|
||||
if (mainPodDevice == null) {
|
||||
log(TAG) { "MainPodDevice is null" }
|
||||
return@onEach
|
||||
.filterNotNull()
|
||||
.map { podDevice ->
|
||||
log(TAG) { "mainPodDevice is $podDevice" }
|
||||
|
||||
val mainDeviceAddr = generalSettings.mainDeviceAddress.value
|
||||
if (mainDeviceAddr.isNullOrEmpty()) {
|
||||
log(TAG) { "mainDeviceAddress is null" }
|
||||
return@map
|
||||
} else {
|
||||
log(TAG) { "mainDeviceAddress is $mainDeviceAddr" }
|
||||
}
|
||||
|
||||
val podName = "AirPod"
|
||||
|
||||
val bondedDevice = bluetoothManager.bondedDevices()
|
||||
.firstOrNull { it.name?.contains("AirPod") ?: false }
|
||||
val bondedDevice = bluetoothManager.bondedDevices().firstOrNull { it.address == mainDeviceAddr }
|
||||
if (bondedDevice == null) {
|
||||
log(TAG) { "No bonded device matches $podName" }
|
||||
return@onEach
|
||||
log(TAG) { "No bonded device matches $mainDeviceAddr" }
|
||||
return@map
|
||||
} 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
|
||||
val isAlreadyConnected = bluetoothManager.connectedDevices().first().any {
|
||||
it.address == bondedDevice.address
|
||||
}
|
||||
|
||||
bluetoothManager.nudgeConnection(bondedDevice)
|
||||
if (isAlreadyConnected) {
|
||||
log(TAG) { "We are already connected to the target device: $bondedDevice" }
|
||||
return@map
|
||||
}
|
||||
|
||||
val condition = reactionSettings.autoConnectCondition.value
|
||||
log(TAG) { "Checking condition $condition" }
|
||||
val conditionFulfilled = when (condition) {
|
||||
AutoConnectCondition.WHEN_SEEN -> true
|
||||
AutoConnectCondition.CASE_OPEN -> when (podDevice) {
|
||||
is DualApplePods -> podDevice.caseLidState == DualApplePods.LidState.OPEN
|
||||
else -> true
|
||||
}
|
||||
AutoConnectCondition.IN_EAR -> when (podDevice) {
|
||||
is HasEarDetection -> podDevice.isBeingWorn
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
if (!conditionFulfilled) {
|
||||
log(TAG) { "Auto connect condition ($condition) is not fullfilled." }
|
||||
return@map
|
||||
}
|
||||
val result = bluetoothManager.nudgeConnection(bondedDevice)
|
||||
log(TAG) { "nudgeConnection($bondedDevice) returned $result" }
|
||||
}
|
||||
.map { Unit }
|
||||
.setupCommonEventHandlers(TAG) { "monitor" }
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -12,14 +12,14 @@ enum class AutoConnectCondition(
|
||||
) {
|
||||
@Json(name = "autoconnect.condition.seen") WHEN_SEEN(
|
||||
"monitor.mode.manual",
|
||||
R.string.settings_monitor_mode_manual_label
|
||||
R.string.settings_reaction_autoconnect_whenseen_label
|
||||
),
|
||||
@Json(name = "autoconnect.condition.case") CASE_OPEN(
|
||||
"autoconnect.condition.case",
|
||||
R.string.settings_monitor_mode_automatic_label
|
||||
R.string.settings_reaction_autoconnect_caseopen_label
|
||||
),
|
||||
@Json(name = "autoconnect.condition.inear") IN_EAR(
|
||||
"autoconnect.condition.inear",
|
||||
R.string.settings_monitor_mode_always_label
|
||||
R.string.settings_reaction_autoconnect_inear_label
|
||||
),
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package eu.darken.capod.reaction.core.playpause
|
||||
import dagger.Reusable
|
||||
import eu.darken.capod.common.MediaControl
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.setupCommonEventHandlers
|
||||
@@ -34,24 +35,33 @@ class PlayPause @Inject constructor(
|
||||
podMonitor.mainDevice
|
||||
}
|
||||
}
|
||||
.setupCommonEventHandlers(TAG) { "monitor" }
|
||||
.distinctUntilChanged()
|
||||
.withPrevious()
|
||||
.onEach { (previous, current) ->
|
||||
if (previous is HasEarDetection && current is HasEarDetection) {
|
||||
log(TAG) { "previous=${previous.isBeingWorn}, current=${current.isBeingWorn}" }
|
||||
log(TAG) { "previous-id=${previous.identifier}, current-id=${current.identifier}" }
|
||||
log(TAG, VERBOSE) { "previous=${previous.isBeingWorn}, current=${current.isBeingWorn}" }
|
||||
log(TAG, VERBOSE) { "previous-id=${previous.identifier}, current-id=${current.identifier}" }
|
||||
if (previous.identifier == current.identifier && previous.isBeingWorn != current.isBeingWorn) {
|
||||
if (current.isBeingWorn && reactionSettings.autoPlay.value && !mediaControl.isPlaying) {
|
||||
mediaControl.sendPlay()
|
||||
} else if (!current.isBeingWorn && reactionSettings.autoPause.value && mediaControl.isPlaying) {
|
||||
mediaControl.sendPause()
|
||||
log(TAG) { "Ear status changed for monitored device." }
|
||||
if (current.isBeingWorn && !mediaControl.isPlaying) {
|
||||
if (reactionSettings.autoPlay.value) {
|
||||
mediaControl.sendPlay()
|
||||
} else {
|
||||
log(TAG) { "autoPlay is disabled" }
|
||||
}
|
||||
} else if (!current.isBeingWorn && mediaControl.isPlaying) {
|
||||
if (reactionSettings.autoPause.value) {
|
||||
mediaControl.sendPause()
|
||||
} else {
|
||||
log(TAG) { "autoPause is disabled" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.setupCommonEventHandlers(TAG) { "monitor" }
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Reactions", "PlayPause")
|
||||
private val TAG = logTag("Reaction", "PlayPause")
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,15 @@ import android.view.View
|
||||
import androidx.annotation.Keep
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.preference.CheckBoxPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.uix.PreferenceFragment2
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
import eu.darken.capod.main.core.MonitorMode
|
||||
import eu.darken.capod.main.ui.settings.general.DeviceSelectionDialogFactory
|
||||
import eu.darken.capod.reaction.core.ReactionSettings
|
||||
import eu.darken.capod.reaction.core.autoconnect.AutoConnectCondition
|
||||
import javax.inject.Inject
|
||||
@@ -19,6 +24,7 @@ class ReactionSettingsFragment : PreferenceFragment2() {
|
||||
|
||||
private val vm: ReactionSettingsFragmentVM by viewModels()
|
||||
|
||||
@Inject lateinit var generalSettings: GeneralSettings
|
||||
@Inject lateinit var reactionSettings: ReactionSettings
|
||||
|
||||
override val settings: ReactionSettings
|
||||
@@ -37,10 +43,30 @@ class ReactionSettingsFragment : PreferenceFragment2() {
|
||||
super.onPreferencesCreated()
|
||||
}
|
||||
|
||||
override fun onPreferenceTreeClick(preference: Preference): Boolean {
|
||||
if (preference.key == reactionSettings.autoConnect.key && generalSettings.mainDeviceAddress.value == null) {
|
||||
preference as CheckBoxPreference
|
||||
|
||||
val devices = vm.bondedDevices
|
||||
DeviceSelectionDialogFactory(requireContext()).create(
|
||||
devices = devices,
|
||||
current = devices.firstOrNull { it.address == generalSettings.mainDeviceAddress.value }
|
||||
) { selected ->
|
||||
generalSettings.mainDeviceAddress.value = selected?.address
|
||||
if (selected != null) preference.isChecked = true
|
||||
}.show()
|
||||
preference.isChecked = false
|
||||
return true
|
||||
}
|
||||
return super.onPreferenceTreeClick(preference)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
settings.autoConnect.flow.asLiveData().observe2 {
|
||||
generalSettings.monitorMode.value = MonitorMode.ALWAYS
|
||||
autoConnectConditionPref.isEnabled = it
|
||||
}
|
||||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package eu.darken.capod.reaction.ui.settings
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.uix.ViewModel3
|
||||
@@ -11,8 +12,10 @@ import javax.inject.Inject
|
||||
class ReactionSettingsFragmentVM @Inject constructor(
|
||||
private val handle: SavedStateHandle,
|
||||
private val dispatcherProvider: DispatcherProvider,
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
) : ViewModel3(dispatcherProvider) {
|
||||
|
||||
val bondedDevices = bluetoothManager.bondedDevices().toList()
|
||||
|
||||
companion object {
|
||||
private val TAG = logTag("Settings", "Reaction", "VM")
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<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"
|
||||
android:autoMirrored="true">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M14.24,12.01l2.32,2.32c0.28,-0.72 0.44,-1.51 0.44,-2.33 0,-0.82 -0.16,-1.59 -0.43,-2.31l-2.33,2.32zM19.53,6.71l-1.26,1.26c0.63,1.21 0.98,2.57 0.98,4.02s-0.36,2.82 -0.98,4.02l1.2,1.2c0.97,-1.54 1.54,-3.36 1.54,-5.31 -0.01,-1.89 -0.55,-3.67 -1.48,-5.19zM15.71,7.71L10,2L9,2v7.59L4.41,5 3,6.41 8.59,12 3,17.59 4.41,19 9,14.41L9,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM11,5.83l1.88,1.88L11,9.59L11,5.83zM12.88,16.29L11,18.17v-3.76l1.88,1.88z" />
|
||||
</vector>
|
||||
@@ -61,7 +61,7 @@
|
||||
<string name="pods_connection_state_hanging_up_label">Hanging up</string>
|
||||
<string name="pods_unknown_raw_data_label">Raw data</string>
|
||||
<string name="pods_unknown_label">Unknown device</string>
|
||||
<string name="pods_unknown_contact_dev">This is an unknown device, but it is using Apple\'s message format. Let\'s add support for it, contact me :).</string>
|
||||
<string name="pods_unknown_contact_dev">This is an unknown device, but it is using similar message format. Let\'s add support for it, contact me :).</string>
|
||||
<string name="pods_none_label_short">No device</string>
|
||||
<string name="pods_none_label">No paired device connected.</string>
|
||||
<string name="pods_none_description">Connect a paired device or enable the \'Show all\' option.</string>
|
||||
@@ -103,11 +103,11 @@
|
||||
<string name="settings_scanner_mode_balanced_label">Balanced</string>
|
||||
<string name="settings_scanner_mode_lowlatency_label">Low latency</string>
|
||||
<string name="settings_autopause_label">Auto pause</string>
|
||||
<string name="settings_autopause_description">Pause music when removing the device from your ear (if supported).</string>
|
||||
<string name="settings_autopause_description">Pause music when removing the device from your ear.</string>
|
||||
<string name="settings_showall_label">Show all devices</string>
|
||||
<string name="settings_showall_description">Show other people\'s devices that are near you.</string>
|
||||
<string name="settings_autopplay_label">Auto play</string>
|
||||
<string name="settings_autoplay_description">Start music playback music when wearing the device (if supported).</string>
|
||||
<string name="settings_autoplay_description">Start music playback music when wearing the device.</string>
|
||||
<string name="settings_fake_data_label">Fake data</string>
|
||||
<string name="settings_fake_data_description">Show fake data, i.e. simulate device that don\'t exist.</string>
|
||||
<string name="settings_debug_label">Debug settings</string>
|
||||
@@ -118,10 +118,16 @@
|
||||
<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_description">If Android does not automatically connect, we can ask it too. This will set the monitor mode setting to \'Always\'.</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_autoconnect_condition_description">When should we try to 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>
|
||||
<string name="settings_maindevice_address_label">Main device</string>
|
||||
<string name="settings_maindevice_address_description">The paired pod device to which this app should react.</string>
|
||||
<string name="settings_maindevice_address_none">None</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">When seen</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Case is open</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">In ear</string>
|
||||
</resources>
|
||||
@@ -29,11 +29,12 @@
|
||||
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" />
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/ic_baseline_bluetooth_searching_24"
|
||||
android:key="core.maindevice.address"
|
||||
android:summary="@string/settings_maindevice_address_description"
|
||||
android:title="@string/settings_maindevice_address_label" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/settings_category_other_label">
|
||||
|
||||
Reference in New Issue
Block a user