address some more warnings

This commit is contained in:
Helium314 2023-09-09 12:51:49 +02:00
parent af1eb551cf
commit e543788920
10 changed files with 77 additions and 52 deletions

View file

@ -2,6 +2,7 @@ package org.dslul.openboard.inputmethod.accessibility
import android.content.Context import android.content.Context
import android.media.AudioManager import android.media.AudioManager
import android.os.Build
import android.os.SystemClock import android.os.SystemClock
import android.provider.Settings import android.provider.Settings
import android.text.TextUtils import android.text.TextUtils
@ -12,7 +13,6 @@ import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import androidx.core.view.accessibility.AccessibilityEventCompat
import org.dslul.openboard.inputmethod.latin.R import org.dslul.openboard.inputmethod.latin.R
import org.dslul.openboard.inputmethod.latin.SuggestedWords import org.dslul.openboard.inputmethod.latin.SuggestedWords
import org.dslul.openboard.inputmethod.latin.utils.InputTypeUtils import org.dslul.openboard.inputmethod.latin.utils.InputTypeUtils
@ -127,17 +127,17 @@ class AccessibilityUtils private constructor() {
return return
} }
// The following is a hack to avoid using the heavy-weight TextToSpeech // The following is a hack to avoid using the heavy-weight TextToSpeech
// class. Instead, we're just forcing a fake AccessibilityEvent into // class. Instead, we're just forcing a fake AccessibilityEvent into
// the screen reader to make it speak. // the screen reader to make it speak.
val event = AccessibilityEvent.obtain() val event = obtainEvent()
event.packageName = PACKAGE event.packageName = PACKAGE
event.className = CLASS event.className = CLASS
event.eventTime = SystemClock.uptimeMillis() event.eventTime = SystemClock.uptimeMillis()
event.isEnabled = true event.isEnabled = true
event.text.add(text) event.text.add(text)
// Platforms starting at SDK version 16 (Build.VERSION_CODES.JELLY_BEAN) should use // Platforms starting at SDK version 16 (Build.VERSION_CODES.JELLY_BEAN) should use
// announce events. // announce events.
event.eventType = AccessibilityEventCompat.TYPE_ANNOUNCEMENT event.eventType = AccessibilityEvent.TYPE_ANNOUNCEMENT
val viewParent = view.parent val viewParent = view.parent
if (viewParent == null || viewParent !is ViewGroup) { if (viewParent == null || viewParent !is ViewGroup) {
Log.e(TAG, "Failed to obtain ViewParent in announceForAccessibility") Log.e(TAG, "Failed to obtain ViewParent in announceForAccessibility")
@ -205,5 +205,21 @@ class AccessibilityUtils private constructor() {
val action = event.action val action = event.action
return action == MotionEvent.ACTION_HOVER_ENTER || action == MotionEvent.ACTION_HOVER_EXIT || action == MotionEvent.ACTION_HOVER_MOVE return action == MotionEvent.ACTION_HOVER_ENTER || action == MotionEvent.ACTION_HOVER_EXIT || action == MotionEvent.ACTION_HOVER_MOVE
} }
fun obtainEvent(eventType: Int): AccessibilityEvent =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
AccessibilityEvent(eventType)
} else {
@Suppress("deprecation")
AccessibilityEvent.obtain(eventType)
}
fun obtainEvent(): AccessibilityEvent =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
AccessibilityEvent()
} else {
@Suppress("deprecation")
AccessibilityEvent.obtain()
}
} }
} }

View file

@ -73,8 +73,7 @@ open class KeyboardAccessibilityDelegate<KV : KeyboardView?>(protected val mKeyb
* @param text The text to send with the event. * @param text The text to send with the event.
*/ */
protected fun sendWindowStateChanged(text: String?) { protected fun sendWindowStateChanged(text: String?) {
val stateChange = AccessibilityEvent.obtain( val stateChange = AccessibilityUtils.obtainEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)
mKeyboardView!!.onInitializeAccessibilityEvent(stateChange) mKeyboardView!!.onInitializeAccessibilityEvent(stateChange)
stateChange.text.add(text) stateChange.text.add(text)
stateChange.contentDescription = null stateChange.contentDescription = null

View file

@ -1,10 +1,12 @@
package org.dslul.openboard.inputmethod.accessibility package org.dslul.openboard.inputmethod.accessibility
import android.graphics.Rect import android.graphics.Rect
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityRecord
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityEventCompat import androidx.core.view.accessibility.AccessibilityEventCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
@ -91,12 +93,12 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
fun createAccessibilityEvent(key: Key, eventType: Int): AccessibilityEvent { fun createAccessibilityEvent(key: Key, eventType: Int): AccessibilityEvent {
val virtualViewId = getVirtualViewIdOf(key) val virtualViewId = getVirtualViewIdOf(key)
val keyDescription = getKeyDescription(key) val keyDescription = getKeyDescription(key)
val event = AccessibilityEvent.obtain(eventType) val event = AccessibilityUtils.obtainEvent(eventType)
event.packageName = mKeyboardView!!.context.packageName event.packageName = mKeyboardView!!.context.packageName
event.className = key.javaClass.name event.className = key.javaClass.name
event.contentDescription = keyDescription event.contentDescription = keyDescription
event.isEnabled = true event.isEnabled = true
val record = AccessibilityEventCompat.asRecord(event) val record: AccessibilityRecord = event
record.setSource(mKeyboardView, virtualViewId) record.setSource(mKeyboardView, virtualViewId)
return event return event
} }
@ -111,16 +113,16 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
// announcements. // announcements.
mHoveringNodeId = id mHoveringNodeId = id
// Invalidate the node info of the key. // Invalidate the node info of the key.
sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED) sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)
sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER) sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
} }
fun onHoverExitFrom(key: Key) { fun onHoverExitFrom(key: Key) {
mHoveringNodeId = UNDEFINED mHoveringNodeId = UNDEFINED
// Invalidate the node info of the key to be able to revert the change we have done // 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, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED) sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)
sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT) sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT)
} }
/** /**
@ -292,8 +294,8 @@ class KeyboardAccessibilityNodeProvider<KV : KeyboardView?>(keyboardView: KV,
} }
init { init {
mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.Companion.instance mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.instance
mAccessibilityUtils = AccessibilityUtils.Companion.instance mAccessibilityUtils = AccessibilityUtils.instance
mKeyboardView = keyboardView mKeyboardView = keyboardView
mDelegate = delegate mDelegate = delegate
// Since this class is constructed lazily, we might not get a subsequent // Since this class is constructed lazily, we might not get a subsequent

View file

@ -24,8 +24,8 @@ object CompatUtils {
} }
try { try {
return targetClass.getMethod(name!!, *parameterTypes) return targetClass.getMethod(name!!, *parameterTypes)
} catch (e: SecurityException) { // ignore } catch (_: SecurityException) { // ignore
} catch (e: NoSuchMethodException) { } catch (_: NoSuchMethodException) {
} }
return null return null
} }
@ -36,8 +36,8 @@ object CompatUtils {
} }
try { try {
return targetClass.getField(name!!) return targetClass.getField(name!!)
} catch (e: SecurityException) { // ignore } catch (_: SecurityException) { // ignore
} catch (e: NoSuchFieldException) { } catch (_: NoSuchFieldException) {
} }
return null return null
} }
@ -49,8 +49,8 @@ object CompatUtils {
} }
try { try {
return targetClass.getConstructor(*types) return targetClass.getConstructor(*types)
} catch (e: SecurityException) { // ignore } catch (_: SecurityException) { // ignore
} catch (e: NoSuchMethodException) { } catch (_: NoSuchMethodException) {
} }
return null return null
} }
@ -153,9 +153,10 @@ object CompatUtils {
} }
@Suppress("unchecked_cast")
class ToObjectMethodWrapper<T>(private val mMethod: Method?, private val mDefaultValue: T) { class ToObjectMethodWrapper<T>(private val mMethod: Method?, private val mDefaultValue: T) {
operator fun invoke(receiver: Any?, vararg args: Any?): T { operator fun invoke(receiver: Any?, vararg args: Any?): T {
return CompatUtils.invoke(receiver, mDefaultValue!!, mMethod, *args) as T return CompatUtils.invoke(receiver, mDefaultValue, mMethod, *args) as T
} }
} }

View file

@ -2,28 +2,24 @@ package org.dslul.openboard.inputmethod.compat
import android.os.Build import android.os.Build
import android.os.Build.VERSION_CODES import android.os.Build.VERSION_CODES
import android.text.TextUtils
import android.util.Log
import android.view.inputmethod.InputMethodSubtype import android.view.inputmethod.InputMethodSubtype
import org.dslul.openboard.inputmethod.annotations.UsedForTesting
import org.dslul.openboard.inputmethod.latin.RichInputMethodSubtype
import org.dslul.openboard.inputmethod.latin.common.Constants
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
import org.dslul.openboard.inputmethod.latin.settings.locale
import java.util.* import java.util.*
object InputMethodSubtypeCompatUtils { object InputMethodSubtypeCompatUtils {
// Note that InputMethodSubtype.getLanguageTag() is expected to be available in Android N+. // Note that InputMethodSubtype.getLanguageTag() is expected to be available in Android N+.
private val GET_LANGUAGE_TAG = CompatUtils.getMethod(InputMethodSubtype::class.java, "getLanguageTag") private val GET_LANGUAGE_TAG = CompatUtils.getMethod(InputMethodSubtype::class.java, "getLanguageTag")
@kotlin.jvm.JvmStatic @JvmStatic
fun getLocaleObject(subtype: InputMethodSubtype): Locale { // Locale.forLanguageTag() is available only in Android L and later. fun getLocaleObject(subtype: InputMethodSubtype): Locale { // Locale.forLanguageTag() is available only in Android L and later.
if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
val languageTag = CompatUtils.invoke(subtype, null, GET_LANGUAGE_TAG) as String? val languageTag = CompatUtils.invoke(subtype, null, GET_LANGUAGE_TAG) as String?
if (!TextUtils.isEmpty(languageTag)) { if (!languageTag.isNullOrEmpty()) {
return Locale.forLanguageTag(languageTag) return Locale.forLanguageTag(languageTag)
} }
} }
return LocaleUtils.constructLocaleFromString(subtype.locale) return LocaleUtils.constructLocaleFromString(subtype.locale())
} }
} }

View file

@ -87,6 +87,7 @@ class ClipboardHistoryView @JvmOverloads constructor(
layoutManager = StaggeredGridLayoutManager(colCount, StaggeredGridLayoutManager.VERTICAL) layoutManager = StaggeredGridLayoutManager(colCount, StaggeredGridLayoutManager.VERTICAL)
val dividerHeight = resources.getDimensionPixelSize(R.dimen.config_clipboard_divider_height) val dividerHeight = resources.getDimensionPixelSize(R.dimen.config_clipboard_divider_height)
addItemDecoration(ClipboardHistoryRecyclerView.BottomDividerItemDecoration(dividerHeight, dividerColor)) addItemDecoration(ClipboardHistoryRecyclerView.BottomDividerItemDecoration(dividerHeight, dividerColor))
@Suppress("deprecation") // no cache should be fine according to warning in https://developer.android.com/reference/android/view/ViewGroup#setPersistentDrawingCache(int)
persistentDrawingCache = PERSISTENT_NO_CACHE persistentDrawingCache = PERSISTENT_NO_CACHE
clipboardLayoutParams.setListProperties(this) clipboardLayoutParams.setListProperties(this)
placeholderView = this@ClipboardHistoryView.placeholderView placeholderView = this@ClipboardHistoryView.placeholderView
@ -222,8 +223,8 @@ class ClipboardHistoryView @JvmOverloads constructor(
clipboardRecyclerView.smoothScrollToPosition(at) clipboardRecyclerView.smoothScrollToPosition(at)
} }
override fun onClipboardHistoryEntriesRemoved(position: Int, count: Int) { override fun onClipboardHistoryEntriesRemoved(pos: Int, count: Int) {
clipboardAdapter.notifyItemRangeRemoved(position, count) clipboardAdapter.notifyItemRangeRemoved(pos, count)
} }
override fun onClipboardHistoryEntryMoved(from: Int, to: Int) { override fun onClipboardHistoryEntryMoved(from: Int, to: Int) {

View file

@ -3,7 +3,6 @@ package org.dslul.openboard.inputmethod.latin.settings
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.text.Html
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -179,7 +178,7 @@ class LanguageSettingsDialog(
private fun fillDictionariesView(dictionariesView: LinearLayout) { private fun fillDictionariesView(dictionariesView: LinearLayout) {
dictionariesView.findViewById<ImageView>(R.id.add_dictionary).setOnClickListener { dictionariesView.findViewById<ImageView>(R.id.add_dictionary).setOnClickListener {
val link = "<a href='$DICTIONARY_URL'>" + context.getString(R.string.dictionary_link_text) + "</a>" val link = "<a href='$DICTIONARY_URL'>" + context.getString(R.string.dictionary_link_text) + "</a>"
val message = Html.fromHtml(context.getString(R.string.add_dictionary, link)) val message = SpannableStringUtils.fromHtml(context.getString(R.string.add_dictionary, link))
val dialog = Builder(context) val dialog = Builder(context)
.setTitle(R.string.add_new_dictionary_title) .setTitle(R.string.add_new_dictionary_title)
.setMessage(message) .setMessage(message)

View file

@ -259,4 +259,8 @@ private val systemSubtypes = mutableListOf<InputMethodSubtype>()
private const val SUBTYPE_SEPARATOR = ";" private const val SUBTYPE_SEPARATOR = ";"
private const val LOCALE_LAYOUT_SEPARATOR = ":" private const val LOCALE_LAYOUT_SEPARATOR = ":"
@Suppress("deprecation") // it's deprecated, but no replacement for API < 24
// todo: add language tags in method.xml, and adjust this method to use locale only if necessary
// but then language tag should be converted to locale, or other way!
// see also InputMethodSubtypeCompatUtils
fun InputMethodSubtype.locale() = locale fun InputMethodSubtype.locale() = locale

View file

@ -1,7 +1,6 @@
package org.dslul.openboard.inputmethod.latin.utils package org.dslul.openboard.inputmethod.latin.utils
import android.content.Context import android.content.Context
import android.text.Html
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.View import android.view.View
import android.widget.TextView import android.widget.TextView
@ -49,7 +48,7 @@ fun showMissingDictionaryDialog(context: Context, locale: Locale) {
val dictionaryLink = "<a href='$DICTIONARY_URL/src/branch/main/dictionaries/main_$locale.dict'>" + context.getString( val dictionaryLink = "<a href='$DICTIONARY_URL/src/branch/main/dictionaries/main_$locale.dict'>" + context.getString(
R.string.dictionary_link_text) + "</a>" R.string.dictionary_link_text) + "</a>"
val message = Html.fromHtml(context.getString( val message = SpannableStringUtils.fromHtml(context.getString(
R.string.no_dictionary_message, R.string.no_dictionary_message,
repositoryLink, repositoryLink,
locale.toString(), locale.toString(),

View file

@ -16,6 +16,8 @@
package org.dslul.openboard.inputmethod.latin.utils; package org.dslul.openboard.inputmethod.latin.utils;
import android.os.Build;
import android.text.Html;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
@ -50,8 +52,8 @@ public final class SpannableStringUtils {
Spannable dest, int destoff) { Spannable dest, int destoff) {
Object[] spans = source.getSpans(start, end, SuggestionSpan.class); Object[] spans = source.getSpans(start, end, SuggestionSpan.class);
for (int i = 0; i < spans.length; i++) { for (Object span : spans) {
int fl = source.getSpanFlags(spans[i]); int fl = source.getSpanFlags(span);
// We don't care about the PARAGRAPH flag in LatinIME code. However, if this flag // We don't care about the PARAGRAPH flag in LatinIME code. However, if this flag
// is set, Spannable#setSpan will throw an exception unless the span is on the edge // is set, Spannable#setSpan will throw an exception unless the span is on the edge
// of a word. But the spans have been split into two by the getText{Before,After}Cursor // of a word. But the spans have been split into two by the getText{Before,After}Cursor
@ -59,16 +61,15 @@ public final class SpannableStringUtils {
// Since we don't use them, we can just remove them and avoid crashing. // Since we don't use them, we can just remove them and avoid crashing.
fl &= ~Spanned.SPAN_PARAGRAPH; fl &= ~Spanned.SPAN_PARAGRAPH;
int st = source.getSpanStart(spans[i]); int st = source.getSpanStart(span);
int en = source.getSpanEnd(spans[i]); int en = source.getSpanEnd(span);
if (st < start) if (st < start)
st = start; st = start;
if (en > end) if (en > end)
en = end; en = end;
dest.setSpan(spans[i], st - start + destoff, en - start + destoff, dest.setSpan(span, st - start + destoff, en - start + destoff, fl);
fl);
} }
} }
@ -89,16 +90,16 @@ public final class SpannableStringUtils {
} }
boolean spanned = false; boolean spanned = false;
for (int i = 0; i < text.length; i++) { for (CharSequence value : text) {
if (text[i] instanceof Spanned) { if (value instanceof Spanned) {
spanned = true; spanned = true;
break; break;
} }
} }
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < text.length; i++) { for (CharSequence sequence : text) {
sb.append(text[i]); sb.append(sequence);
} }
if (!spanned) { if (!spanned) {
@ -107,11 +108,11 @@ public final class SpannableStringUtils {
SpannableString ss = new SpannableString(sb); SpannableString ss = new SpannableString(sb);
int off = 0; int off = 0;
for (int i = 0; i < text.length; i++) { for (CharSequence charSequence : text) {
int len = text[i].length(); int len = charSequence.length();
if (text[i] instanceof Spanned) { if (charSequence instanceof Spanned) {
copyNonParagraphSuggestionSpansFrom((Spanned) text[i], 0, len, ss, off); copyNonParagraphSuggestionSpansFrom((Spanned) charSequence, 0, len, ss, off);
} }
off += len; off += len;
@ -120,6 +121,13 @@ public final class SpannableStringUtils {
return new SpannedString(ss); return new SpannedString(ss);
} }
@SuppressWarnings("deprecation")
public static Spanned fromHtml(final String text) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
return Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY);
return Html.fromHtml(text);
}
public static boolean hasUrlSpans(final CharSequence text, public static boolean hasUrlSpans(final CharSequence text,
final int startIndex, final int endIndex) { final int startIndex, final int endIndex) {
if (!(text instanceof Spanned)) { if (!(text instanceof Spanned)) {
@ -178,6 +186,6 @@ public final class SpannableStringUtils {
sequences.remove(i); sequences.remove(i);
} }
} }
return sequences.toArray(new CharSequence[sequences.size()]); return sequences.toArray(new CharSequence[0]);
} }
} }