mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a3c2b3d23 | ||
|
|
7cccff8a71 | ||
|
|
4a3e5056b6 | ||
|
|
76c1b65b89 | ||
|
|
d7902bc86e |
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 332 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 760 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 761 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 605 KiB |
@@ -5,7 +5,9 @@
|
|||||||
|
|
||||||
A companion app that adds support for AirPod specific features to Android.
|
A companion app that adds support for AirPod specific features to Android.
|
||||||
|
|
||||||
Supported models:
|
<img src="https://github.com/d4rken/capod/raw/master/.assets/banner.png" width="400">
|
||||||
|
|
||||||
|
Currently supported models:
|
||||||
|
|
||||||
* AirPods Gen1
|
* AirPods Gen1
|
||||||
* AirPods Gen2
|
* AirPods Gen2
|
||||||
@@ -21,8 +23,8 @@ Supported models:
|
|||||||
## Support the project
|
## Support the project
|
||||||
|
|
||||||
* Buy the CAPod Pro In-App purchase on [Google Play](https://play.google.com/store/apps/details?id=eu.darken.capod)
|
* Buy the CAPod Pro In-App purchase on [Google Play](https://play.google.com/store/apps/details?id=eu.darken.capod)
|
||||||
* [Buy me a coffee](https://www.buymeacoffee.com/tydarken)
|
|
||||||
* Help translate CAPod [on Crowdin](https://crowdin.com/project/airpod-companion)
|
* Help translate CAPod [on Crowdin](https://crowdin.com/project/airpod-companion)
|
||||||
|
* [Buy me a coffee](https://www.buymeacoffee.com/tydarken)
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
@@ -36,6 +38,11 @@ Supported models:
|
|||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
|
<img src="https://github.com/d4rken/capod/raw/master/.assets/screenshots/1.png" width="200">
|
||||||
|
<img src="https://github.com/d4rken/capod/raw/master/.assets/screenshots/2.png" width="200">
|
||||||
|
<img src="https://github.com/d4rken/capod/raw/master/.assets/screenshots/3.png" width="200">
|
||||||
|
<img src="https://github.com/d4rken/capod/raw/master/.assets/screenshots/4.png" width="200">
|
||||||
|
|
||||||
## Thanks to
|
## Thanks to
|
||||||
|
|
||||||
* The [OpenPods](https://github.com/adolfintel/OpenPods) project,
|
* The [OpenPods](https://github.com/adolfintel/OpenPods) project,
|
||||||
@@ -49,8 +56,9 @@ Supported models:
|
|||||||
|
|
||||||
CAPod's code is available under a GPL v3 license, this excludes:
|
CAPod's code is available under a GPL v3 license, this excludes:
|
||||||
|
|
||||||
* CAPod icons, logos, mascots and marketing materials.
|
* CAPod icons, logos, mascots and marketing materials/assets.
|
||||||
* CAPod animations and videos.
|
* CAPod animations and videos.
|
||||||
* CAPod documentation.
|
* CAPod documentation.
|
||||||
* Google Play screenshots.
|
* Google Play screenshots.
|
||||||
* Google Play texts & descriptions.
|
* Google Play texts & descriptions.
|
||||||
|
* Translations.
|
||||||
|
|||||||
+5
-5
@@ -14,10 +14,7 @@ import eu.darken.capod.common.flow.setupCommonEventHandlers
|
|||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
|
||||||
import kotlinx.coroutines.flow.retryWhen
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
@@ -112,8 +109,11 @@ class BillingClientConnectionProvider @Inject constructor(
|
|||||||
delay(3000 * attempt)
|
delay(3000 * attempt)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
.catch {
|
||||||
|
log(TAG, ERROR) { "Unable to provide client connection:\n${it.asLog()}" }
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG: String = logTag("Upgrade", "Gplay", "Billing", "ClientProvider")
|
val TAG: String = logTag("Upgrade", "Gplay", "Billing", "Client", "ConnectionProvider")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,43 @@
|
|||||||
package eu.darken.capod.main.core
|
package eu.darken.capod.main.core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dagger.Reusable
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import eu.darken.capod.common.debug.logging.log
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
import eu.darken.capod.common.permissions.Permission
|
import eu.darken.capod.common.permissions.Permission
|
||||||
import eu.darken.capod.common.permissions.isRequired
|
import eu.darken.capod.common.permissions.isRequired
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Reusable
|
@Singleton
|
||||||
class PermissionTool @Inject constructor(
|
class PermissionTool @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val generalSettings: GeneralSettings,
|
private val generalSettings: GeneralSettings,
|
||||||
) {
|
) {
|
||||||
|
private val permissionCheckTrigger = MutableStateFlow(UUID.randomUUID())
|
||||||
|
|
||||||
suspend fun missingPermissions(): Set<Permission> = Permission.values()
|
fun recheck() {
|
||||||
.filter { it != Permission.IGNORE_BATTERY_OPTIMIZATION || generalSettings.monitorMode.value == MonitorMode.ALWAYS }
|
log(TAG) { "recheck()" }
|
||||||
.filter { it.isRequired(context) }
|
permissionCheckTrigger.value = UUID.randomUUID()
|
||||||
.toSet()
|
}
|
||||||
|
|
||||||
|
val missingPermissions: Flow<Set<Permission>> = combine(
|
||||||
|
permissionCheckTrigger,
|
||||||
|
generalSettings.monitorMode.flow,
|
||||||
|
) { _, monitorMode ->
|
||||||
|
Permission.values()
|
||||||
|
.filter { it != Permission.IGNORE_BATTERY_OPTIMIZATION || monitorMode == MonitorMode.ALWAYS }
|
||||||
|
.filter { it.isRequired(context) }
|
||||||
|
.toSet()
|
||||||
|
}
|
||||||
|
.onEach { log(TAG) { "Missing permission: $it" } }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = logTag("PermissionTool")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,8 +11,8 @@ import eu.darken.capod.common.lists.differ.setupDiffer
|
|||||||
import eu.darken.capod.common.lists.modular.ModularAdapter
|
import eu.darken.capod.common.lists.modular.ModularAdapter
|
||||||
import eu.darken.capod.common.lists.modular.mods.DataBinderMod
|
import eu.darken.capod.common.lists.modular.mods.DataBinderMod
|
||||||
import eu.darken.capod.common.lists.modular.mods.TypedVHCreatorMod
|
import eu.darken.capod.common.lists.modular.mods.TypedVHCreatorMod
|
||||||
|
import eu.darken.capod.main.ui.overview.cards.BluetoothDisabledVH
|
||||||
import eu.darken.capod.main.ui.overview.cards.MissingMainDeviceVH
|
import eu.darken.capod.main.ui.overview.cards.MissingMainDeviceVH
|
||||||
import eu.darken.capod.main.ui.overview.cards.NoPairedDeviceCardVH
|
|
||||||
import eu.darken.capod.main.ui.overview.cards.PermissionCardVH
|
import eu.darken.capod.main.ui.overview.cards.PermissionCardVH
|
||||||
import eu.darken.capod.main.ui.overview.cards.pods.BasicSingleApplePodsCardVH
|
import eu.darken.capod.main.ui.overview.cards.pods.BasicSingleApplePodsCardVH
|
||||||
import eu.darken.capod.main.ui.overview.cards.pods.DualApplePodsCardVH
|
import eu.darken.capod.main.ui.overview.cards.pods.DualApplePodsCardVH
|
||||||
@@ -33,8 +33,8 @@ class OverviewAdapter @Inject constructor() :
|
|||||||
modules.add(TypedVHCreatorMod({ data[it] is SingleApplePodsCardVH.Item }) { SingleApplePodsCardVH(it) })
|
modules.add(TypedVHCreatorMod({ data[it] is SingleApplePodsCardVH.Item }) { SingleApplePodsCardVH(it) })
|
||||||
modules.add(TypedVHCreatorMod({ data[it] is BasicSingleApplePodsCardVH.Item }) { BasicSingleApplePodsCardVH(it) })
|
modules.add(TypedVHCreatorMod({ data[it] is BasicSingleApplePodsCardVH.Item }) { BasicSingleApplePodsCardVH(it) })
|
||||||
modules.add(TypedVHCreatorMod({ data[it] is UnknownPodDeviceCardVH.Item }) { UnknownPodDeviceCardVH(it) })
|
modules.add(TypedVHCreatorMod({ data[it] is UnknownPodDeviceCardVH.Item }) { UnknownPodDeviceCardVH(it) })
|
||||||
modules.add(TypedVHCreatorMod({ data[it] is NoPairedDeviceCardVH.Item }) { NoPairedDeviceCardVH(it) })
|
|
||||||
modules.add(TypedVHCreatorMod({ data[it] is MissingMainDeviceVH.Item }) { MissingMainDeviceVH(it) })
|
modules.add(TypedVHCreatorMod({ data[it] is MissingMainDeviceVH.Item }) { MissingMainDeviceVH(it) })
|
||||||
|
modules.add(TypedVHCreatorMod({ data[it] is BluetoothDisabledVH.Item }) { BluetoothDisabledVH(it) })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = data.size
|
override fun getItemCount(): Int = data.size
|
||||||
|
|||||||
@@ -70,8 +70,10 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.listItems.observe2(ui) {
|
vm.listItems.observe2(ui) { adapter.update(it) }
|
||||||
adapter.update(it)
|
|
||||||
|
vm.workerAutolaunch.observe2 {
|
||||||
|
// While UI is active, subscribe to the autolaunch routine
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.requestPermissionEvent.observe2(ui) {
|
vm.requestPermissionEvent.observe2(ui) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import eu.darken.capod.common.bluetooth.BluetoothManager2
|
|||||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||||
import eu.darken.capod.common.debug.autoreport.DebugSettings
|
import eu.darken.capod.common.debug.autoreport.DebugSettings
|
||||||
import eu.darken.capod.common.debug.logging.log
|
import eu.darken.capod.common.debug.logging.log
|
||||||
|
import eu.darken.capod.common.flow.combine
|
||||||
import eu.darken.capod.common.livedata.SingleLiveEvent
|
import eu.darken.capod.common.livedata.SingleLiveEvent
|
||||||
import eu.darken.capod.common.navigation.navVia
|
import eu.darken.capod.common.navigation.navVia
|
||||||
import eu.darken.capod.common.permissions.Permission
|
import eu.darken.capod.common.permissions.Permission
|
||||||
@@ -16,8 +17,8 @@ import eu.darken.capod.common.upgrade.UpgradeRepo
|
|||||||
import eu.darken.capod.main.core.GeneralSettings
|
import eu.darken.capod.main.core.GeneralSettings
|
||||||
import eu.darken.capod.main.core.MonitorMode
|
import eu.darken.capod.main.core.MonitorMode
|
||||||
import eu.darken.capod.main.core.PermissionTool
|
import eu.darken.capod.main.core.PermissionTool
|
||||||
|
import eu.darken.capod.main.ui.overview.cards.BluetoothDisabledVH
|
||||||
import eu.darken.capod.main.ui.overview.cards.MissingMainDeviceVH
|
import eu.darken.capod.main.ui.overview.cards.MissingMainDeviceVH
|
||||||
import eu.darken.capod.main.ui.overview.cards.NoPairedDeviceCardVH
|
|
||||||
import eu.darken.capod.main.ui.overview.cards.PermissionCardVH
|
import eu.darken.capod.main.ui.overview.cards.PermissionCardVH
|
||||||
import eu.darken.capod.main.ui.overview.cards.pods.*
|
import eu.darken.capod.main.ui.overview.cards.pods.*
|
||||||
import eu.darken.capod.monitor.core.PodMonitor
|
import eu.darken.capod.monitor.core.PodMonitor
|
||||||
@@ -26,7 +27,6 @@ import eu.darken.capod.pods.core.PodDevice
|
|||||||
import eu.darken.capod.pods.core.apple.BasicSingleApplePods
|
import eu.darken.capod.pods.core.apple.BasicSingleApplePods
|
||||||
import eu.darken.capod.pods.core.apple.DualApplePods
|
import eu.darken.capod.pods.core.apple.DualApplePods
|
||||||
import eu.darken.capod.pods.core.apple.SingleApplePods
|
import eu.darken.capod.pods.core.apple.SingleApplePods
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
@@ -57,9 +57,7 @@ class OverviewFragmentVM @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val permissionCheckTrigger = MutableStateFlow(UUID.randomUUID())
|
val workerAutolaunch: LiveData<Unit> = permissionTool.missingPermissions
|
||||||
private val requiredPermissions: Flow<Collection<Permission>> = permissionCheckTrigger
|
|
||||||
.map { permissionTool.missingPermissions() }
|
|
||||||
.onEach {
|
.onEach {
|
||||||
if (it.isNotEmpty()) {
|
if (it.isNotEmpty()) {
|
||||||
log(TAG) { "Missing permissions: $it" }
|
log(TAG) { "Missing permissions: $it" }
|
||||||
@@ -76,26 +74,24 @@ class OverviewFragmentVM @Inject constructor(
|
|||||||
monitorControl.startMonitor()
|
monitorControl.startMonitor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.map { Unit }
|
||||||
|
.asLiveData2()
|
||||||
|
|
||||||
val requestPermissionEvent = SingleLiveEvent<Permission>()
|
val requestPermissionEvent = SingleLiveEvent<Permission>()
|
||||||
|
|
||||||
private val pods: Flow<List<PodDevice>> = requiredPermissions
|
private val pods: Flow<List<PodDevice>> = permissionTool.missingPermissions
|
||||||
.flatMapLatest { permissions ->
|
.flatMapLatest { permissions ->
|
||||||
if (permissions.isEmpty()) {
|
if (permissions.isNotEmpty()) {
|
||||||
generalSettings.showAll.flow
|
return@flatMapLatest flowOf(emptyList())
|
||||||
.flatMapLatest { showAll ->
|
}
|
||||||
if (showAll) {
|
|
||||||
podMonitor.devices
|
generalSettings.showAll.flow.flatMapLatest { showAll ->
|
||||||
} else {
|
if (showAll) {
|
||||||
podMonitor.mainDevice.map { mainDevice ->
|
podMonitor.devices
|
||||||
mainDevice?.let { listOf(it) } ?: emptyList()
|
} else {
|
||||||
}
|
podMonitor.mainDevice.map { mainDevice ->
|
||||||
}
|
mainDevice?.let { listOf(it) } ?: emptyList()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
channelFlow {
|
|
||||||
send(emptyList())
|
|
||||||
awaitClose()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,11 +99,12 @@ class OverviewFragmentVM @Inject constructor(
|
|||||||
|
|
||||||
val listItems: LiveData<List<OverviewAdapter.Item>> = combine(
|
val listItems: LiveData<List<OverviewAdapter.Item>> = combine(
|
||||||
updateTicker,
|
updateTicker,
|
||||||
requiredPermissions,
|
permissionTool.missingPermissions,
|
||||||
pods,
|
pods,
|
||||||
debugSettings.isDebugModeEnabled.flow,
|
debugSettings.isDebugModeEnabled.flow,
|
||||||
generalSettings.showAll.flow,
|
generalSettings.showAll.flow,
|
||||||
) { tick, permissions, pods, isDebugMode, showAll ->
|
bluetoothManager.isBluetoothEnabled,
|
||||||
|
) { _, permissions, pods, isDebugMode, showAll, isBluetoothEnabled ->
|
||||||
val items = mutableListOf<OverviewAdapter.Item>()
|
val items = mutableListOf<OverviewAdapter.Item>()
|
||||||
|
|
||||||
val mainPod = podMonitor.mainDevice.first()
|
val mainPod = podMonitor.mainDevice.first()
|
||||||
@@ -144,20 +141,20 @@ class OverviewFragmentVM @Inject constructor(
|
|||||||
.run { items.addAll(this) }
|
.run { items.addAll(this) }
|
||||||
|
|
||||||
permissions
|
permissions
|
||||||
.map {
|
.map { perm ->
|
||||||
PermissionCardVH.Item(
|
PermissionCardVH.Item(
|
||||||
permission = it,
|
permission = perm,
|
||||||
onRequest = { requestPermissionEvent.postValue(it) }
|
onRequest = { requestPermissionEvent.postValue(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.run { items.addAll(this) }
|
.run { items.addAll(this) }
|
||||||
|
|
||||||
if (!showAll && items.none { it is PodDeviceVH.Item } && permissions.isEmpty()) {
|
if (permissions.isEmpty()) {
|
||||||
NoPairedDeviceCardVH.Item {
|
if (!isBluetoothEnabled) {
|
||||||
generalSettings.showAll.value = true
|
items.add(0, BluetoothDisabledVH.Item)
|
||||||
}.run { items.add(this) }
|
} else if (mainPod == null) {
|
||||||
} else if (showAll && mainPod == null && permissions.isEmpty()) {
|
items.add(0, MissingMainDeviceVH.Item)
|
||||||
items.add(0, MissingMainDeviceVH.Item)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items
|
items
|
||||||
@@ -166,7 +163,7 @@ class OverviewFragmentVM @Inject constructor(
|
|||||||
.asLiveData2()
|
.asLiveData2()
|
||||||
|
|
||||||
fun onPermissionResult(granted: Boolean) {
|
fun onPermissionResult(granted: Boolean) {
|
||||||
if (granted) permissionCheckTrigger.value = UUID.randomUUID()
|
if (granted) permissionTool.recheck()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun goToSettings() = launch {
|
fun goToSettings() = launch {
|
||||||
|
|||||||
+8
-10
@@ -4,29 +4,27 @@ import android.view.ViewGroup
|
|||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
import eu.darken.capod.common.lists.binding
|
import eu.darken.capod.common.lists.binding
|
||||||
import eu.darken.capod.common.lists.differ.DifferItem
|
import eu.darken.capod.common.lists.differ.DifferItem
|
||||||
import eu.darken.capod.databinding.OverviewNopaireddeviceItemBinding
|
import eu.darken.capod.databinding.OverviewBluetoothDisabledItemBinding
|
||||||
import eu.darken.capod.main.ui.overview.OverviewAdapter
|
import eu.darken.capod.main.ui.overview.OverviewAdapter
|
||||||
|
|
||||||
class NoPairedDeviceCardVH(parent: ViewGroup) :
|
class BluetoothDisabledVH(parent: ViewGroup) :
|
||||||
OverviewAdapter.BaseVH<NoPairedDeviceCardVH.Item, OverviewNopaireddeviceItemBinding>(
|
OverviewAdapter.BaseVH<BluetoothDisabledVH.Item, OverviewBluetoothDisabledItemBinding>(
|
||||||
R.layout.overview_nopaireddevice_item,
|
R.layout.overview_bluetooth_disabled_item,
|
||||||
parent
|
parent
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override val viewBinding = lazy {
|
override val viewBinding = lazy {
|
||||||
OverviewNopaireddeviceItemBinding.bind(itemView)
|
OverviewBluetoothDisabledItemBinding.bind(itemView)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val onBindData: OverviewNopaireddeviceItemBinding.(
|
override val onBindData: OverviewBluetoothDisabledItemBinding.(
|
||||||
item: Item,
|
item: Item,
|
||||||
payloads: List<Any>
|
payloads: List<Any>
|
||||||
) -> Unit = binding(payload = true) { item ->
|
) -> Unit = binding(payload = true) { item ->
|
||||||
showAllAction.setOnClickListener { item.onShowAll() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Item(
|
object Item : OverviewAdapter.Item {
|
||||||
val onShowAll: () -> Unit
|
|
||||||
) : OverviewAdapter.Item {
|
|
||||||
override val stableId: Long = Item::class.hashCode().toLong()
|
override val stableId: Long = Item::class.hashCode().toLong()
|
||||||
|
|
||||||
override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?)
|
override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?)
|
||||||
@@ -32,17 +32,16 @@ class PodMonitor @Inject constructor(
|
|||||||
private val deviceCache = mutableMapOf<PodDevice.Id, PodDevice>()
|
private val deviceCache = mutableMapOf<PodDevice.Id, PodDevice>()
|
||||||
private val cacheLock = Mutex()
|
private val cacheLock = Mutex()
|
||||||
|
|
||||||
val devices: Flow<List<PodDevice>> = generalSettings.scannerMode.flow
|
val devices: Flow<List<PodDevice>> = bluetoothManager.isBluetoothEnabled
|
||||||
.flatMapLatest { scannerMode ->
|
.flatMapLatest { isBluetoothEnabled ->
|
||||||
bluetoothManager.isBluetoothEnabled.flatMapLatest { isBluetoothEnabled ->
|
if (isBluetoothEnabled) {
|
||||||
if (isBluetoothEnabled) {
|
generalSettings.scannerMode.flow
|
||||||
bleScanner.scan(scannerMode = scannerMode)
|
} else {
|
||||||
} else {
|
log(TAG, WARN) { "Bluetooth is currently disabled" }
|
||||||
log(TAG, WARN) { "Bluetooth is current disabled" }
|
emptyFlow()
|
||||||
emptyFlow()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.flatMapLatest { bleScanner.scan(scannerMode = it) }
|
||||||
.map { result ->
|
.map { result ->
|
||||||
// For each address we only want the newest result, upstream may batch data
|
// For each address we only want the newest result, upstream may batch data
|
||||||
result.groupBy { it.address }
|
result.groupBy { it.address }
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class MonitorWorker @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun doDoWork() {
|
private suspend fun doDoWork() {
|
||||||
val permissionsMissingOnStart = permissionTool.missingPermissions()
|
val permissionsMissingOnStart = permissionTool.missingPermissions.first()
|
||||||
if (permissionsMissingOnStart.isNotEmpty()) {
|
if (permissionsMissingOnStart.isNotEmpty()) {
|
||||||
log(TAG, WARN) { "Aborting, missing permissions: $permissionsMissingOnStart" }
|
log(TAG, WARN) { "Aborting, missing permissions: $permissionsMissingOnStart" }
|
||||||
return
|
return
|
||||||
@@ -99,7 +99,7 @@ class MonitorWorker @AssistedInject constructor(
|
|||||||
|
|
||||||
generalSettings.monitorMode.flow
|
generalSettings.monitorMode.flow
|
||||||
.flatMapLatest { monitorMode ->
|
.flatMapLatest { monitorMode ->
|
||||||
val missingPermsFlow = permissionTool.missingPermissions()
|
val missingPermsFlow = permissionTool.missingPermissions.first()
|
||||||
if (missingPermsFlow.isNotEmpty()) {
|
if (missingPermsFlow.isNotEmpty()) {
|
||||||
log(TAG, WARN) { "Aborting, permissions are missing for $monitorMode: $missingPermsFlow" }
|
log(TAG, WARN) { "Aborting, permissions are missing for $monitorMode: $missingPermsFlow" }
|
||||||
workerScope.coroutineContext.cancelChildren()
|
workerScope.coroutineContext.cancelChildren()
|
||||||
|
|||||||
+10
-22
@@ -14,42 +14,30 @@
|
|||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/permission_label"
|
android:id="@+id/bluetooth_label"
|
||||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/overview_bluetooth_disabled_label"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:text="@string/overview_nopaireddevices_label" />
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/permission_description"
|
android:id="@+id/bluetooth_description"
|
||||||
style="@style/TextAppearance.MaterialComponents.Body2"
|
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:layout_marginHorizontal="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:layout_height="wrap_content"
|
android:text="@string/overview_bluetooth_disabled_description"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/permission_label"
|
|
||||||
android:text="@string/overview_nopaireddevices_description" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/show_all_action"
|
|
||||||
style="@style/Widget.MaterialComponents.Button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_marginHorizontal="16dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="@string/settings_showall_label"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/permission_description" />
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/bluetooth_label" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
@@ -71,9 +71,7 @@
|
|||||||
<string name="pods_microphone_label">Microphone</string>
|
<string name="pods_microphone_label">Microphone</string>
|
||||||
<string name="pods_yours">Yours</string>
|
<string name="pods_yours">Yours</string>
|
||||||
|
|
||||||
<string name="overview_nopaireddevices_label">No paired device connected.</string>
|
<string name="overview_nomaindevice_label">No primary device</string>
|
||||||
<string name="overview_nopaireddevices_description">Connect a paired device or enable the \'Show all\' option.</string>
|
|
||||||
<string name="overview_nomaindevice_label">Missing main device</string>
|
|
||||||
<string name="overview_nomaindevice_description">All detected devices are unlikely to be yours. Power on and connect your device or adjust the settings.</string>
|
<string name="overview_nomaindevice_description">All detected devices are unlikely to be yours. Power on and connect your device or adjust the settings.</string>
|
||||||
|
|
||||||
<string name="settings_label">Settings</string>
|
<string name="settings_label">Settings</string>
|
||||||
@@ -146,4 +144,6 @@
|
|||||||
<string name="settings_popup_caseopen_label">Show popup</string>
|
<string name="settings_popup_caseopen_label">Show popup</string>
|
||||||
<string name="settings_popup_caseopen_description">Show a popup when the device case is opened (experimental).</string>
|
<string name="settings_popup_caseopen_description">Show a popup when the device case is opened (experimental).</string>
|
||||||
<string name="notification_channel_reaction_popup_label">Popup device reactions</string>
|
<string name="notification_channel_reaction_popup_label">Popup device reactions</string>
|
||||||
|
<string name="overview_bluetooth_disabled_label">Bluetooth is disabled</string>
|
||||||
|
<string name="overview_bluetooth_disabled_description">Bluetooth is disabled, enable it ;).</string>
|
||||||
</resources>
|
</resources>
|
||||||
+1
-1
@@ -8,7 +8,7 @@ buildscript {
|
|||||||
'version' : [
|
'version' : [
|
||||||
'major': 1,
|
'major': 1,
|
||||||
'minor': 0,
|
'minor': 0,
|
||||||
'patch': 0,
|
'patch': 1,
|
||||||
'build': 0,
|
'build': 0,
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user