Skip to content

Commit

Permalink
Merge pull request #1247 from keymapperorg/526--dont-require-dnd-perm…
Browse files Browse the repository at this point in the history
…ission-for-volume-related-actions-to-work

#526 fix: show a dialog giving the option to never ask for do not dis…
  • Loading branch information
sds100 authored Jul 7, 2024
2 parents a2d8793 + 32cee47 commit 91cffbf
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class KeyMapperApp : MultiDexApplication() {
appCoroutineScope,
suAdapter,
notificationReceiverAdapter,
ServiceLocator.settingsRepository(this),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import io.github.sds100.keymapper.mappings.DisplayActionUseCase
import io.github.sds100.keymapper.mappings.Mapping
import io.github.sds100.keymapper.mappings.isDelayBeforeNextActionAllowed
import io.github.sds100.keymapper.onboarding.OnboardingUseCase
import io.github.sds100.keymapper.system.permissions.Permission
import io.github.sds100.keymapper.util.Error
import io.github.sds100.keymapper.util.State
import io.github.sds100.keymapper.util.getFullMessage
Expand Down Expand Up @@ -106,7 +107,30 @@ class ConfigActionsViewModel<A : Action, M : Mapping<A>>(

when {
error == null -> attemptTestAction(actionData)
error.isFixable -> displayActionUseCase.fixError(error)
error.isFixable -> onFixError(error)
}
}
}
}

private fun onFixError(error: Error) {
coroutineScope.launch {
if (error == Error.PermissionDenied(Permission.ACCESS_NOTIFICATION_POLICY)) {
coroutineScope.launch {
ViewModelHelper.showDialogExplainingDndAccessBeingUnavailable(
resourceProvider = this@ConfigActionsViewModel,
popupViewModel = this@ConfigActionsViewModel,
neverShowDndTriggerErrorAgain = { displayActionUseCase.neverShowDndTriggerErrorAgain() },
fixError = { displayActionUseCase.fixError(it) },
)
}
} else {
ViewModelHelper.showFixErrorDialog(
resourceProvider = this@ConfigActionsViewModel,
popupViewModel = this@ConfigActionsViewModel,
error,
) {
displayActionUseCase.fixError(error)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.github.sds100.keymapper.R
import io.github.sds100.keymapper.mappings.ConfigMappingUseCase
import io.github.sds100.keymapper.mappings.DisplayConstraintUseCase
import io.github.sds100.keymapper.mappings.Mapping
import io.github.sds100.keymapper.system.permissions.Permission
import io.github.sds100.keymapper.util.Error
import io.github.sds100.keymapper.util.State
import io.github.sds100.keymapper.util.getFullMessage
Expand All @@ -19,6 +20,7 @@ import io.github.sds100.keymapper.util.ui.PopupViewModel
import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
import io.github.sds100.keymapper.util.ui.ResourceProvider
import io.github.sds100.keymapper.util.ui.TintType
import io.github.sds100.keymapper.util.ui.ViewModelHelper
import io.github.sds100.keymapper.util.ui.navigate
import io.github.sds100.keymapper.util.ui.showPopup
import kotlinx.coroutines.CoroutineScope
Expand All @@ -36,15 +38,15 @@ import kotlinx.coroutines.launch

class ConfigConstraintsViewModel(
private val coroutineScope: CoroutineScope,
private val display: DisplayConstraintUseCase,
private val config: ConfigMappingUseCase<*, *>,
private val displayUseCase: DisplayConstraintUseCase,
private val configMappingUseCase: ConfigMappingUseCase<*, *>,
private val allowedConstraints: List<ChooseConstraintType>,
resourceProvider: ResourceProvider,
) : ResourceProvider by resourceProvider,
PopupViewModel by PopupViewModelImpl(),
NavigationViewModel by NavigationViewModelImpl() {

private val uiHelper = ConstraintUiHelper(display, resourceProvider)
private val uiHelper = ConstraintUiHelper(displayUseCase, resourceProvider)

private val _state by lazy { MutableStateFlow(buildState(State.Loading)) }
val state by lazy { _state.asStateFlow() }
Expand All @@ -59,20 +61,22 @@ class ConfigConstraintsViewModel(
}

coroutineScope.launch {
config.mapping.collectLatest {
configMappingUseCase.mapping.collectLatest {
rebuildUiState.emit(it)
}
}

coroutineScope.launch {
display.invalidateConstraintErrors.collectLatest {
rebuildUiState.emit(config.mapping.firstOrNull() ?: return@collectLatest)
displayUseCase.invalidateConstraintErrors.collectLatest {
rebuildUiState.emit(
configMappingUseCase.mapping.firstOrNull() ?: return@collectLatest,
)
}
}
}

fun onChosenNewConstraint(constraint: Constraint) {
val isDuplicate = !config.addConstraint(constraint)
val isDuplicate = !configMappingUseCase.addConstraint(constraint)

if (isDuplicate) {
coroutineScope.launch {
Expand All @@ -85,30 +89,53 @@ class ConfigConstraintsViewModel(
}
}

fun onRemoveConstraintClick(id: String) = config.removeConstraint(id)
fun onRemoveConstraintClick(id: String) = configMappingUseCase.removeConstraint(id)

fun onAndRadioButtonCheckedChange(checked: Boolean) {
if (checked) {
config.setAndMode()
configMappingUseCase.setAndMode()
}
}

fun onOrRadioButtonCheckedChange(checked: Boolean) {
if (checked) {
config.setOrMode()
configMappingUseCase.setOrMode()
}
}

fun onListItemClick(id: String) {
coroutineScope.launch {
config.mapping.firstOrNull()?.ifIsData { mapping ->
configMappingUseCase.mapping.firstOrNull()?.ifIsData { mapping ->
val constraint = mapping.constraintState.constraints.singleOrNull { it.uid == id }
?: return@launch

val error = display.getConstraintError(constraint) ?: return@launch
val error = displayUseCase.getConstraintError(constraint) ?: return@launch

if (error.isFixable) {
display.fixError(error)
onFixError(error)
}
}
}
}

private fun onFixError(error: Error) {
coroutineScope.launch {
if (error == Error.PermissionDenied(Permission.ACCESS_NOTIFICATION_POLICY)) {
coroutineScope.launch {
ViewModelHelper.showDialogExplainingDndAccessBeingUnavailable(
resourceProvider = this@ConfigConstraintsViewModel,
popupViewModel = this@ConfigConstraintsViewModel,
neverShowDndTriggerErrorAgain = { displayUseCase.neverShowDndTriggerErrorAgain() },
fixError = { displayUseCase.fixError(it) },
)
}
} else {
ViewModelHelper.showFixErrorDialog(
resourceProvider = this@ConfigConstraintsViewModel,
popupViewModel = this@ConfigConstraintsViewModel,
error,
) {
displayUseCase.fixError(error)
}
}
}
Expand All @@ -120,14 +147,14 @@ class ConfigConstraintsViewModel(
navigate("add_constraint", NavDestination.ChooseConstraint(allowedConstraints))
?: return@launch

config.addConstraint(constraint)
configMappingUseCase.addConstraint(constraint)
}
}

private fun createListItem(constraint: Constraint): ConstraintListItem {
val title: String = uiHelper.getTitle(constraint)
val icon: IconInfo? = uiHelper.getIcon(constraint)
val error: Error? = display.getConstraintError(constraint)
val error: Error? = displayUseCase.getConstraintError(constraint)

return ConstraintListItem(
id = constraint.uid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ class DisplaySimpleMappingUseCaseImpl(
override fun startAccessibilityService(): Boolean = accessibilityServiceAdapter.start()

override fun restartAccessibilityService(): Boolean = accessibilityServiceAdapter.restart()

override fun neverShowDndTriggerErrorAgain() {
preferenceRepository.set(Keys.neverShowDndError, true)
}
}

interface DisplaySimpleMappingUseCase :
Expand All @@ -97,6 +101,7 @@ interface DisplayActionUseCase : GetActionErrorUseCase {
fun getAppIcon(packageName: String): Result<Drawable>
fun getInputMethodLabel(imeId: String): Result<String>
suspend fun fixError(error: Error)
fun neverShowDndTriggerErrorAgain()
fun startAccessibilityService(): Boolean
fun restartAccessibilityService(): Boolean
}
Expand All @@ -105,5 +110,6 @@ interface DisplayConstraintUseCase : GetConstraintErrorUseCase {
fun getAppName(packageName: String): Result<String>
fun getAppIcon(packageName: String): Result<Drawable>
fun getInputMethodLabel(imeId: String): Result<String>
fun neverShowDndTriggerErrorAgain()
suspend fun fixError(error: Error)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.sds100.keymapper.mappings.fingerprintmaps

import io.github.sds100.keymapper.R
import io.github.sds100.keymapper.system.permissions.Permission
import io.github.sds100.keymapper.util.Error
import io.github.sds100.keymapper.util.State
import io.github.sds100.keymapper.util.getFullMessage
Expand All @@ -14,6 +15,7 @@ import io.github.sds100.keymapper.util.ui.PopupUi
import io.github.sds100.keymapper.util.ui.PopupViewModel
import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
import io.github.sds100.keymapper.util.ui.ResourceProvider
import io.github.sds100.keymapper.util.ui.ViewModelHelper
import io.github.sds100.keymapper.util.ui.navigate
import io.github.sds100.keymapper.util.ui.showPopup
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -138,7 +140,30 @@ class FingerprintMapListViewModel(
showPopup("fix_error", snackBar) ?: return@launch

if (error.isFixable) {
useCase.fixError(error)
onFixError(error)
}
}
}

private fun onFixError(error: Error) {
coroutineScope.launch {
if (error == Error.PermissionDenied(Permission.ACCESS_NOTIFICATION_POLICY)) {
coroutineScope.launch {
ViewModelHelper.showDialogExplainingDndAccessBeingUnavailable(
resourceProvider = this@FingerprintMapListViewModel,
popupViewModel = this@FingerprintMapListViewModel,
neverShowDndTriggerErrorAgain = { useCase.neverShowDndTriggerErrorAgain() },
fixError = { useCase.fixError(it) },
)
}
} else {
ViewModelHelper.showFixErrorDialog(
resourceProvider = this@FingerprintMapListViewModel,
popupViewModel = this@FingerprintMapListViewModel,
error,
) {
useCase.fixError(error)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import io.github.sds100.keymapper.system.permissions.Permission
import io.github.sds100.keymapper.system.permissions.PermissionAdapter
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge

Expand Down Expand Up @@ -52,8 +51,7 @@ class DisplayKeyMapUseCaseImpl(

if (trigger.keys.any { it.keyCode in keysThatRequireDndAccess }) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
!permissionAdapter.isGranted(Permission.ACCESS_NOTIFICATION_POLICY) &&
preferenceRepository.get(Keys.neverShowDndError).first() != true
!permissionAdapter.isGranted(Permission.ACCESS_NOTIFICATION_POLICY)
) {
errors.add(KeyMapTriggerError.DND_ACCESS_DENIED)
}
Expand All @@ -68,14 +66,9 @@ class DisplayKeyMapUseCaseImpl(

return errors
}

override fun neverShowDndTriggerErrorAgain() {
preferenceRepository.set(Keys.neverShowDndError, true)
}
}

interface DisplayKeyMapUseCase : DisplaySimpleMappingUseCase {
val invalidateTriggerErrors: Flow<Unit>
suspend fun getTriggerErrors(keyMap: KeyMap): List<KeyMapTriggerError>
fun neverShowDndTriggerErrorAgain()
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,41 +140,41 @@ open class KeyMapListViewModel(

fun onTriggerErrorChipClick(chipModel: ChipUi) {
if (chipModel is ChipUi.Error) {
if (chipModel.error == Error.PermissionDenied(Permission.ACCESS_NOTIFICATION_POLICY)) {
coroutineScope.launch {
ViewModelHelper.showDialogExplainingDndAccessBeingUnavailable(
resourceProvider = this@KeyMapListViewModel,
popupViewModel = this@KeyMapListViewModel,
neverShowDndTriggerErrorAgain = { useCase.neverShowDndTriggerErrorAgain() },
fixError = { useCase.fixError(it) },
)
}
} else {
showDialogAndFixError(chipModel.error)
}
onFixError(chipModel.error)
}
}

fun onActionChipClick(chipModel: ChipUi) {
if (chipModel is ChipUi.Error) {
showDialogAndFixError(chipModel.error)
onFixError(chipModel.error)
}
}

fun onConstraintsChipClick(chipModel: ChipUi) {
if (chipModel is ChipUi.Error) {
showDialogAndFixError(chipModel.error)
onFixError(chipModel.error)
}
}

private fun showDialogAndFixError(error: Error) {
private fun onFixError(error: Error) {
coroutineScope.launch {
ViewModelHelper.showFixErrorDialog(
resourceProvider = this@KeyMapListViewModel,
popupViewModel = this@KeyMapListViewModel,
error,
) {
useCase.fixError(error)
if (error == Error.PermissionDenied(Permission.ACCESS_NOTIFICATION_POLICY)) {
coroutineScope.launch {
ViewModelHelper.showDialogExplainingDndAccessBeingUnavailable(
resourceProvider = this@KeyMapListViewModel,
popupViewModel = this@KeyMapListViewModel,
neverShowDndTriggerErrorAgain = { useCase.neverShowDndTriggerErrorAgain() },
fixError = { useCase.fixError(it) },
)
}
} else {
ViewModelHelper.showFixErrorDialog(
resourceProvider = this@KeyMapListViewModel,
popupViewModel = this@KeyMapListViewModel,
error,
) {
useCase.fixError(error)
}
}
}
}
Expand Down
Loading

0 comments on commit 91cffbf

Please sign in to comment.