Skip to content

Commit

Permalink
#1016 feat: Don't show Do Not Disturb permission prompt on devices th…
Browse files Browse the repository at this point in the history
…at don't have the settings
  • Loading branch information
sds100 committed Jun 21, 2022
1 parent 4af585c commit c41e85c
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 95 deletions.
3 changes: 2 additions & 1 deletion app/src/main/java/io/github/sds100/keymapper/UseCases.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ object UseCases {
return DisplayKeyMapUseCaseImpl(
ServiceLocator.permissionAdapter(ctx),
ServiceLocator.inputMethodAdapter(ctx),
displaySimpleMapping(ctx)
displaySimpleMapping(ctx),
ServiceLocator.settingsRepository(ctx)
)
}

Expand Down
6 changes: 4 additions & 2 deletions app/src/main/java/io/github/sds100/keymapper/data/Keys.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,15 @@ object Keys {
booleanPreferencesKey("fingerprint_gestures_available")

val approvedSetupChosenDevicesAgain =
booleanPreferencesKey("pref_approved_new_choose_devices_settings")
booleanPreferencesKey("pref_approved_new_choose_devices_settings")

val rerouteKeyEvents = booleanPreferencesKey("key_reroute_key_events_from_specified_devices")
val devicesToRerouteKeyEvents =
stringSetPreferencesKey("key_devices_to_reroute_key_events")
stringSetPreferencesKey("key_devices_to_reroute_key_events")

val log = booleanPreferencesKey("key_log")
val shownShizukuPermissionPrompt = booleanPreferencesKey("key_shown_shizuku_permission_prompt")
val savedWifiSSIDs = stringSetPreferencesKey("key_saved_wifi_ssids")

val neverShowDndError = booleanPreferencesKey("key_never_show_dnd_error")
}
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,16 @@ class ConfigKeyMapTriggerViewModel(
}
}

fun fixError(listItemId: String) {
fun onTriggerErrorClick(listItemId: String) {
coroutineScope.launch {
when (KeyMapTriggerError.valueOf(listItemId)) {
KeyMapTriggerError.DND_ACCESS_DENIED -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val error =
Error.PermissionDenied(Permission.ACCESS_NOTIFICATION_POLICY)
displayKeyMap.fixError(error)
ViewModelHelper.showDialogExplainingDndAccessBeingUnavailable(
resourceProvider = this@ConfigKeyMapTriggerViewModel,
popupViewModel = this@ConfigKeyMapTriggerViewModel,
neverShowDndTriggerErrorAgain = { displayKeyMap.neverShowDndTriggerErrorAgain() },
fixError = { displayKeyMap.fixError(it) }
)
}

KeyMapTriggerError.SCREEN_OFF_ROOT_DENIED -> {
Expand All @@ -369,58 +372,57 @@ class ConfigKeyMapTriggerViewModel(
}

private fun createListItems(
trigger: KeyMapTrigger,
showDeviceDescriptors: Boolean
trigger: KeyMapTrigger,
showDeviceDescriptors: Boolean
): List<TriggerKeyListItem> =
trigger.keys.mapIndexed { index, key ->
val extraInfo = buildString {
append(getTriggerKeyDeviceName(key.device, showDeviceDescriptors))
trigger.keys.mapIndexed { index, key ->
val extraInfo = buildString {
append(getTriggerKeyDeviceName(key.device, showDeviceDescriptors))

if (!key.consumeKeyEvent) {
val midDot = getString(R.string.middot)
append(" $midDot ${getString(R.string.flag_dont_override_default_action)}")
}
if (!key.consumeKeyEvent) {
val midDot = getString(R.string.middot)
append(" $midDot ${getString(R.string.flag_dont_override_default_action)}")
}
}

val clickTypeString = when (key.clickType) {
ClickType.SHORT_PRESS -> null
ClickType.LONG_PRESS -> getString(R.string.clicktype_long_press)
ClickType.DOUBLE_PRESS -> getString(R.string.clicktype_double_press)
}
val clickTypeString = when (key.clickType) {
ClickType.SHORT_PRESS -> null
ClickType.LONG_PRESS -> getString(R.string.clicktype_long_press)
ClickType.DOUBLE_PRESS -> getString(R.string.clicktype_double_press)
}

val linkDrawable = when {
trigger.mode is TriggerMode.Parallel && index < trigger.keys.lastIndex -> TriggerKeyLinkType.PLUS
trigger.mode is TriggerMode.Sequence && index < trigger.keys.lastIndex -> TriggerKeyLinkType.ARROW
else -> TriggerKeyLinkType.HIDDEN
}
val linkDrawable = when {
trigger.mode is TriggerMode.Parallel && index < trigger.keys.lastIndex -> TriggerKeyLinkType.PLUS
trigger.mode is TriggerMode.Sequence && index < trigger.keys.lastIndex -> TriggerKeyLinkType.ARROW
else -> TriggerKeyLinkType.HIDDEN
}

TriggerKeyListItem(
id = key.uid,
keyCode = key.keyCode,
name = KeyEventUtils.keyCodeToString(key.keyCode),
clickTypeString = clickTypeString,
extraInfo = extraInfo,
linkType = linkDrawable,
isDragDropEnabled = trigger.keys.size > 1
)
TriggerKeyListItem(
id = key.uid,
keyCode = key.keyCode,
name = KeyEventUtils.keyCodeToString(key.keyCode),
clickTypeString = clickTypeString,
extraInfo = extraInfo,
linkType = linkDrawable,
isDragDropEnabled = trigger.keys.size > 1
)
}

private fun getTriggerKeyDeviceName(
device: TriggerKeyDevice,
showDeviceDescriptors: Boolean
): String =
when (device) {
is TriggerKeyDevice.Internal -> getString(R.string.this_device)
is TriggerKeyDevice.Any -> getString(R.string.any_device)
is TriggerKeyDevice.External -> {
if (showDeviceDescriptors) {
InputDeviceUtils.appendDeviceDescriptorToName(
device.descriptor,
device.name
)
} else {
device.name
}
}
device: TriggerKeyDevice,
showDeviceDescriptors: Boolean
): String = when (device) {
is TriggerKeyDevice.Internal -> getString(R.string.this_device)
is TriggerKeyDevice.Any -> getString(R.string.any_device)
is TriggerKeyDevice.External -> {
if (showDeviceDescriptors) {
InputDeviceUtils.appendDeviceDescriptorToName(
device.descriptor,
device.name
)
} else {
device.name
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,25 @@ package io.github.sds100.keymapper.mappings.keymaps

import android.os.Build
import android.view.KeyEvent
import io.github.sds100.keymapper.data.Keys
import io.github.sds100.keymapper.data.repositories.PreferenceRepository
import io.github.sds100.keymapper.mappings.DisplaySimpleMappingUseCase
import io.github.sds100.keymapper.mappings.keymaps.trigger.KeyMapTriggerError
import io.github.sds100.keymapper.system.inputmethod.InputMethodAdapter
import io.github.sds100.keymapper.system.inputmethod.KeyMapperImeHelper
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.merge
import kotlinx.coroutines.flow.*

/**
* Created by sds100 on 04/04/2021.
*/

class DisplayKeyMapUseCaseImpl(
private val permissionAdapter: PermissionAdapter,
private val inputMethodAdapter: InputMethodAdapter,
displaySimpleMappingUseCase: DisplaySimpleMappingUseCase
private val permissionAdapter: PermissionAdapter,
private val inputMethodAdapter: InputMethodAdapter,
displaySimpleMappingUseCase: DisplaySimpleMappingUseCase,
private val preferenceRepository: PreferenceRepository
) : DisplayKeyMapUseCase, DisplaySimpleMappingUseCase by displaySimpleMappingUseCase {
private companion object {
val keysThatRequireDndAccess = arrayOf(
Expand All @@ -30,39 +32,45 @@ class DisplayKeyMapUseCaseImpl(
private val keyMapperImeHelper: KeyMapperImeHelper = KeyMapperImeHelper(inputMethodAdapter)

override val invalidateTriggerErrors = merge(
permissionAdapter.onPermissionsUpdate
permissionAdapter.onPermissionsUpdate,
preferenceRepository.get(Keys.neverShowDndError).map { }.drop(1)
)

override fun getTriggerErrors(keyMap: KeyMap): List<KeyMapTriggerError> {
override suspend fun getTriggerErrors(keyMap: KeyMap): List<KeyMapTriggerError> {
val trigger = keyMap.trigger
val errors = mutableListOf<KeyMapTriggerError>()

// can only detect volume button presses during a phone call with an input method service
if (!keyMapperImeHelper.isCompatibleImeChosen() &&keyMap.requiresImeKeyEventForwarding()) {

if (!keyMapperImeHelper.isCompatibleImeChosen() && keyMap.requiresImeKeyEventForwarding()) {
errors.add(KeyMapTriggerError.CANT_DETECT_IN_PHONE_CALL)
}

if (trigger.keys.any { it.keyCode in keysThatRequireDndAccess }) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !permissionAdapter.isGranted(Permission.ACCESS_NOTIFICATION_POLICY)
&& !permissionAdapter.isGranted(Permission.ACCESS_NOTIFICATION_POLICY)
&& preferenceRepository.get(Keys.neverShowDndError).first() != true
) {
errors.add(KeyMapTriggerError.DND_ACCESS_DENIED)
}
}

if (trigger.screenOffTrigger
&& !permissionAdapter.isGranted(Permission.ROOT)
&& trigger.isDetectingWhenScreenOffAllowed()) {

&& !permissionAdapter.isGranted(Permission.ROOT)
&& trigger.isDetectingWhenScreenOffAllowed()
) {
errors.add(KeyMapTriggerError.SCREEN_OFF_ROOT_DENIED)
}

return errors
}

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

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

fun create(keyMap: KeyMap, showDeviceDescriptors: Boolean): KeyMapListItem.KeyMapUiState {
val midDot = getString(R.string.middot)

val triggerDescription = buildString {
val separator = when (keyMap.trigger.mode) {
is TriggerMode.Parallel -> getString(R.string.plus)
is TriggerMode.Sequence -> getString(R.string.arrow)
is TriggerMode.Undefined -> null
}
suspend fun create(keyMap: KeyMap, showDeviceDescriptors: Boolean): KeyMapListItem.KeyMapUiState {
val midDot = getString(R.string.middot)

val triggerDescription = buildString {
val separator = when (keyMap.trigger.mode) {
is TriggerMode.Parallel -> getString(R.string.plus)
is TriggerMode.Sequence -> getString(R.string.arrow)
is TriggerMode.Undefined -> null
}

val longPressString = getString(R.string.clicktype_long_press)
val longPressString = getString(R.string.clicktype_long_press)
val doublePressString = getString(R.string.clicktype_double_press)

keyMap.trigger.keys.forEachIndexed { index, key ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.github.sds100.keymapper.mappings.keymaps

import io.github.sds100.keymapper.ui.*
import io.github.sds100.keymapper.util.*
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.mapData
import io.github.sds100.keymapper.util.ui.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -118,7 +120,18 @@ open class KeyMapListViewModel constructor(

fun onTriggerErrorChipClick(chipModel: ChipUi) {
if (chipModel is ChipUi.Error) {
showDialogAndFixError(chipModel.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)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class TriggerFragment : RecyclerViewFragment<TriggerKeyListItem, FragmentTrigger
model(it)

onFixClick { _ ->
configKeyMapTriggerViewModel.fixError(it.id)
configKeyMapTriggerViewModel.onTriggerErrorClick(it.id)
}
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.github.sds100.keymapper.util.ui

import androidx.annotation.StringRes
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.getFullMessage
import io.github.sds100.keymapper.util.isFixable
Expand Down Expand Up @@ -149,11 +150,36 @@ object ViewModelHelper {
}
} else {
val dialog = PopupUi.Dialog(
message = error.getFullMessage(resourceProvider),
positiveButtonText = resourceProvider.getString(R.string.pos_ok),
message = error.getFullMessage(resourceProvider),
positiveButtonText = resourceProvider.getString(R.string.pos_ok),
)

popupViewModel.showPopup("fix_error", dialog)
}
}

suspend fun showDialogExplainingDndAccessBeingUnavailable(
resourceProvider: ResourceProvider,
popupViewModel: PopupViewModel,
neverShowDndTriggerErrorAgain: () -> Unit,
fixError: suspend (Error) -> Unit
) {
val dialog = PopupUi.Dialog(
title = resourceProvider.getString(R.string.dialog_title_fix_dnd_trigger_error),
message = resourceProvider.getText(R.string.dialog_message_fix_dnd_trigger_error),
positiveButtonText = resourceProvider.getString(R.string.pos_ok),
negativeButtonText = resourceProvider.getString(R.string.neg_cancel),
neutralButtonText = resourceProvider.getString(R.string.neg_dont_show_again)
)

val dialogResponse = popupViewModel.showPopup("fix_dnd_trigger_error", dialog)

if (dialogResponse == DialogResponse.POSITIVE) {
val error = Error.PermissionDenied(Permission.ACCESS_NOTIFICATION_POLICY)
fixError.invoke(error)
} else if (dialogResponse == DialogResponse.NEUTRAL) {
neverShowDndTriggerErrorAgain.invoke()
}
}

}
5 changes: 4 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -550,12 +550,15 @@

<string name="dialog_title_explain_bluetooth_permission">Permission is required</string>
<string name="dialog_message_explain_bluetooth_permission">Key Mapper needs the "nearby devices" permission so it can get the list of paired Bluetooth devices.</string>

<string name="dialog_message_no_app_found_to_create_file">You have no files app installed that allows you to create a file for Key Mapper. Please install a file manager.</string>
<string name="dialog_message_no_app_found_to_choose_a_file">You have no files app installed that allows you to choose a file for Key Mapper. Please install a file manager.</string>

<string name="dialog_title_accessibility_service_explanation">Accessibility service must be enabled</string>
<string name="dialog_message_accessibility_service_explanation">@string/accessibility_service_explanation</string>

<string name="dialog_title_fix_dnd_trigger_error">Grant Do Not Disturb access</string>
<string name="dialog_message_fix_dnd_trigger_error">You will be taken to your device\'s settings page to manage which apps can modify the Do Not Disturb state. This is <b>not present on some devices</b> so tap don\'t show again if you do not see Key Mapper in the list.</string>

<string name="pos_yes">Yes</string>
<string name="pos_done">Done</string>
Expand Down

0 comments on commit c41e85c

Please sign in to comment.