From 6abaeaed37cffd4e2575373c497bea3a35107e5f Mon Sep 17 00:00:00 2001 From: darken Date: Fri, 14 Jan 2022 09:54:27 +0100 Subject: [PATCH] Autoconnection wip2 --- .../common/bluetooth/BluetoothDevice2.kt | 17 ------ .../common/bluetooth/BluetoothManager2.kt | 17 +++--- .../common/bluetooth/BluetoothProfile2.kt | 5 +- .../capod/common/uix/PreferenceFragment2.kt | 15 ++++++ .../darken/capod/main/core/GeneralSettings.kt | 17 ++---- .../eu/darken/capod/main/core/MonitorMode.kt | 4 -- .../monitor/core/worker/MonitorWorker.kt | 6 +-- .../core/ReactionHub.kt} | 10 ++-- .../capod/reaction/core/ReactionSettings.kt | 51 ++++++++++++++++++ .../reaction/core/autoconnect/AutoConnect.kt | 53 +++++++++++++++++++ .../core/autoconnect/AutoConnectCondition.kt | 25 +++++++++ .../core/playpause}/PlayPause.kt | 12 ++--- .../ui/settings/ReactionSettingsFragment.kt | 47 ++++++++++++++++ .../ui/settings/ReactionSettingsFragmentVM.kt | 20 +++++++ .../capod/reactions/core/AutoConnect.kt | 26 --------- .../ic_baseline_bluetooth_connected_24.xml | 10 ++++ .../res/drawable/ic_baseline_widgets_24.xml | 10 ++++ .../drawable/ic_baseline_workspaces_24.xml | 10 ++++ app/src/main/res/values/strings.xml | 7 +++ app/src/main/res/xml/preferences_general.xml | 34 ++++++------ app/src/main/res/xml/preferences_index.xml | 6 +++ .../main/res/xml/preferences_reactions.xml | 28 ++++++++++ 22 files changed, 329 insertions(+), 101 deletions(-) delete mode 100644 app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothDevice2.kt rename app/src/main/java/eu/darken/capod/{reactions/core/ReactionsHub.kt => reaction/core/ReactionHub.kt} (54%) create mode 100644 app/src/main/java/eu/darken/capod/reaction/core/ReactionSettings.kt create mode 100644 app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnect.kt create mode 100644 app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnectCondition.kt rename app/src/main/java/eu/darken/capod/{reactions/core => reaction/core/playpause}/PlayPause.kt (81%) create mode 100644 app/src/main/java/eu/darken/capod/reaction/ui/settings/ReactionSettingsFragment.kt create mode 100644 app/src/main/java/eu/darken/capod/reaction/ui/settings/ReactionSettingsFragmentVM.kt delete mode 100644 app/src/main/java/eu/darken/capod/reactions/core/AutoConnect.kt create mode 100644 app/src/main/res/drawable/ic_baseline_bluetooth_connected_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_widgets_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_workspaces_24.xml create mode 100644 app/src/main/res/xml/preferences_reactions.xml diff --git a/app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothDevice2.kt b/app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothDevice2.kt deleted file mode 100644 index 6517fc0f..00000000 --- a/app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothDevice2.kt +++ /dev/null @@ -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) -} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothManager2.kt b/app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothManager2.kt index fc35efeb..94faf9ed 100644 --- a/app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothManager2.kt +++ b/app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothManager2.kt @@ -96,7 +96,7 @@ class BluetoothManager2 @Inject constructor( } } - private fun connectedDevicesForProfile(profile: Int): Flow> = callbackFlow { + private fun connectedDevicesForProfile(profile: Int): Flow> = 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.EXTRA_DEVICE)?.let { - BluetoothDevice2(it) - } + val device = intent.getParcelableExtra(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> = isBluetoothEnabled + fun connectedDevices(): Flow> = isBluetoothEnabled .flatMapLatest { connectedDevicesForProfile(BluetoothProfile.HEADSET) } .map { devices -> devices.filter { device -> @@ -160,14 +158,15 @@ class BluetoothManager2 @Inject constructor( } } - fun bondedDevices(): List = adapter.bondedDevices - .map { BluetoothDevice2(it) } + fun bondedDevices(): Set = 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()}" } diff --git a/app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothProfile2.kt b/app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothProfile2.kt index 63d539fa..05f8326b 100644 --- a/app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothProfile2.kt +++ b/app/src/main/java/eu/darken/capod/common/bluetooth/BluetoothProfile2.kt @@ -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 - get() = profile.connectedDevices.map { BluetoothDevice2(it) }.toSet() + val connectedDevices: Set + get() = profile.connectedDevices.toSet() val isConnected: Boolean get() = isConnectedAtomic.get() diff --git a/app/src/main/java/eu/darken/capod/common/uix/PreferenceFragment2.kt b/app/src/main/java/eu/darken/capod/common/uix/PreferenceFragment2.kt index 06aafd4c..e8a192da 100644 --- a/app/src/main/java/eu/darken/capod/common/uix/PreferenceFragment2.kt +++ b/app/src/main/java/eu/darken/capod/common/uix/PreferenceFragment2.kt @@ -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 LiveData.observe2( + crossinline callback: (T) -> Unit + ) { + observe(viewLifecycleOwner) { callback.invoke(it) } + } + + inline fun LiveData.observe2( + ui: VB, + crossinline callback: VB.(T) -> Unit + ) { + observe(viewLifecycleOwner) { callback.invoke(ui, it) } + } } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt b/app/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt index 9cb94096..9b581764 100644 --- a/app/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt +++ b/app/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt @@ -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( + "core.maindevice.address", + null + ) + override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper( monitorMode, scannerMode, - autoPause, - autoPlay, showAll, minimumSignalQuality, debugSettings.isAutoReportEnabled, diff --git a/app/src/main/java/eu/darken/capod/main/core/MonitorMode.kt b/app/src/main/java/eu/darken/capod/main/core/MonitorMode.kt index 67bd7798..2ee4fc9c 100644 --- a/app/src/main/java/eu/darken/capod/main/core/MonitorMode.kt +++ b/app/src/main/java/eu/darken/capod/main/core/MonitorMode.kt @@ -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 ), } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/monitor/core/worker/MonitorWorker.kt b/app/src/main/java/eu/darken/capod/monitor/core/worker/MonitorWorker.kt index 1b5b47af..2f01bf9d 100644 --- a/app/src/main/java/eu/darken/capod/monitor/core/worker/MonitorWorker.kt +++ b/app/src/main/java/eu/darken/capod/monitor/core/worker/MonitorWorker.kt @@ -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()}" } } diff --git a/app/src/main/java/eu/darken/capod/reactions/core/ReactionsHub.kt b/app/src/main/java/eu/darken/capod/reaction/core/ReactionHub.kt similarity index 54% rename from app/src/main/java/eu/darken/capod/reactions/core/ReactionsHub.kt rename to app/src/main/java/eu/darken/capod/reaction/core/ReactionHub.kt index f19486d4..f1ae1f32 100644 --- a/app/src/main/java/eu/darken/capod/reactions/core/ReactionsHub.kt +++ b/app/src/main/java/eu/darken/capod/reaction/core/ReactionHub.kt @@ -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") } } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/reaction/core/ReactionSettings.kt b/app/src/main/java/eu/darken/capod/reaction/core/ReactionSettings.kt new file mode 100644 index 00000000..eed788b1 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/reaction/core/ReactionSettings.kt @@ -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, + ) +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnect.kt b/app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnect.kt new file mode 100644 index 00000000..6727f246 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnect.kt @@ -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 = 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") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnectCondition.kt b/app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnectCondition.kt new file mode 100644 index 00000000..53b8ed2c --- /dev/null +++ b/app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnectCondition.kt @@ -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 + ), +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/reactions/core/PlayPause.kt b/app/src/main/java/eu/darken/capod/reaction/core/playpause/PlayPause.kt similarity index 81% rename from app/src/main/java/eu/darken/capod/reactions/core/PlayPause.kt rename to app/src/main/java/eu/darken/capod/reaction/core/playpause/PlayPause.kt index 0ea12406..680200f2 100644 --- a/app/src/main/java/eu/darken/capod/reactions/core/PlayPause.kt +++ b/app/src/main/java/eu/darken/capod/reaction/core/playpause/PlayPause.kt @@ -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() } } diff --git a/app/src/main/java/eu/darken/capod/reaction/ui/settings/ReactionSettingsFragment.kt b/app/src/main/java/eu/darken/capod/reaction/ui/settings/ReactionSettingsFragment.kt new file mode 100644 index 00000000..cccd01cc --- /dev/null +++ b/app/src/main/java/eu/darken/capod/reaction/ui/settings/ReactionSettingsFragment.kt @@ -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(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) + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/reaction/ui/settings/ReactionSettingsFragmentVM.kt b/app/src/main/java/eu/darken/capod/reaction/ui/settings/ReactionSettingsFragmentVM.kt new file mode 100644 index 00000000..e18e39be --- /dev/null +++ b/app/src/main/java/eu/darken/capod/reaction/ui/settings/ReactionSettingsFragmentVM.kt @@ -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") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/reactions/core/AutoConnect.kt b/app/src/main/java/eu/darken/capod/reactions/core/AutoConnect.kt deleted file mode 100644 index 7e816235..00000000 --- a/app/src/main/java/eu/darken/capod/reactions/core/AutoConnect.kt +++ /dev/null @@ -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 = podMonitor.mainDevice - .map { - val podDevice = bluetoothManager2.bondedDevices().firstOrNull { it.name?.contains("AirPod") ?: false } - bluetoothManager2.nudgeConnection(podDevice!!) - } - - companion object { - private val TAG = logTag("Reactions", "AutoConnect") - } -} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_bluetooth_connected_24.xml b/app/src/main/res/drawable/ic_baseline_bluetooth_connected_24.xml new file mode 100644 index 00000000..090786e8 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_bluetooth_connected_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_widgets_24.xml b/app/src/main/res/drawable/ic_baseline_widgets_24.xml new file mode 100644 index 00000000..377cd039 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_widgets_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_workspaces_24.xml b/app/src/main/res/drawable/ic_baseline_workspaces_24.xml new file mode 100644 index 00000000..6dc0fb68 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_workspaces_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2f65a52a..41960017 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -117,4 +117,11 @@ Microphone Minimum signal quality The minimum signal quality that a device needs to have to be considered yours. + Auto connect + Seeing the device and being connected is not the same. If Android does not automatically connect, we can ask it too. + Auto connect condition + When should we auto connect to your device? + Reactions + React to events and behaviors. + Your device \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_general.xml b/app/src/main/res/xml/preferences_general.xml index ca385f7b..2378c6d1 100644 --- a/app/src/main/res/xml/preferences_general.xml +++ b/app/src/main/res/xml/preferences_general.xml @@ -14,31 +14,27 @@ android:summary="@string/settings_scanner_mode_description" android:title="@string/settings_scanner_mode_label" /> - - - - - + + + + + diff --git a/app/src/main/res/xml/preferences_index.xml b/app/src/main/res/xml/preferences_index.xml index e6eca260..a3467367 100644 --- a/app/src/main/res/xml/preferences_index.xml +++ b/app/src/main/res/xml/preferences_index.xml @@ -7,6 +7,12 @@ app:summary="@string/settings_general_description" app:title="@string/settings_general_label" /> + + + + + + + + + + + + + \ No newline at end of file