mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-22 23:29:10 +00:00
more readable kotlin files, get rid of much !!
This commit is contained in:
parent
9c091e7f31
commit
6e46f5c9f6
6 changed files with 199 additions and 221 deletions
|
@ -18,9 +18,9 @@ import org.dslul.openboard.inputmethod.latin.SuggestedWords
|
|||
import org.dslul.openboard.inputmethod.latin.utils.InputTypeUtils
|
||||
|
||||
class AccessibilityUtils private constructor() {
|
||||
private var mContext: Context? = null
|
||||
private var mAccessibilityManager: AccessibilityManager? = null
|
||||
private var mAudioManager: AudioManager? = null
|
||||
private lateinit var mContext: Context
|
||||
private lateinit var mAccessibilityManager: AccessibilityManager
|
||||
private lateinit var mAudioManager: AudioManager
|
||||
/** The most recent auto-correction. */
|
||||
private var mAutoCorrectionWord: String? = null
|
||||
/** The most recent typed word for auto-correction. */
|
||||
|
@ -39,7 +39,7 @@ class AccessibilityUtils private constructor() {
|
|||
* @return `true` if accessibility is enabled.
|
||||
*/
|
||||
val isAccessibilityEnabled: Boolean
|
||||
get() = ENABLE_ACCESSIBILITY && mAccessibilityManager!!.isEnabled
|
||||
get() = ENABLE_ACCESSIBILITY && mAccessibilityManager.isEnabled
|
||||
|
||||
/**
|
||||
* Returns `true` if touch exploration is enabled. Currently, this
|
||||
|
@ -49,7 +49,7 @@ class AccessibilityUtils private constructor() {
|
|||
* @return `true` if touch exploration is enabled.
|
||||
*/
|
||||
val isTouchExplorationEnabled: Boolean
|
||||
get() = isAccessibilityEnabled && mAccessibilityManager!!.isTouchExplorationEnabled
|
||||
get() = isAccessibilityEnabled && mAccessibilityManager.isTouchExplorationEnabled
|
||||
|
||||
/**
|
||||
* Returns whether the device should obscure typed password characters.
|
||||
|
@ -61,12 +61,12 @@ class AccessibilityUtils private constructor() {
|
|||
if (editorInfo == null) return false
|
||||
// The user can optionally force speaking passwords.
|
||||
if (Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD != null) {
|
||||
val speakPassword = Settings.Secure.getInt(mContext!!.contentResolver,
|
||||
val speakPassword = Settings.Secure.getInt(mContext.contentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0
|
||||
if (speakPassword) return false
|
||||
}
|
||||
// Always speak if the user is listening through headphones.
|
||||
return if (mAudioManager!!.isWiredHeadsetOn || mAudioManager!!.isBluetoothA2dpOn) {
|
||||
return if (mAudioManager.isWiredHeadsetOn || mAudioManager.isBluetoothA2dpOn) {
|
||||
false
|
||||
} else InputTypeUtils.isPasswordInputType(editorInfo.inputType)
|
||||
// Don't speak if the IME is connected to a password field.
|
||||
|
@ -105,9 +105,9 @@ class AccessibilityUtils private constructor() {
|
|||
if (!TextUtils.isEmpty(mAutoCorrectionWord)) {
|
||||
if (!TextUtils.equals(mAutoCorrectionWord, mTypedWord)) {
|
||||
return if (shouldObscure) { // This should never happen, but just in case...
|
||||
mContext!!.getString(R.string.spoken_auto_correct_obscured,
|
||||
mContext.getString(R.string.spoken_auto_correct_obscured,
|
||||
keyCodeDescription)
|
||||
} else mContext!!.getString(R.string.spoken_auto_correct, keyCodeDescription,
|
||||
} else mContext.getString(R.string.spoken_auto_correct, keyCodeDescription,
|
||||
mTypedWord, mAutoCorrectionWord)
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ class AccessibilityUtils private constructor() {
|
|||
* @param text The text to speak.
|
||||
*/
|
||||
fun announceForAccessibility(view: View, text: CharSequence?) {
|
||||
if (!mAccessibilityManager!!.isEnabled) {
|
||||
if (!mAccessibilityManager.isEnabled) {
|
||||
Log.e(TAG, "Attempted to speak when accessibility was disabled!")
|
||||
return
|
||||
}
|
||||
|
@ -154,10 +154,9 @@ class AccessibilityUtils private constructor() {
|
|||
* @param editorInfo The input connection's editor info attribute.
|
||||
* @param restarting Whether the connection is being restarted.
|
||||
*/
|
||||
fun onStartInputViewInternal(view: View, editorInfo: EditorInfo?,
|
||||
restarting: Boolean) {
|
||||
fun onStartInputViewInternal(view: View, editorInfo: EditorInfo?, restarting: Boolean) {
|
||||
if (shouldObscureInput(editorInfo)) {
|
||||
val text = mContext!!.getText(R.string.spoken_use_headphones)
|
||||
val text = mContext.getText(R.string.spoken_use_headphones)
|
||||
announceForAccessibility(view, text)
|
||||
}
|
||||
}
|
||||
|
@ -169,8 +168,8 @@ class AccessibilityUtils private constructor() {
|
|||
* @param event The event to send.
|
||||
*/
|
||||
fun requestSendAccessibilityEvent(event: AccessibilityEvent?) {
|
||||
if (mAccessibilityManager!!.isEnabled) {
|
||||
mAccessibilityManager!!.sendAccessibilityEvent(event)
|
||||
if (mAccessibilityManager.isEnabled) {
|
||||
mAccessibilityManager.sendAccessibilityEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,10 +179,10 @@ class AccessibilityUtils private constructor() {
|
|||
private val PACKAGE = AccessibilityUtils::class.java.getPackage()!!.name
|
||||
val instance = AccessibilityUtils()
|
||||
/*
|
||||
* Setting this constant to {@code false} will disable all keyboard
|
||||
* accessibility code, regardless of whether Accessibility is turned on in
|
||||
* the system settings. It should ONLY be used in the event of an emergency.
|
||||
*/
|
||||
* Setting this constant to {@code false} will disable all keyboard
|
||||
* accessibility code, regardless of whether Accessibility is turned on in
|
||||
* the system settings. It should ONLY be used in the event of an emergency.
|
||||
*/
|
||||
private const val ENABLE_ACCESSIBILITY = true
|
||||
|
||||
@JvmStatic
|
||||
|
|
|
@ -15,7 +15,30 @@ import java.util.*
|
|||
|
||||
internal class KeyCodeDescriptionMapper private constructor() {
|
||||
// Sparse array of spoken description resource IDs indexed by key codes
|
||||
private val mKeyCodeMap = SparseIntArray()
|
||||
private val mKeyCodeMap = SparseIntArray().apply {
|
||||
// Special non-character codes defined in Keyboard
|
||||
put(Constants.CODE_SPACE, R.string.spoken_description_space)
|
||||
put(Constants.CODE_DELETE, R.string.spoken_description_delete)
|
||||
put(Constants.CODE_ENTER, R.string.spoken_description_return)
|
||||
put(Constants.CODE_SETTINGS, R.string.spoken_description_settings)
|
||||
put(Constants.CODE_SHIFT, R.string.spoken_description_shift)
|
||||
put(Constants.CODE_SHORTCUT, R.string.spoken_description_mic)
|
||||
put(Constants.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol)
|
||||
put(Constants.CODE_TAB, R.string.spoken_description_tab)
|
||||
put(Constants.CODE_LANGUAGE_SWITCH, R.string.spoken_description_language_switch)
|
||||
put(Constants.CODE_ACTION_NEXT, R.string.spoken_description_action_next)
|
||||
put(Constants.CODE_ACTION_PREVIOUS, R.string.spoken_description_action_previous)
|
||||
put(Constants.CODE_EMOJI, R.string.spoken_description_emoji)
|
||||
// Because the upper-case and lower-case mappings of the following letters is depending on
|
||||
// the locale, the upper case descriptions should be defined here. The lower case
|
||||
// descriptions are handled in {@link #getSpokenLetterDescriptionId(Context,int)}.
|
||||
// U+0049: "I" LATIN CAPITAL LETTER I
|
||||
// U+0069: "i" LATIN SMALL LETTER I
|
||||
// U+0130: "İ" LATIN CAPITAL LETTER I WITH DOT ABOVE
|
||||
// U+0131: "ı" LATIN SMALL LETTER DOTLESS I
|
||||
put(0x0049, R.string.spoken_letter_0049)
|
||||
put(0x0130, R.string.spoken_letter_0130)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the localized description of the action performed by a specified
|
||||
|
@ -27,8 +50,7 @@ internal class KeyCodeDescriptionMapper private constructor() {
|
|||
* @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured.
|
||||
* @return a character sequence describing the action performed by pressing the key
|
||||
*/
|
||||
fun getDescriptionForKey(context: Context, keyboard: Keyboard?,
|
||||
key: Key, shouldObscure: Boolean): String? {
|
||||
fun getDescriptionForKey(context: Context, keyboard: Keyboard?, key: Key, shouldObscure: Boolean): String? {
|
||||
val code = key.code
|
||||
if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
|
||||
val description = getDescriptionForSwitchAlphaSymbol(context, keyboard)
|
||||
|
@ -39,17 +61,19 @@ internal class KeyCodeDescriptionMapper private constructor() {
|
|||
if (code == Constants.CODE_SHIFT) {
|
||||
return getDescriptionForShiftKey(context, keyboard)
|
||||
}
|
||||
if (code == Constants.CODE_ENTER) { // The following function returns the correct description in all action and
|
||||
// regular enter cases, taking care of all modes.
|
||||
if (code == Constants.CODE_ENTER) {
|
||||
// The following function returns the correct description in all action and
|
||||
// regular enter cases, taking care of all modes.
|
||||
return getDescriptionForActionKey(context, keyboard, key)
|
||||
}
|
||||
if (code == Constants.CODE_OUTPUT_TEXT) {
|
||||
val outputText = key.outputText
|
||||
val outputText = key.outputText ?: return context.getString(R.string.spoken_description_unknown)
|
||||
val description = getSpokenEmoticonDescription(context, outputText)
|
||||
return if (TextUtils.isEmpty(description)) outputText else description
|
||||
}
|
||||
// Just attempt to speak the description.
|
||||
if (code != Constants.CODE_UNSPECIFIED) { // If the key description should be obscured, now is the time to do it.
|
||||
if (code != Constants.CODE_UNSPECIFIED) {
|
||||
// If the key description should be obscured, now is the time to do it.
|
||||
val isDefinedNonCtrl = (Character.isDefined(code)
|
||||
&& !Character.isISOControl(code))
|
||||
if (shouldObscure && isDefinedNonCtrl) {
|
||||
|
@ -74,7 +98,8 @@ internal class KeyCodeDescriptionMapper private constructor() {
|
|||
* @param codePoint The code point from which to obtain a description.
|
||||
* @return a character sequence describing the code point.
|
||||
*/
|
||||
fun getDescriptionForCodePoint(context: Context, codePoint: Int): String? { // If the key description should be obscured, now is the time to do it.
|
||||
fun getDescriptionForCodePoint(context: Context, codePoint: Int): String? {
|
||||
// If the key description should be obscured, now is the time to do it.
|
||||
val index = mKeyCodeMap.indexOfKey(codePoint)
|
||||
if (index >= 0) {
|
||||
return context.getString(mKeyCodeMap.valueAt(index))
|
||||
|
@ -141,8 +166,7 @@ internal class KeyCodeDescriptionMapper private constructor() {
|
|||
val resourceName = String.format(Locale.ROOT, resourceNameFormat, code)
|
||||
val resources = context.resources
|
||||
// Note that the resource package name may differ from the context package name.
|
||||
val resourcePackageName = resources.getResourcePackageName(
|
||||
R.string.spoken_description_unknown)
|
||||
val resourcePackageName = resources.getResourcePackageName(R.string.spoken_description_unknown)
|
||||
val resId = resources.getIdentifier(resourceName, "string", resourcePackageName)
|
||||
if (resId != 0) {
|
||||
mKeyCodeMap.append(code, resId)
|
||||
|
@ -170,12 +194,8 @@ internal class KeyCodeDescriptionMapper private constructor() {
|
|||
* @param keyboard The keyboard on which the key resides.
|
||||
* @return a character sequence describing the action performed by pressing the key
|
||||
*/
|
||||
private fun getDescriptionForSwitchAlphaSymbol(context: Context,
|
||||
keyboard: Keyboard?): String? {
|
||||
val keyboardId = keyboard!!.mId
|
||||
val elementId = keyboardId.mElementId
|
||||
val resId: Int
|
||||
resId = when (elementId) {
|
||||
private fun getDescriptionForSwitchAlphaSymbol(context: Context, keyboard: Keyboard?): String? {
|
||||
val resId = when (val elementId = keyboard?.mId?.mElementId) {
|
||||
KeyboardId.ELEMENT_ALPHABET, KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED, KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED, KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED, KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED -> R.string.spoken_description_to_symbol
|
||||
KeyboardId.ELEMENT_SYMBOLS, KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> R.string.spoken_description_to_alpha
|
||||
KeyboardId.ELEMENT_PHONE -> R.string.spoken_description_to_symbol
|
||||
|
@ -195,12 +215,8 @@ internal class KeyCodeDescriptionMapper private constructor() {
|
|||
* @param keyboard The keyboard on which the key resides.
|
||||
* @return A context-sensitive description of the "Shift" key.
|
||||
*/
|
||||
private fun getDescriptionForShiftKey(context: Context,
|
||||
keyboard: Keyboard?): String {
|
||||
val keyboardId = keyboard!!.mId
|
||||
val elementId = keyboardId.mElementId
|
||||
val resId: Int
|
||||
resId = when (elementId) {
|
||||
private fun getDescriptionForShiftKey(context: Context, keyboard: Keyboard?): String {
|
||||
val resId: Int = when (keyboard?.mId?.mElementId) {
|
||||
KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED, KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED -> R.string.spoken_description_caps_lock
|
||||
KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED, KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED -> R.string.spoken_description_shift_shifted
|
||||
KeyboardId.ELEMENT_SYMBOLS -> R.string.spoken_description_symbols_shift
|
||||
|
@ -218,16 +234,12 @@ internal class KeyCodeDescriptionMapper private constructor() {
|
|||
* @param key The key to describe.
|
||||
* @return Returns a context-sensitive description of the "Enter" action key.
|
||||
*/
|
||||
private fun getDescriptionForActionKey(context: Context, keyboard: Keyboard?,
|
||||
key: Key): String {
|
||||
val keyboardId = keyboard!!.mId
|
||||
val actionId = keyboardId.imeAction()
|
||||
val resId: Int
|
||||
private fun getDescriptionForActionKey(context: Context, keyboard: Keyboard?, key: Key): String {
|
||||
// Always use the label, if available.
|
||||
if (!TextUtils.isEmpty(key.label)) {
|
||||
return key.label!!.trim { it <= ' ' }
|
||||
}
|
||||
resId = when (actionId) {
|
||||
val resId = when (keyboard?.mId?.imeAction()) {
|
||||
EditorInfo.IME_ACTION_SEARCH -> R.string.label_search_key
|
||||
EditorInfo.IME_ACTION_GO -> R.string.label_go_key
|
||||
EditorInfo.IME_ACTION_SEND -> R.string.label_send_key
|
||||
|
@ -240,10 +252,9 @@ internal class KeyCodeDescriptionMapper private constructor() {
|
|||
}
|
||||
|
||||
// TODO: Remove this method once TTS supports emoticon verbalization.
|
||||
private fun getSpokenEmoticonDescription(context: Context,
|
||||
outputText: String?): String? {
|
||||
private fun getSpokenEmoticonDescription(context: Context, outputText: String): String? {
|
||||
val sb = StringBuilder(SPOKEN_EMOTICON_RESOURCE_NAME_PREFIX)
|
||||
val textLength = outputText!!.length
|
||||
val textLength = outputText.length
|
||||
var index = 0
|
||||
while (index < textLength) {
|
||||
val codePoint = outputText.codePointAt(index)
|
||||
|
@ -253,36 +264,10 @@ internal class KeyCodeDescriptionMapper private constructor() {
|
|||
val resourceName = sb.toString()
|
||||
val resources = context.resources
|
||||
// Note that the resource package name may differ from the context package name.
|
||||
val resourcePackageName = resources.getResourcePackageName(
|
||||
R.string.spoken_description_unknown)
|
||||
val resourcePackageName = resources.getResourcePackageName(R.string.spoken_description_unknown)
|
||||
val resId = resources.getIdentifier(resourceName, "string", resourcePackageName)
|
||||
return if (resId == 0) null else resources.getString(resId)
|
||||
}
|
||||
}
|
||||
|
||||
init { // Special non-character codes defined in Keyboard
|
||||
mKeyCodeMap.put(Constants.CODE_SPACE, R.string.spoken_description_space)
|
||||
mKeyCodeMap.put(Constants.CODE_DELETE, R.string.spoken_description_delete)
|
||||
mKeyCodeMap.put(Constants.CODE_ENTER, R.string.spoken_description_return)
|
||||
mKeyCodeMap.put(Constants.CODE_SETTINGS, R.string.spoken_description_settings)
|
||||
mKeyCodeMap.put(Constants.CODE_SHIFT, R.string.spoken_description_shift)
|
||||
mKeyCodeMap.put(Constants.CODE_SHORTCUT, R.string.spoken_description_mic)
|
||||
mKeyCodeMap.put(Constants.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol)
|
||||
mKeyCodeMap.put(Constants.CODE_TAB, R.string.spoken_description_tab)
|
||||
mKeyCodeMap.put(Constants.CODE_LANGUAGE_SWITCH,
|
||||
R.string.spoken_description_language_switch)
|
||||
mKeyCodeMap.put(Constants.CODE_ACTION_NEXT, R.string.spoken_description_action_next)
|
||||
mKeyCodeMap.put(Constants.CODE_ACTION_PREVIOUS,
|
||||
R.string.spoken_description_action_previous)
|
||||
mKeyCodeMap.put(Constants.CODE_EMOJI, R.string.spoken_description_emoji)
|
||||
// Because the upper-case and lower-case mappings of the following letters is depending on
|
||||
// the locale, the upper case descriptions should be defined here. The lower case
|
||||
// descriptions are handled in {@link #getSpokenLetterDescriptionId(Context,int)}.
|
||||
// U+0049: "I" LATIN CAPITAL LETTER I
|
||||
// U+0069: "i" LATIN SMALL LETTER I
|
||||
// U+0130: "İ" LATIN CAPITAL LETTER I WITH DOT ABOVE
|
||||
// U+0131: "ı" LATIN SMALL LETTER DOTLESS I
|
||||
mKeyCodeMap.put(0x0049, R.string.spoken_letter_0049)
|
||||
mKeyCodeMap.put(0x0130, R.string.spoken_letter_0130)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,12 +25,14 @@ import org.dslul.openboard.inputmethod.keyboard.KeyboardView
|
|||
*
|
||||
* @param <KV> The keyboard view class type.
|
||||
</KV> */
|
||||
open class KeyboardAccessibilityDelegate<KV : KeyboardView?>(protected val mKeyboardView: KV, protected val mKeyDetector: KeyDetector) : AccessibilityDelegateCompat() {
|
||||
open class KeyboardAccessibilityDelegate<KV : KeyboardView>(
|
||||
protected val mKeyboardView: KV,
|
||||
protected val mKeyDetector: KeyDetector
|
||||
) : AccessibilityDelegateCompat() {
|
||||
private var mKeyboard: Keyboard? = null
|
||||
private var mAccessibilityNodeProvider: KeyboardAccessibilityNodeProvider<KV>? = null
|
||||
private var mLastHoverKey: Key? = null
|
||||
|
||||
|
||||
protected open var lastHoverKey: Key?
|
||||
get() = mLastHoverKey
|
||||
set(key) {
|
||||
|
@ -45,14 +47,14 @@ open class KeyboardAccessibilityDelegate<KV : KeyboardView?>(protected val mKeyb
|
|||
* @param keyboard The keyboard that is being set to the wrapping view.
|
||||
*/
|
||||
open var keyboard: Keyboard?
|
||||
get() = mKeyboard
|
||||
set(keyboard) {
|
||||
if (keyboard == null) {
|
||||
return
|
||||
get() = mKeyboard
|
||||
set(keyboard) {
|
||||
if (keyboard == null) {
|
||||
return
|
||||
}
|
||||
mAccessibilityNodeProvider?.setKeyboard(keyboard)
|
||||
mKeyboard = keyboard
|
||||
}
|
||||
mAccessibilityNodeProvider?.setKeyboard(keyboard)
|
||||
mKeyboard = keyboard
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a window state change event with the specified string resource id.
|
||||
|
@ -63,7 +65,7 @@ open class KeyboardAccessibilityDelegate<KV : KeyboardView?>(protected val mKeyb
|
|||
if (resId == 0) {
|
||||
return
|
||||
}
|
||||
val context = mKeyboardView!!.context
|
||||
val context = mKeyboardView.context
|
||||
sendWindowStateChanged(context.getString(resId))
|
||||
}
|
||||
|
||||
|
@ -74,7 +76,7 @@ open class KeyboardAccessibilityDelegate<KV : KeyboardView?>(protected val mKeyb
|
|||
*/
|
||||
protected fun sendWindowStateChanged(text: String?) {
|
||||
val stateChange = AccessibilityUtils.obtainEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)
|
||||
mKeyboardView!!.onInitializeAccessibilityEvent(stateChange)
|
||||
mKeyboardView.onInitializeAccessibilityEvent(stateChange)
|
||||
stateChange.text.add(text)
|
||||
stateChange.contentDescription = null
|
||||
val parent = mKeyboardView.parent
|
||||
|
@ -91,19 +93,21 @@ open class KeyboardAccessibilityDelegate<KV : KeyboardView?>(protected val mKeyb
|
|||
*/
|
||||
override fun getAccessibilityNodeProvider(host: View): KeyboardAccessibilityNodeProvider<KV> {
|
||||
return accessibilityNodeProvider
|
||||
}// Instantiate the provide only when requested. Since the system
|
||||
// will call this method multiple times it is a good practice to
|
||||
// cache the provider instance.
|
||||
}
|
||||
// Instantiate the provide only when requested. Since the system
|
||||
// will call this method multiple times it is a good practice to
|
||||
// cache the provider instance.
|
||||
|
||||
/**
|
||||
* @return A lazily-instantiated node provider for this view delegate.
|
||||
*/
|
||||
protected val accessibilityNodeProvider: KeyboardAccessibilityNodeProvider<KV>
|
||||
get() { // Instantiate the provide only when requested. Since the system
|
||||
// will call this method multiple times it is a good practice to
|
||||
// cache the provider instance.
|
||||
return mAccessibilityNodeProvider ?: KeyboardAccessibilityNodeProvider(mKeyboardView, this)
|
||||
}
|
||||
get() {
|
||||
// Instantiate the provide only when requested. Since the system
|
||||
// will call this method multiple times it is a good practice to
|
||||
// cache the provider instance.
|
||||
return mAccessibilityNodeProvider ?: KeyboardAccessibilityNodeProvider(mKeyboardView, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a key that a hover event is on.
|
||||
|
@ -177,7 +181,7 @@ open class KeyboardAccessibilityDelegate<KV : KeyboardView?>(protected val mKeyb
|
|||
lastKey?.let { onHoverExitFrom(it) }
|
||||
val key = getHoverKeyOf(event)
|
||||
// Make sure we're not getting an EXIT event because the user slid
|
||||
// off the keyboard area, then force a key press.
|
||||
// off the keyboard area, then force a key press.
|
||||
key?.let { performClickOn(it)
|
||||
onHoverExitFrom(it) }
|
||||
mLastHoverKey = null
|
||||
|
@ -208,7 +212,7 @@ open class KeyboardAccessibilityDelegate<KV : KeyboardView?>(protected val mKeyb
|
|||
val eventTime = SystemClock.uptimeMillis()
|
||||
val touchEvent = MotionEvent.obtain(
|
||||
eventTime, eventTime, touchAction, x.toFloat(), y.toFloat(), 0 /* metaState */)
|
||||
mKeyboardView!!.onTouchEvent(touchEvent)
|
||||
mKeyboardView.onTouchEvent(touchEvent)
|
||||
touchEvent.recycle()
|
||||
}
|
||||
|
||||
|
@ -222,7 +226,7 @@ open class KeyboardAccessibilityDelegate<KV : KeyboardView?>(protected val mKeyb
|
|||
Log.d(TAG, "onHoverEnterTo: key=$key")
|
||||
}
|
||||
key.onPressed()
|
||||
mKeyboardView!!.invalidateKey(key)
|
||||
mKeyboardView.invalidateKey(key)
|
||||
val provider = accessibilityNodeProvider
|
||||
provider.onHoverEnterTo(key)
|
||||
provider.performActionForKey(key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS)
|
||||
|
@ -245,7 +249,7 @@ open class KeyboardAccessibilityDelegate<KV : KeyboardView?>(protected val mKeyb
|
|||
Log.d(TAG, "onHoverExitFrom: key=$key")
|
||||
}
|
||||
key.onReleased()
|
||||
mKeyboardView!!.invalidateKey(key)
|
||||
mKeyboardView.invalidateKey(key)
|
||||
val provider = accessibilityNodeProvider
|
||||
provider.onHoverExitFrom(key)
|
||||
}
|
||||
|
@ -255,17 +259,18 @@ open class KeyboardAccessibilityDelegate<KV : KeyboardView?>(protected val mKeyb
|
|||
*
|
||||
* @param key A key to be long pressed on.
|
||||
*/
|
||||
open fun performLongClickOn(key: Key) { // A extended class should override this method to implement long press.
|
||||
open fun performLongClickOn(key: Key) {
|
||||
// A extended class should override this method to implement long press.
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = KeyboardAccessibilityDelegate::class.java.simpleName
|
||||
const val DEBUG_HOVER = false
|
||||
const val HOVER_EVENT_POINTER_ID = 0
|
||||
private val TAG = KeyboardAccessibilityDelegate::class.java.simpleName
|
||||
const val DEBUG_HOVER = false
|
||||
const val HOVER_EVENT_POINTER_ID = 0
|
||||
}
|
||||
|
||||
init {
|
||||
// Ensure that the view has an accessibility delegate.
|
||||
ViewCompat.setAccessibilityDelegate(mKeyboardView!!, this)
|
||||
ViewCompat.setAccessibilityDelegate(mKeyboardView, this) // todo: see the warning, this may be bad
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.dslul.openboard.inputmethod.accessibility
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
|
@ -28,10 +27,14 @@ import org.dslul.openboard.inputmethod.latin.settings.Settings
|
|||
* virtual views, thus conveying their logical structure.
|
||||
*
|
||||
*/
|
||||
class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
||||
delegate: KeyboardAccessibilityDelegate<KV>) : AccessibilityNodeProviderCompat() {
|
||||
private val mKeyCodeDescriptionMapper: KeyCodeDescriptionMapper
|
||||
private val mAccessibilityUtils: AccessibilityUtils
|
||||
class KeyboardAccessibilityNodeProvider<KV : KeyboardView>(
|
||||
/** The keyboard view to provide an accessibility node info. */
|
||||
private val mKeyboardView: KV,
|
||||
/** The accessibility delegate. */
|
||||
private val mDelegate: KeyboardAccessibilityDelegate<KV>
|
||||
) : AccessibilityNodeProviderCompat() {
|
||||
private val mKeyCodeDescriptionMapper: KeyCodeDescriptionMapper = KeyCodeDescriptionMapper.instance
|
||||
private val mAccessibilityUtils: AccessibilityUtils = AccessibilityUtils.instance
|
||||
/** Temporary rect used to calculate in-screen bounds. */
|
||||
private val mTempBoundsInScreen = Rect()
|
||||
/** The parent view's cached on-screen location. */
|
||||
|
@ -40,12 +43,8 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
private var mAccessibilityFocusedView = UNDEFINED
|
||||
/** The virtual view identifier for the hovering node. */
|
||||
private var mHoveringNodeId = UNDEFINED
|
||||
/** The keyboard view to provide an accessibility node info. */
|
||||
private val mKeyboardView: KV
|
||||
/** The accessibility delegate. */
|
||||
private val mDelegate: KeyboardAccessibilityDelegate<KV>
|
||||
/** The current keyboard. */
|
||||
private var mKeyboard: Keyboard? = null
|
||||
private var mKeyboard: Keyboard? = mKeyboardView.keyboard
|
||||
|
||||
/**
|
||||
* Sets the keyboard represented by this node provider.
|
||||
|
@ -57,10 +56,8 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
}
|
||||
|
||||
private fun getKeyOf(virtualViewId: Int): Key? {
|
||||
if (mKeyboard == null) {
|
||||
return null
|
||||
}
|
||||
val sortedKeys = mKeyboard!!.sortedKeys
|
||||
val keyboard = mKeyboard ?: return null
|
||||
val sortedKeys = keyboard.sortedKeys
|
||||
// Use a virtual view id as an index of the sorted keys list.
|
||||
return if (virtualViewId >= 0 && virtualViewId < sortedKeys.size) {
|
||||
sortedKeys[virtualViewId]
|
||||
|
@ -68,10 +65,8 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
}
|
||||
|
||||
private fun getVirtualViewIdOf(key: Key): Int {
|
||||
if (mKeyboard == null) {
|
||||
return View.NO_ID
|
||||
}
|
||||
val sortedKeys = mKeyboard!!.sortedKeys
|
||||
val keyboard = mKeyboard ?: return View.NO_ID
|
||||
val sortedKeys = keyboard.sortedKeys
|
||||
val size = sortedKeys.size
|
||||
for (index in 0 until size) {
|
||||
if (sortedKeys[index] === key) { // Use an index of the sorted keys list as a virtual view id.
|
||||
|
@ -94,7 +89,7 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
val virtualViewId = getVirtualViewIdOf(key)
|
||||
val keyDescription = getKeyDescription(key)
|
||||
val event = AccessibilityUtils.obtainEvent(eventType)
|
||||
event.packageName = mKeyboardView!!.context.packageName
|
||||
event.packageName = mKeyboardView.context.packageName
|
||||
event.className = key.javaClass.name
|
||||
event.contentDescription = keyDescription
|
||||
event.isEnabled = true
|
||||
|
@ -109,8 +104,8 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
return
|
||||
}
|
||||
// Start hovering on the key. Because our accessibility model is lift-to-type, we should
|
||||
// report the node info without click and long click actions to avoid unnecessary
|
||||
// announcements.
|
||||
// report the node info without click and long click actions to avoid unnecessary
|
||||
// announcements.
|
||||
mHoveringNodeId = id
|
||||
// Invalidate the node info of the key.
|
||||
sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)
|
||||
|
@ -120,7 +115,7 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
fun onHoverExitFrom(key: Key) {
|
||||
mHoveringNodeId = UNDEFINED
|
||||
// Invalidate the node info of the key to be able to revert the change we have done
|
||||
// in {@link #onHoverEnterTo(Key)}.
|
||||
// in {@link #onHoverEnterTo(Key)}.
|
||||
sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)
|
||||
sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT)
|
||||
}
|
||||
|
@ -152,13 +147,15 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
if (virtualViewId == UNDEFINED) {
|
||||
return null
|
||||
}
|
||||
if (virtualViewId == View.NO_ID) { // We are requested to create an AccessibilityNodeInfo describing
|
||||
// this View, i.e. the root of the virtual sub-tree.
|
||||
val keyboard = mKeyboard ?: return null
|
||||
if (virtualViewId == View.NO_ID) {
|
||||
// We are requested to create an AccessibilityNodeInfo describing
|
||||
// this View, i.e. the root of the virtual sub-tree.
|
||||
val rootInfo = AccessibilityNodeInfoCompat.obtain(mKeyboardView)
|
||||
ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView!!, rootInfo)
|
||||
ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, rootInfo)
|
||||
updateParentLocation()
|
||||
// Add the virtual children of the root View.
|
||||
val sortedKeys = mKeyboard!!.sortedKeys
|
||||
val sortedKeys = keyboard.sortedKeys
|
||||
val size = sortedKeys.size
|
||||
for (index in 0 until size) {
|
||||
val key = sortedKeys[index]
|
||||
|
@ -180,12 +177,11 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
val boundsInParent = key.hitBox
|
||||
// Calculate the key's in-screen bounds.
|
||||
mTempBoundsInScreen.set(boundsInParent)
|
||||
mTempBoundsInScreen.offset(
|
||||
CoordinateUtils.x(mParentLocation), CoordinateUtils.y(mParentLocation))
|
||||
mTempBoundsInScreen.offset(CoordinateUtils.x(mParentLocation), CoordinateUtils.y(mParentLocation))
|
||||
val boundsInScreen = mTempBoundsInScreen
|
||||
// Obtain and initialize an AccessibilityNodeInfo with information about the virtual view.
|
||||
val info = AccessibilityNodeInfoCompat.obtain()
|
||||
info.packageName = mKeyboardView!!.context.packageName
|
||||
info.packageName = mKeyboardView.context.packageName
|
||||
info.className = key.javaClass.name
|
||||
info.contentDescription = keyDescription
|
||||
info.setBoundsInParent(boundsInParent)
|
||||
|
@ -195,7 +191,7 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
info.isEnabled = key.isEnabled
|
||||
info.isVisibleToUser = true
|
||||
// Don't add ACTION_CLICK and ACTION_LONG_CLOCK actions while hovering on the key.
|
||||
// See {@link #onHoverEnterTo(Key)} and {@link #onHoverExitFrom(Key)}.
|
||||
// See {@link #onHoverEnterTo(Key)} and {@link #onHoverExitFrom(Key)}.
|
||||
if (virtualViewId != mHoveringNodeId) {
|
||||
info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK)
|
||||
if (key.isLongPressEnabled) {
|
||||
|
@ -227,14 +223,12 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
return when (action) {
|
||||
AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS -> {
|
||||
mAccessibilityFocusedView = getVirtualViewIdOf(key)
|
||||
sendAccessibilityEventForKey(
|
||||
key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED)
|
||||
sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED)
|
||||
true
|
||||
}
|
||||
AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS -> {
|
||||
mAccessibilityFocusedView = UNDEFINED
|
||||
sendAccessibilityEventForKey(
|
||||
key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED)
|
||||
sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED)
|
||||
true
|
||||
}
|
||||
AccessibilityNodeInfoCompat.ACTION_CLICK -> {
|
||||
|
@ -269,14 +263,13 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
* @return The context-specific description of the key.
|
||||
*/
|
||||
private fun getKeyDescription(key: Key): String? {
|
||||
val editorInfo = mKeyboard!!.mId.mEditorInfo
|
||||
val editorInfo = mKeyboard?.mId?.mEditorInfo
|
||||
val shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo)
|
||||
val currentSettings = Settings.getInstance().current
|
||||
val keyCodeDescription = mKeyCodeDescriptionMapper.getDescriptionForKey(
|
||||
mKeyboardView!!.context, mKeyboard, key, shouldObscure)
|
||||
mKeyboardView.context, mKeyboard, key, shouldObscure)
|
||||
return if (currentSettings.isWordSeparator(key.code)) {
|
||||
mAccessibilityUtils.getAutoCorrectionDescription(
|
||||
keyCodeDescription, shouldObscure)
|
||||
mAccessibilityUtils.getAutoCorrectionDescription(keyCodeDescription, shouldObscure)
|
||||
} else keyCodeDescription
|
||||
}
|
||||
|
||||
|
@ -284,7 +277,7 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
* Updates the parent's on-screen location.
|
||||
*/
|
||||
private fun updateParentLocation() {
|
||||
mKeyboardView!!.getLocationOnScreen(mParentLocation)
|
||||
mKeyboardView.getLocationOnScreen(mParentLocation)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -293,13 +286,4 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
|
|||
private const val UNDEFINED = Int.MAX_VALUE
|
||||
}
|
||||
|
||||
init {
|
||||
mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.instance
|
||||
mAccessibilityUtils = AccessibilityUtils.instance
|
||||
mKeyboardView = keyboardView
|
||||
mDelegate = delegate
|
||||
// Since this class is constructed lazily, we might not get a subsequent
|
||||
// call to setKeyboard() and therefore need to call it now.
|
||||
setKeyboard(keyboardView!!.keyboard)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,33 +14,18 @@ import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils
|
|||
* This class represents a delegate that can be registered in [MainKeyboardView] to enhance
|
||||
* accessibility support via composition rather via inheritance.
|
||||
*/
|
||||
class MainKeyboardAccessibilityDelegate(mainKeyboardView: MainKeyboardView,
|
||||
keyDetector: KeyDetector) : KeyboardAccessibilityDelegate<MainKeyboardView?>(mainKeyboardView, keyDetector), LongPressTimerCallback {
|
||||
companion object {
|
||||
private val TAG = MainKeyboardAccessibilityDelegate::class.java.simpleName
|
||||
/** Map of keyboard modes to resource IDs. */
|
||||
private val KEYBOARD_MODE_RES_IDS = SparseIntArray()
|
||||
private const val KEYBOARD_IS_HIDDEN = -1
|
||||
|
||||
init {
|
||||
KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATE, R.string.keyboard_mode_date)
|
||||
KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATETIME, R.string.keyboard_mode_date_time)
|
||||
KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_EMAIL, R.string.keyboard_mode_email)
|
||||
KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_IM, R.string.keyboard_mode_im)
|
||||
KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_NUMBER, R.string.keyboard_mode_number)
|
||||
KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_PHONE, R.string.keyboard_mode_phone)
|
||||
KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TEXT, R.string.keyboard_mode_text)
|
||||
KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TIME, R.string.keyboard_mode_time)
|
||||
KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_URL, R.string.keyboard_mode_url)
|
||||
}
|
||||
}
|
||||
|
||||
class MainKeyboardAccessibilityDelegate(
|
||||
mainKeyboardView: MainKeyboardView,
|
||||
keyDetector: KeyDetector
|
||||
) : KeyboardAccessibilityDelegate<MainKeyboardView>(mainKeyboardView, keyDetector), LongPressTimerCallback {
|
||||
/** The most recently set keyboard mode. */
|
||||
private var mLastKeyboardMode = KEYBOARD_IS_HIDDEN
|
||||
// The rectangle region to ignore hover events.
|
||||
private val mBoundsToIgnoreHoverEvent = Rect()
|
||||
private val mAccessibilityLongPressTimer: AccessibilityLongPressTimer// Since this method is called even when accessibility is off, make sure
|
||||
// to check the state before announcing anything.
|
||||
private val mAccessibilityLongPressTimer = AccessibilityLongPressTimer(this /* callback */, mainKeyboardView.context)
|
||||
|
||||
// Since this method is called even when accessibility is off, make sure
|
||||
// to check the state before announcing anything.
|
||||
// Announce the language name only when the language is changed.
|
||||
// Announce the mode only when the mode is changed.
|
||||
// Announce the keyboard type only when the type is changed.
|
||||
|
@ -58,7 +43,7 @@ class MainKeyboardAccessibilityDelegate(mainKeyboardView: MainKeyboardView,
|
|||
val lastKeyboardMode = mLastKeyboardMode
|
||||
mLastKeyboardMode = keyboard.mId.mMode
|
||||
// Since this method is called even when accessibility is off, make sure
|
||||
// to check the state before announcing anything.
|
||||
// to check the state before announcing anything.
|
||||
if (!AccessibilityUtils.instance.isAccessibilityEnabled) {
|
||||
return
|
||||
}
|
||||
|
@ -107,7 +92,7 @@ class MainKeyboardAccessibilityDelegate(mainKeyboardView: MainKeyboardView,
|
|||
* @param keyboard The new keyboard.
|
||||
*/
|
||||
private fun announceKeyboardMode(keyboard: Keyboard) {
|
||||
val context = mKeyboardView!!.context
|
||||
val context = mKeyboardView.context
|
||||
val modeTextResId = KEYBOARD_MODE_RES_IDS[keyboard.mId.mMode]
|
||||
if (modeTextResId == 0) {
|
||||
return
|
||||
|
@ -129,22 +114,25 @@ class MainKeyboardAccessibilityDelegate(mainKeyboardView: MainKeyboardView,
|
|||
resId = when (keyboard.mId.mElementId) {
|
||||
KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED, KeyboardId.ELEMENT_ALPHABET -> {
|
||||
if (lastElementId == KeyboardId.ELEMENT_ALPHABET
|
||||
|| lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { // Transition between alphabet mode and automatic shifted mode should be silently
|
||||
// ignored because it can be determined by each key's talk back announce.
|
||||
|| lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
|
||||
// Transition between alphabet mode and automatic shifted mode should be silently
|
||||
// ignored because it can be determined by each key's talk back announce.
|
||||
return
|
||||
}
|
||||
R.string.spoken_description_mode_alpha
|
||||
}
|
||||
KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED -> {
|
||||
if (lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { // Resetting automatic shifted mode by pressing the shift key causes the transition
|
||||
// from automatic shifted to manual shifted that should be silently ignored.
|
||||
if (lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
|
||||
// Resetting automatic shifted mode by pressing the shift key causes the transition
|
||||
// from automatic shifted to manual shifted that should be silently ignored.
|
||||
return
|
||||
}
|
||||
R.string.spoken_description_shiftmode_on
|
||||
}
|
||||
KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED -> {
|
||||
if (lastElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) { // Resetting caps locked mode by pressing the shift key causes the transition
|
||||
// from shift locked to shift lock shifted that should be silently ignored.
|
||||
if (lastElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) {
|
||||
// Resetting caps locked mode by pressing the shift key causes the transition
|
||||
// from shift locked to shift lock shifted that should be silently ignored.
|
||||
return
|
||||
}
|
||||
R.string.spoken_description_shiftmode_locked
|
||||
|
@ -169,12 +157,13 @@ class MainKeyboardAccessibilityDelegate(mainKeyboardView: MainKeyboardView,
|
|||
override fun performClickOn(key: Key) {
|
||||
val x = key.hitBox.centerX()
|
||||
val y = key.hitBox.centerY()
|
||||
if (KeyboardAccessibilityDelegate.DEBUG_HOVER) {
|
||||
if (DEBUG_HOVER) {
|
||||
Log.d(TAG, "performClickOn: key=" + key
|
||||
+ " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y))
|
||||
}
|
||||
if (mBoundsToIgnoreHoverEvent.contains(x, y)) { // This hover exit event points to the key that should be ignored.
|
||||
// Clear the ignoring region to handle further hover events.
|
||||
if (mBoundsToIgnoreHoverEvent.contains(x, y)) {
|
||||
// This hover exit event points to the key that should be ignored.
|
||||
// Clear the ignoring region to handle further hover events.
|
||||
mBoundsToIgnoreHoverEvent.setEmpty()
|
||||
return
|
||||
}
|
||||
|
@ -184,7 +173,7 @@ class MainKeyboardAccessibilityDelegate(mainKeyboardView: MainKeyboardView,
|
|||
override fun onHoverEnterTo(key: Key) {
|
||||
val x = key.hitBox.centerX()
|
||||
val y = key.hitBox.centerY()
|
||||
if (KeyboardAccessibilityDelegate.DEBUG_HOVER) {
|
||||
if (DEBUG_HOVER) {
|
||||
Log.d(TAG, "onHoverEnterTo: key=" + key
|
||||
+ " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y))
|
||||
}
|
||||
|
@ -193,7 +182,7 @@ class MainKeyboardAccessibilityDelegate(mainKeyboardView: MainKeyboardView,
|
|||
return
|
||||
}
|
||||
// This hover enter event points to the key that isn't in the ignoring region.
|
||||
// Further hover events should be handled.
|
||||
// Further hover events should be handled.
|
||||
mBoundsToIgnoreHoverEvent.setEmpty()
|
||||
super.onHoverEnterTo(key)
|
||||
if (key.isLongPressEnabled) {
|
||||
|
@ -204,7 +193,7 @@ class MainKeyboardAccessibilityDelegate(mainKeyboardView: MainKeyboardView,
|
|||
override fun onHoverExitFrom(key: Key) {
|
||||
val x = key.hitBox.centerX()
|
||||
val y = key.hitBox.centerY()
|
||||
if (KeyboardAccessibilityDelegate.DEBUG_HOVER) {
|
||||
if (DEBUG_HOVER) {
|
||||
Log.d(TAG, "onHoverExitFrom: key=" + key
|
||||
+ " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y))
|
||||
}
|
||||
|
@ -213,43 +202,56 @@ class MainKeyboardAccessibilityDelegate(mainKeyboardView: MainKeyboardView,
|
|||
}
|
||||
|
||||
override fun performLongClickOn(key: Key) {
|
||||
if (KeyboardAccessibilityDelegate.Companion.DEBUG_HOVER) {
|
||||
if (DEBUG_HOVER) {
|
||||
Log.d(TAG, "performLongClickOn: key=$key")
|
||||
}
|
||||
val tracker = PointerTracker.getPointerTracker(KeyboardAccessibilityDelegate.Companion.HOVER_EVENT_POINTER_ID)
|
||||
val tracker = PointerTracker.getPointerTracker(HOVER_EVENT_POINTER_ID)
|
||||
val eventTime = SystemClock.uptimeMillis()
|
||||
val x = key.hitBox.centerX()
|
||||
val y = key.hitBox.centerY()
|
||||
val downEvent = MotionEvent.obtain(
|
||||
eventTime, eventTime, MotionEvent.ACTION_DOWN, x.toFloat(), y.toFloat(), 0 /* metaState */)
|
||||
val downEvent = MotionEvent.obtain(eventTime, eventTime, MotionEvent.ACTION_DOWN, x.toFloat(), y.toFloat(), 0)
|
||||
// Inject a fake down event to {@link PointerTracker} to handle a long press correctly.
|
||||
tracker.processMotionEvent(downEvent, mKeyDetector)
|
||||
downEvent.recycle()
|
||||
// Invoke {@link PointerTracker#onLongPressed()} as if a long press timeout has passed.
|
||||
tracker.onLongPressed()
|
||||
// If {@link Key#hasNoPanelAutoMoreKeys()} is true (such as "0 +" key on the phone layout)
|
||||
// or a key invokes IME switcher dialog, we should just ignore the next
|
||||
// {@link #onRegisterHoverKey(Key,MotionEvent)}. It can be determined by whether
|
||||
// {@link PointerTracker} is in operation or not.
|
||||
if (tracker.isInOperation) { // This long press shows a more keys keyboard and further hover events should be
|
||||
// handled.
|
||||
// or a key invokes IME switcher dialog, we should just ignore the next
|
||||
// {@link #onRegisterHoverKey(Key,MotionEvent)}. It can be determined by whether
|
||||
// {@link PointerTracker} is in operation or not.
|
||||
if (tracker.isInOperation) {
|
||||
// This long press shows a more keys keyboard and further hover events should be
|
||||
// handled.
|
||||
mBoundsToIgnoreHoverEvent.setEmpty()
|
||||
return
|
||||
}
|
||||
// This long press has handled at {@link MainKeyboardView#onLongPress(PointerTracker)}.
|
||||
// We should ignore further hover events on this key.
|
||||
// We should ignore further hover events on this key.
|
||||
mBoundsToIgnoreHoverEvent.set(key.hitBox)
|
||||
if (key.hasNoPanelAutoMoreKey()) { // This long press has registered a code point without showing a more keys keyboard.
|
||||
// We should talk back the code point if possible.
|
||||
val codePointOfNoPanelAutoMoreKey = key.moreKeys!![0].mCode
|
||||
if (key.hasNoPanelAutoMoreKey()) {
|
||||
// This long press has registered a code point without showing a more keys keyboard.
|
||||
// We should talk back the code point if possible.
|
||||
val codePointOfNoPanelAutoMoreKey = key.moreKeys?.get(0)?.mCode ?: return
|
||||
val text: String = KeyCodeDescriptionMapper.instance.getDescriptionForCodePoint(
|
||||
mKeyboardView!!.context, codePointOfNoPanelAutoMoreKey)!!
|
||||
text.let { sendWindowStateChanged(it) }
|
||||
mKeyboardView.context, codePointOfNoPanelAutoMoreKey) ?: return
|
||||
sendWindowStateChanged(text)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
mAccessibilityLongPressTimer = AccessibilityLongPressTimer(
|
||||
this /* callback */, mainKeyboardView.context)
|
||||
companion object {
|
||||
private val TAG = MainKeyboardAccessibilityDelegate::class.java.simpleName
|
||||
/** Map of keyboard modes to resource IDs. */
|
||||
private val KEYBOARD_MODE_RES_IDS = SparseIntArray().apply {
|
||||
put(KeyboardId.MODE_DATE, R.string.keyboard_mode_date)
|
||||
put(KeyboardId.MODE_DATETIME, R.string.keyboard_mode_date_time)
|
||||
put(KeyboardId.MODE_EMAIL, R.string.keyboard_mode_email)
|
||||
put(KeyboardId.MODE_IM, R.string.keyboard_mode_im)
|
||||
put(KeyboardId.MODE_NUMBER, R.string.keyboard_mode_number)
|
||||
put(KeyboardId.MODE_PHONE, R.string.keyboard_mode_phone)
|
||||
put(KeyboardId.MODE_TEXT, R.string.keyboard_mode_text)
|
||||
put(KeyboardId.MODE_TIME, R.string.keyboard_mode_time)
|
||||
put(KeyboardId.MODE_URL, R.string.keyboard_mode_url)
|
||||
}
|
||||
private const val KEYBOARD_IS_HIDDEN = -1
|
||||
}
|
||||
}
|
|
@ -11,8 +11,10 @@ import org.dslul.openboard.inputmethod.keyboard.PointerTracker
|
|||
* This class represents a delegate that can be registered in [MoreKeysKeyboardView] to
|
||||
* enhance accessibility support via composition rather via inheritance.
|
||||
*/
|
||||
class MoreKeysKeyboardAccessibilityDelegate(moreKeysKeyboardView: MoreKeysKeyboardView,
|
||||
keyDetector: KeyDetector) : KeyboardAccessibilityDelegate<MoreKeysKeyboardView?>(moreKeysKeyboardView, keyDetector) {
|
||||
class MoreKeysKeyboardAccessibilityDelegate(
|
||||
moreKeysKeyboardView: MoreKeysKeyboardView,
|
||||
keyDetector: KeyDetector
|
||||
) : KeyboardAccessibilityDelegate<MoreKeysKeyboardView>(moreKeysKeyboardView, keyDetector) {
|
||||
private val mMoreKeysKeyboardValidBounds = Rect()
|
||||
private var mOpenAnnounceResId = 0
|
||||
private var mCloseAnnounceResId = 0
|
||||
|
@ -42,7 +44,7 @@ class MoreKeysKeyboardAccessibilityDelegate(moreKeysKeyboardView: MoreKeysKeyboa
|
|||
val y = event.getY(actionIndex).toInt()
|
||||
val pointerId = event.getPointerId(actionIndex)
|
||||
val eventTime = event.eventTime
|
||||
mKeyboardView!!.onDownEvent(x, y, pointerId, eventTime)
|
||||
mKeyboardView.onDownEvent(x, y, pointerId, eventTime)
|
||||
}
|
||||
|
||||
override fun onHoverMove(event: MotionEvent) {
|
||||
|
@ -52,7 +54,7 @@ class MoreKeysKeyboardAccessibilityDelegate(moreKeysKeyboardView: MoreKeysKeyboa
|
|||
val y = event.getY(actionIndex).toInt()
|
||||
val pointerId = event.getPointerId(actionIndex)
|
||||
val eventTime = event.eventTime
|
||||
mKeyboardView!!.onMoveEvent(x, y, pointerId, eventTime)
|
||||
mKeyboardView.onMoveEvent(x, y, pointerId, eventTime)
|
||||
}
|
||||
|
||||
override fun onHoverExit(event: MotionEvent) {
|
||||
|
@ -70,20 +72,21 @@ class MoreKeysKeyboardAccessibilityDelegate(moreKeysKeyboardView: MoreKeysKeyboa
|
|||
val pointerId = event.getPointerId(actionIndex)
|
||||
val eventTime = event.eventTime
|
||||
// A hover exit event at one pixel width or height area on the edges of more keys keyboard
|
||||
// are treated as closing.
|
||||
mMoreKeysKeyboardValidBounds[0, 0, mKeyboardView!!.width] = mKeyboardView.height
|
||||
// are treated as closing.
|
||||
mMoreKeysKeyboardValidBounds[0, 0, mKeyboardView.width] = mKeyboardView.height
|
||||
mMoreKeysKeyboardValidBounds.inset(CLOSING_INSET_IN_PIXEL, CLOSING_INSET_IN_PIXEL)
|
||||
if (mMoreKeysKeyboardValidBounds.contains(x, y)) { // Invoke {@link MoreKeysKeyboardView#onUpEvent(int,int,int,long)} as if this hover
|
||||
// exit event selects a key.
|
||||
if (mMoreKeysKeyboardValidBounds.contains(x, y)) {
|
||||
// Invoke {@link MoreKeysKeyboardView#onUpEvent(int,int,int,long)} as if this hover
|
||||
// exit event selects a key.
|
||||
mKeyboardView.onUpEvent(x, y, pointerId, eventTime)
|
||||
// TODO: Should fix this reference. This is a hack to clear the state of
|
||||
// {@link PointerTracker}.
|
||||
// {@link PointerTracker}.
|
||||
PointerTracker.dismissAllMoreKeysPanels()
|
||||
return
|
||||
}
|
||||
// Close the more keys keyboard.
|
||||
// TODO: Should fix this reference. This is a hack to clear the state of
|
||||
// {@link PointerTracker}.
|
||||
// TODO: Should fix this reference. This is a hack to clear the state of
|
||||
// {@link PointerTracker}.
|
||||
PointerTracker.dismissAllMoreKeysPanels()
|
||||
}
|
||||
|
||||
|
@ -91,4 +94,4 @@ class MoreKeysKeyboardAccessibilityDelegate(moreKeysKeyboardView: MoreKeysKeyboa
|
|||
private val TAG = MoreKeysKeyboardAccessibilityDelegate::class.java.simpleName
|
||||
private const val CLOSING_INSET_IN_PIXEL = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue