diff --git a/app/src/main/java/eu/darken/capod/pods/core/HasDualEarDetection.kt b/app/src/main/java/eu/darken/capod/pods/core/HasDualEarDetection.kt
new file mode 100644
index 00000000..e609c755
--- /dev/null
+++ b/app/src/main/java/eu/darken/capod/pods/core/HasDualEarDetection.kt
@@ -0,0 +1,15 @@
+package eu.darken.capod.pods.core
+
+interface HasDualEarDetection : HasEarDetection {
+
+ val isLeftPodInEar: Boolean
+
+ val isRightPodInEar: Boolean
+
+ val isEitherPodInEar: Boolean
+ get() = isLeftPodInEar || isRightPodInEar
+
+ override val isBeingWorn: Boolean
+ get() = isLeftPodInEar && isRightPodInEar
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt
index 4a2d4de1..8c5be778 100644
--- a/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt
+++ b/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt
@@ -8,11 +8,11 @@ import eu.darken.capod.common.isBitSet
import eu.darken.capod.common.lowerNibble
import eu.darken.capod.common.upperNibble
import eu.darken.capod.pods.core.HasCase
+import eu.darken.capod.pods.core.HasDualEarDetection
import eu.darken.capod.pods.core.HasDualPods
import eu.darken.capod.pods.core.HasDualPods.Pod
-import eu.darken.capod.pods.core.HasEarDetection
-interface DualApplePods : ApplePods, HasDualPods, HasEarDetection, HasCase {
+interface DualApplePods : ApplePods, HasDualPods, HasDualEarDetection, HasCase {
val microPhonePod: Pod
get() = when (rawStatus.isBitSet(5)) {
@@ -53,22 +53,18 @@ interface DualApplePods : ApplePods, HasDualPods, HasEarDetection, HasCase {
}
}
}
-
- val isLeftPodInEar: Boolean
+ override val isLeftPodInEar: Boolean
get() = when (microPhonePod) {
Pod.LEFT -> rawStatus.isBitSet(1)
Pod.RIGHT -> rawStatus.isBitSet(3)
}
- val isRightPodInEar: Boolean
+ override val isRightPodInEar: Boolean
get() = when (microPhonePod) {
Pod.LEFT -> rawStatus.isBitSet(3)
Pod.RIGHT -> rawStatus.isBitSet(1)
}
- override val isBeingWorn: Boolean
- get() = isLeftPodInEar && isRightPodInEar
-
val isLeftPodCharging: Boolean
get() = when (microPhonePod) {
Pod.LEFT -> rawCaseBattery.upperNibble.isBitSet(0)
diff --git a/app/src/main/java/eu/darken/capod/reaction/autoconnect/AutoConnect.kt b/app/src/main/java/eu/darken/capod/reaction/autoconnect/AutoConnect.kt
index bf3016ff..580744ca 100644
--- a/app/src/main/java/eu/darken/capod/reaction/autoconnect/AutoConnect.kt
+++ b/app/src/main/java/eu/darken/capod/reaction/autoconnect/AutoConnect.kt
@@ -8,6 +8,7 @@ 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.HasDualEarDetection
import eu.darken.capod.pods.core.HasEarDetection
import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.reaction.settings.ReactionSettings
@@ -71,7 +72,13 @@ class AutoConnect @Inject constructor(
else -> true
}
AutoConnectCondition.IN_EAR -> when (mainDevice) {
- is HasEarDetection -> mainDevice.isBeingWorn
+ is HasEarDetection -> {
+ if (mainDevice is HasDualEarDetection && reactionSettings.onePodMode.value) {
+ mainDevice.isEitherPodInEar
+ } else {
+ mainDevice.isBeingWorn
+ }
+ }
else -> true
}
}
diff --git a/app/src/main/java/eu/darken/capod/reaction/playpause/PlayPause.kt b/app/src/main/java/eu/darken/capod/reaction/playpause/PlayPause.kt
index 75d63382..bcdab882 100644
--- a/app/src/main/java/eu/darken/capod/reaction/playpause/PlayPause.kt
+++ b/app/src/main/java/eu/darken/capod/reaction/playpause/PlayPause.kt
@@ -9,6 +9,7 @@ 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.monitor.core.PodMonitor
+import eu.darken.capod.pods.core.HasDualEarDetection
import eu.darken.capod.pods.core.HasEarDetection
import eu.darken.capod.reaction.settings.ReactionSettings
import kotlinx.coroutines.flow.*
@@ -25,7 +26,8 @@ class PlayPause @Inject constructor(
fun monitor() = combine(
reactionSettings.autoPlay.flow,
reactionSettings.autoPause.flow,
- ) { play, pause -> play || pause }
+ reactionSettings.onePodMode.flow,
+ ) { play, pause, _ -> play || pause }
.flatMapLatest { if (it) bluetoothManager.connectedDevices() else emptyFlow() }
.flatMapLatest {
if (it.isEmpty()) {
@@ -42,7 +44,28 @@ class PlayPause @Inject constructor(
if (previous is HasEarDetection && current is HasEarDetection) {
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 (previous.identifier != current.identifier) {
+ return@onEach
+ }
+
+ if (previous is HasDualEarDetection && current is HasDualEarDetection && reactionSettings.onePodMode.value) {
+ log(TAG) { "Ear status changed for dual pod device in single pod mode." }
+ val previousWorn = previous.isEitherPodInEar
+ val currentWorn = current.isEitherPodInEar
+ if (!previousWorn && currentWorn && !mediaControl.isPlaying) {
+ if (reactionSettings.autoPlay.value) {
+ mediaControl.sendPlay()
+ } else {
+ log(TAG) { "autoPlay is disabled" }
+ }
+ } else if (!currentWorn && previousWorn && mediaControl.isPlaying) {
+ if (reactionSettings.autoPause.value) {
+ mediaControl.sendPause()
+ } else {
+ log(TAG) { "autoPause is disabled" }
+ }
+ }
+ } else if (previous.isBeingWorn != current.isBeingWorn) {
log(TAG) { "Ear status changed for monitored device." }
if (current.isBeingWorn && !mediaControl.isPlaying) {
if (reactionSettings.autoPlay.value) {
diff --git a/app/src/main/java/eu/darken/capod/reaction/settings/ReactionSettings.kt b/app/src/main/java/eu/darken/capod/reaction/settings/ReactionSettings.kt
index 63e3c5f9..e19dcbb2 100644
--- a/app/src/main/java/eu/darken/capod/reaction/settings/ReactionSettings.kt
+++ b/app/src/main/java/eu/darken/capod/reaction/settings/ReactionSettings.kt
@@ -47,11 +47,17 @@ class ReactionSettings @Inject constructor(
false
)
+ val onePodMode = preferences.createFlowPreference(
+ "reaction.onepod.enabled",
+ false
+ )
+
override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper(
autoPause,
autoPlay,
autoConnect,
autoConnectCondition,
showPopUpOnCaseOpen,
+ onePodMode,
)
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_baseline_looks_one_24.xml b/app/src/main/res/drawable/ic_baseline_looks_one_24.xml
new file mode 100644
index 00000000..1345585c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_looks_one_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 743afb83..04e306d8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -132,4 +132,6 @@
Bluetooth is disabled, enable it ;).
Translation
Help translate this into your favorite language.
+ One pod mode
+ Wearing both pods is not required, wearing a single pod is sufficient to trigger reactions.
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences_reactions.xml b/app/src/main/res/xml/preferences_reactions.xml
index af537ce6..171e0774 100644
--- a/app/src/main/res/xml/preferences_reactions.xml
+++ b/app/src/main/res/xml/preferences_reactions.xml
@@ -1,6 +1,13 @@
+
+
+