Skip to content

Commit 2474a5a

Browse files
committed
#1414 feat: constraint for when the keyboard is showing
1 parent 606fce1 commit 2474a5a

File tree

9 files changed

+48
-20
lines changed

9 files changed

+48
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- #1871 action to modify any system settings
77
- #1221 action to show a custom notification
88
- #1491 action to toggle/enable/disable hotspot
9+
- #1414 constraint for when the keyboard is showing
910

1011
## [4.0.0 Beta 2](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.02)
1112

base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ enum class ConstraintDependency {
1111
WIFI_SSID,
1212
WIFI_STATE,
1313
CHOSEN_IME,
14-
KEYBOARD_STATE,
14+
KEYBOARD_VISIBLE,
1515
DEVICE_LOCKED_STATE,
1616
LOCK_SCREEN_SHOWING,
1717
PHONE_STATE,

base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ class LazyConstraintSnapshot(
5757
networkAdapter.connectedWifiSSIDFlow.firstBlocking()
5858
}
5959
private val chosenImeId: String? by lazy { inputMethodAdapter.chosenIme.value?.id }
60-
// TODO: Implement keyboard state detection
61-
private val isKeyboardShowing: Boolean by lazy { false }
60+
private val isKeyboardShowing: Boolean by lazy {
61+
accessibilityService.isInputMethodVisible.firstBlocking()
62+
}
6263
private val callState: CallState by lazy { phoneAdapter.getCallState() }
6364
private val isCharging: Boolean by lazy { powerAdapter.isCharging.value }
6465

base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,12 @@ class ConstraintUiHelper(
135135
getString(R.string.constraint_ime_not_chosen_description, label)
136136
}
137137

138-
is ConstraintData.KeyboardShowing -> getString(R.string.constraint_keyboard_showing_description)
139-
is ConstraintData.KeyboardNotShowing -> getString(R.string.constraint_keyboard_not_showing_description)
138+
is ConstraintData.KeyboardShowing -> getString(
139+
R.string.constraint_keyboard_showing_description,
140+
)
141+
is ConstraintData.KeyboardNotShowing -> getString(
142+
R.string.constraint_keyboard_not_showing_description,
143+
)
140144

141145
is ConstraintData.DeviceIsLocked -> getString(R.string.constraint_device_is_locked)
142146
is ConstraintData.DeviceIsUnlocked -> getString(R.string.constraint_device_is_unlocked)

base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.github.sds100.keymapper.base.constraints
22

3+
import android.os.Build
34
import dagger.assisted.Assisted
45
import dagger.assisted.AssistedFactory
56
import dagger.assisted.AssistedInject
@@ -16,7 +17,7 @@ import io.github.sds100.keymapper.system.network.NetworkAdapter
1617
import io.github.sds100.keymapper.system.phone.PhoneAdapter
1718
import io.github.sds100.keymapper.system.power.PowerAdapter
1819
import kotlinx.coroutines.flow.Flow
19-
import kotlinx.coroutines.flow.flowOf
20+
import kotlinx.coroutines.flow.emptyFlow
2021
import kotlinx.coroutines.flow.map
2122
import kotlinx.coroutines.flow.merge
2223

@@ -79,9 +80,6 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor(
7980
networkAdapter.connectedWifiSSIDFlow.map { dependency }
8081
ConstraintDependency.WIFI_STATE -> networkAdapter.isWifiEnabledFlow().map { dependency }
8182
ConstraintDependency.CHOSEN_IME -> inputMethodAdapter.chosenIme.map { dependency }
82-
ConstraintDependency.KEYBOARD_STATE ->
83-
// TODO: Implement keyboard state detection
84-
flowOf(dependency)
8583
ConstraintDependency.DEVICE_LOCKED_STATE ->
8684
lockScreenAdapter.isLockedFlow().map { dependency }
8785

@@ -93,7 +91,14 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor(
9391

9492
ConstraintDependency.PHONE_STATE -> phoneAdapter.callStateFlow.map { dependency }
9593
ConstraintDependency.CHARGING_STATE -> powerAdapter.isCharging.map { dependency }
96-
ConstraintDependency.HINGE_STATE -> foldableAdapter.hingeState.map { dependency }
94+
ConstraintDependency.HINGE_STATE ->
95+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
96+
foldableAdapter.hingeState.map { dependency }
97+
} else {
98+
emptyFlow()
99+
}
100+
ConstraintDependency.KEYBOARD_VISIBLE ->
101+
accessibilityService.isInputMethodVisible.map { dependency }
97102
}
98103
}
99104
}

base/src/main/java/io/github/sds100/keymapper/base/sorting/comparators/KeyMapConstraintsComparator.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ class KeyMapConstraintsComparator(
138138

139139
ConstraintData.HingeClosed -> Success("")
140140
ConstraintData.HingeOpen -> Success("")
141+
ConstraintData.KeyboardNotShowing -> Success("")
142+
ConstraintData.KeyboardShowing -> Success("")
141143
}
142144
}
143145
}

base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import android.os.HandlerThread
1616
import android.view.KeyEvent
1717
import android.view.accessibility.AccessibilityEvent
1818
import android.view.accessibility.AccessibilityNodeInfo
19+
import android.view.accessibility.AccessibilityWindowInfo
1920
import android.view.inputmethod.EditorInfo
2021
import androidx.annotation.RequiresApi
2122
import androidx.core.content.getSystemService
@@ -113,6 +114,13 @@ abstract class BaseAccessibilityService :
113114
override val isKeyboardHidden: Flow<Boolean>
114115
get() = _isKeyboardHidden
115116

117+
private val _isInputMethodVisible by lazy {
118+
MutableStateFlow(isImeWindowVisible())
119+
}
120+
121+
override val isInputMethodVisible: Flow<Boolean>
122+
get() = _isInputMethodVisible
123+
116124
override var serviceFlags: Int?
117125
get() = serviceInfo?.flags
118126
set(value) {
@@ -273,6 +281,7 @@ abstract class BaseAccessibilityService :
273281
}
274282

275283
_activeWindowPackage.update { rootNode?.packageName?.toString() }
284+
_isInputMethodVisible.update { isImeWindowVisible() }
276285
}
277286

278287
getController()?.onAccessibilityEvent(event)
@@ -540,4 +549,11 @@ abstract class BaseAccessibilityService :
540549

541550
return Success(Unit)
542551
}
552+
553+
fun isImeWindowVisible(): Boolean {
554+
val imeWindow: AccessibilityWindowInfo? =
555+
windows.find { it.type == AccessibilityWindowInfo.TYPE_INPUT_METHOD }
556+
557+
return imeWindow != null && imeWindow.root?.isVisibleToUser == true
558+
}
543559
}

base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/IAccessibilityService.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,16 @@ interface IAccessibilityService : SwitchImeInterface {
4949

5050
fun hideKeyboard()
5151
fun showKeyboard()
52+
53+
/**
54+
* Whether the keyboard is force hidden by the accessibility service SoftKeyboardController
55+
*/
5256
val isKeyboardHidden: Flow<Boolean>
5357

54-
fun disableSelf()
58+
/**
59+
* Whether the input method is visible on the screen.
60+
*/
61+
val isInputMethodVisible: Flow<Boolean>
5562

5663
fun findFocussedNode(focus: Int): AccessibilityNodeModel?
5764
}

base/src/main/java/io/github/sds100/keymapper/base/system/inputmethod/AutoSwitchImeController.kt

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package io.github.sds100.keymapper.base.system.inputmethod
22

33
import android.os.Build
44
import android.view.accessibility.AccessibilityEvent
5-
import android.view.accessibility.AccessibilityWindowInfo
65
import android.view.inputmethod.EditorInfo
76
import androidx.annotation.RequiresApi
87
import dagger.assisted.Assisted
@@ -168,7 +167,7 @@ class AutoSwitchImeController @AssistedInject constructor(
168167
return
169168
}
170169

171-
val isInputStarted = isImeWindowVisible()
170+
val isInputStarted = service.isImeWindowVisible()
172171

173172
if (isInputStarted) {
174173
if (chooseIncompatibleIme()) {
@@ -182,13 +181,6 @@ class AutoSwitchImeController @AssistedInject constructor(
182181
}
183182
}
184183

185-
private fun isImeWindowVisible(): Boolean {
186-
val imeWindow: AccessibilityWindowInfo? =
187-
service.windows.find { it.type == AccessibilityWindowInfo.TYPE_INPUT_METHOD }
188-
189-
return imeWindow != null && imeWindow.root?.isVisibleToUser == true
190-
}
191-
192184
fun onStartInput(attribute: EditorInfo, restarting: Boolean) {
193185
if (!changeImeOnStartInput.value) {
194186
return

0 commit comments

Comments
 (0)