From 4a3e5056b65c84ab7d85e103792fe762b9f61857 Mon Sep 17 00:00:00 2001 From: darken Date: Wed, 19 Jan 2022 18:30:15 +0100 Subject: [PATCH] Fix permission refreshing and add card if bluetooth is disabled. --- .../darken/capod/main/core/PermissionTool.kt | 35 +++++++++-- .../capod/main/ui/overview/OverviewAdapter.kt | 2 + .../main/ui/overview/OverviewFragment.kt | 6 +- .../main/ui/overview/OverviewFragmentVM.kt | 62 ++++++++++--------- .../ui/overview/cards/BluetoothDisabledVH.kt | 33 ++++++++++ .../darken/capod/monitor/core/PodMonitor.kt | 17 +++-- .../monitor/core/worker/MonitorWorker.kt | 4 +- .../overview_bluetooth_disabled_item.xml | 43 +++++++++++++ app/src/main/res/values/strings.xml | 2 + 9 files changed, 155 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/eu/darken/capod/main/ui/overview/cards/BluetoothDisabledVH.kt create mode 100644 app/src/main/res/layout/overview_bluetooth_disabled_item.xml diff --git a/app/src/main/java/eu/darken/capod/main/core/PermissionTool.kt b/app/src/main/java/eu/darken/capod/main/core/PermissionTool.kt index 503f7a86..70f03cd3 100644 --- a/app/src/main/java/eu/darken/capod/main/core/PermissionTool.kt +++ b/app/src/main/java/eu/darken/capod/main/core/PermissionTool.kt @@ -1,20 +1,43 @@ package eu.darken.capod.main.core import android.content.Context -import dagger.Reusable 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.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.Singleton -@Reusable +@Singleton class PermissionTool @Inject constructor( @ApplicationContext private val context: Context, private val generalSettings: GeneralSettings, ) { + private val permissionCheckTrigger = MutableStateFlow(UUID.randomUUID()) - suspend fun missingPermissions(): Set = Permission.values() - .filter { it != Permission.IGNORE_BATTERY_OPTIMIZATION || generalSettings.monitorMode.value == MonitorMode.ALWAYS } - .filter { it.isRequired(context) } - .toSet() + fun recheck() { + log(TAG) { "recheck()" } + permissionCheckTrigger.value = UUID.randomUUID() + } + + val missingPermissions: Flow> = 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") + } } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt index 4884ce48..1593597d 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt @@ -11,6 +11,7 @@ import eu.darken.capod.common.lists.differ.setupDiffer 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.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.NoPairedDeviceCardVH import eu.darken.capod.main.ui.overview.cards.PermissionCardVH @@ -35,6 +36,7 @@ class OverviewAdapter @Inject constructor() : 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 BluetoothDisabledVH.Item }) { BluetoothDisabledVH(it) }) } override fun getItemCount(): Int = data.size diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt index cf21e6f9..909ffaf0 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt @@ -70,8 +70,10 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) { } } - vm.listItems.observe2(ui) { - adapter.update(it) + vm.listItems.observe2(ui) { adapter.update(it) } + + vm.workerAutolaunch.observe2 { + // While UI is active, subscribe to the autolaunch routine } vm.requestPermissionEvent.observe2(ui) { diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt index 208e0889..125b6682 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt @@ -8,6 +8,7 @@ import eu.darken.capod.common.bluetooth.BluetoothManager2 import eu.darken.capod.common.coroutine.DispatcherProvider import eu.darken.capod.common.debug.autoreport.DebugSettings 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.navigation.navVia import eu.darken.capod.common.permissions.Permission @@ -16,6 +17,7 @@ import eu.darken.capod.common.upgrade.UpgradeRepo import eu.darken.capod.main.core.GeneralSettings import eu.darken.capod.main.core.MonitorMode 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.NoPairedDeviceCardVH import eu.darken.capod.main.ui.overview.cards.PermissionCardVH @@ -26,7 +28,6 @@ import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.BasicSingleApplePods import eu.darken.capod.pods.core.apple.DualApplePods import eu.darken.capod.pods.core.apple.SingleApplePods -import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.isActive @@ -57,9 +58,7 @@ class OverviewFragmentVM @Inject constructor( } } - private val permissionCheckTrigger = MutableStateFlow(UUID.randomUUID()) - private val requiredPermissions: Flow> = permissionCheckTrigger - .map { permissionTool.missingPermissions() } + val workerAutolaunch: LiveData = permissionTool.missingPermissions .onEach { if (it.isNotEmpty()) { log(TAG) { "Missing permissions: $it" } @@ -76,26 +75,24 @@ class OverviewFragmentVM @Inject constructor( monitorControl.startMonitor() } } + .map { Unit } + .asLiveData2() val requestPermissionEvent = SingleLiveEvent() - private val pods: Flow> = requiredPermissions + private val pods: Flow> = permissionTool.missingPermissions .flatMapLatest { permissions -> - if (permissions.isEmpty()) { - generalSettings.showAll.flow - .flatMapLatest { showAll -> - if (showAll) { - podMonitor.devices - } else { - podMonitor.mainDevice.map { mainDevice -> - mainDevice?.let { listOf(it) } ?: emptyList() - } - } + if (permissions.isNotEmpty()) { + return@flatMapLatest flowOf(emptyList()) + } + + generalSettings.showAll.flow.flatMapLatest { showAll -> + if (showAll) { + podMonitor.devices + } else { + podMonitor.mainDevice.map { mainDevice -> + mainDevice?.let { listOf(it) } ?: emptyList() } - } else { - channelFlow { - send(emptyList()) - awaitClose() } } } @@ -103,11 +100,12 @@ class OverviewFragmentVM @Inject constructor( val listItems: LiveData> = combine( updateTicker, - requiredPermissions, + permissionTool.missingPermissions, pods, debugSettings.isDebugModeEnabled.flow, generalSettings.showAll.flow, - ) { tick, permissions, pods, isDebugMode, showAll -> + bluetoothManager.isBluetoothEnabled, + ) { _, permissions, pods, isDebugMode, showAll, isBluetoothEnabled -> val items = mutableListOf() val mainPod = podMonitor.mainDevice.first() @@ -144,20 +142,24 @@ class OverviewFragmentVM @Inject constructor( .run { items.addAll(this) } permissions - .map { + .map { perm -> PermissionCardVH.Item( - permission = it, + permission = perm, onRequest = { requestPermissionEvent.postValue(it) } ) } .run { items.addAll(this) } - if (!showAll && items.none { it is PodDeviceVH.Item } && permissions.isEmpty()) { - NoPairedDeviceCardVH.Item { - generalSettings.showAll.value = true - }.run { items.add(this) } - } else if (showAll && mainPod == null && permissions.isEmpty()) { - items.add(0, MissingMainDeviceVH.Item) + if (permissions.isEmpty()) { + if (!isBluetoothEnabled) { + items.add(0, BluetoothDisabledVH.Item) + } else if (!showAll && items.none { it is PodDeviceVH.Item }) { + NoPairedDeviceCardVH.Item { + generalSettings.showAll.value = true + }.run { items.add(this) } + } else if (showAll && mainPod == null) { + items.add(0, MissingMainDeviceVH.Item) + } } items @@ -166,7 +168,7 @@ class OverviewFragmentVM @Inject constructor( .asLiveData2() fun onPermissionResult(granted: Boolean) { - if (granted) permissionCheckTrigger.value = UUID.randomUUID() + if (granted) permissionTool.recheck() } fun goToSettings() = launch { diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/BluetoothDisabledVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/BluetoothDisabledVH.kt new file mode 100644 index 00000000..0c0d7a39 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/BluetoothDisabledVH.kt @@ -0,0 +1,33 @@ +package eu.darken.capod.main.ui.overview.cards + +import android.view.ViewGroup +import eu.darken.capod.R +import eu.darken.capod.common.lists.binding +import eu.darken.capod.common.lists.differ.DifferItem +import eu.darken.capod.databinding.OverviewBluetoothDisabledItemBinding +import eu.darken.capod.main.ui.overview.OverviewAdapter + +class BluetoothDisabledVH(parent: ViewGroup) : + OverviewAdapter.BaseVH( + R.layout.overview_bluetooth_disabled_item, + parent + ) { + + override val viewBinding = lazy { + OverviewBluetoothDisabledItemBinding.bind(itemView) + } + + override val onBindData: OverviewBluetoothDisabledItemBinding.( + item: Item, + payloads: List + ) -> Unit = binding(payload = true) { item -> + + } + + object Item : OverviewAdapter.Item { + override val stableId: Long = Item::class.hashCode().toLong() + + override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?) + get() = { old, new -> if (new::class.isInstance(old)) new else null } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt b/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt index 4bb8ab4c..cc3a6150 100644 --- a/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt +++ b/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt @@ -32,17 +32,16 @@ class PodMonitor @Inject constructor( private val deviceCache = mutableMapOf() private val cacheLock = Mutex() - val devices: Flow> = generalSettings.scannerMode.flow - .flatMapLatest { scannerMode -> - bluetoothManager.isBluetoothEnabled.flatMapLatest { isBluetoothEnabled -> - if (isBluetoothEnabled) { - bleScanner.scan(scannerMode = scannerMode) - } else { - log(TAG, WARN) { "Bluetooth is current disabled" } - emptyFlow() - } + val devices: Flow> = bluetoothManager.isBluetoothEnabled + .flatMapLatest { isBluetoothEnabled -> + if (isBluetoothEnabled) { + generalSettings.scannerMode.flow + } else { + log(TAG, WARN) { "Bluetooth is currently disabled" } + emptyFlow() } } + .flatMapLatest { bleScanner.scan(scannerMode = it) } .map { result -> // For each address we only want the newest result, upstream may batch data result.groupBy { it.address } 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 60173162..fd22da2e 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 @@ -76,7 +76,7 @@ class MonitorWorker @AssistedInject constructor( } private suspend fun doDoWork() { - val permissionsMissingOnStart = permissionTool.missingPermissions() + val permissionsMissingOnStart = permissionTool.missingPermissions.first() if (permissionsMissingOnStart.isNotEmpty()) { log(TAG, WARN) { "Aborting, missing permissions: $permissionsMissingOnStart" } return @@ -99,7 +99,7 @@ class MonitorWorker @AssistedInject constructor( generalSettings.monitorMode.flow .flatMapLatest { monitorMode -> - val missingPermsFlow = permissionTool.missingPermissions() + val missingPermsFlow = permissionTool.missingPermissions.first() if (missingPermsFlow.isNotEmpty()) { log(TAG, WARN) { "Aborting, permissions are missing for $monitorMode: $missingPermsFlow" } workerScope.coroutineContext.cancelChildren() diff --git a/app/src/main/res/layout/overview_bluetooth_disabled_item.xml b/app/src/main/res/layout/overview_bluetooth_disabled_item.xml new file mode 100644 index 00000000..3e8f0b34 --- /dev/null +++ b/app/src/main/res/layout/overview_bluetooth_disabled_item.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c0646d43..2e0a5d97 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -146,4 +146,6 @@ Show popup Show a popup when the device case is opened (experimental). Popup device reactions + Bluetooth is disabled + Bluetooth is disabled, enable it ;). \ No newline at end of file