diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/AccessibilityUtils.kt b/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/AccessibilityUtils.kt index 9762b01aa..b35e546ec 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/AccessibilityUtils.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/AccessibilityUtils.kt @@ -2,6 +2,7 @@ package org.dslul.openboard.inputmethod.accessibility import android.content.Context import android.media.AudioManager +import android.os.Build import android.os.SystemClock import android.provider.Settings import android.text.TextUtils @@ -12,7 +13,6 @@ import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityManager import android.view.inputmethod.EditorInfo -import androidx.core.view.accessibility.AccessibilityEventCompat import org.dslul.openboard.inputmethod.latin.R import org.dslul.openboard.inputmethod.latin.SuggestedWords import org.dslul.openboard.inputmethod.latin.utils.InputTypeUtils @@ -127,17 +127,17 @@ class AccessibilityUtils private constructor() { return } // The following is a hack to avoid using the heavy-weight TextToSpeech -// class. Instead, we're just forcing a fake AccessibilityEvent into -// the screen reader to make it speak. - val event = AccessibilityEvent.obtain() + // class. Instead, we're just forcing a fake AccessibilityEvent into + // the screen reader to make it speak. + val event = obtainEvent() event.packageName = PACKAGE event.className = CLASS event.eventTime = SystemClock.uptimeMillis() event.isEnabled = true event.text.add(text) // Platforms starting at SDK version 16 (Build.VERSION_CODES.JELLY_BEAN) should use -// announce events. - event.eventType = AccessibilityEventCompat.TYPE_ANNOUNCEMENT + // announce events. + event.eventType = AccessibilityEvent.TYPE_ANNOUNCEMENT val viewParent = view.parent if (viewParent == null || viewParent !is ViewGroup) { Log.e(TAG, "Failed to obtain ViewParent in announceForAccessibility") @@ -205,5 +205,21 @@ class AccessibilityUtils private constructor() { val action = event.action 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() + } } } \ No newline at end of file diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/KeyboardAccessibilityDelegate.kt b/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/KeyboardAccessibilityDelegate.kt index abf9338a9..ac3632a40 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/KeyboardAccessibilityDelegate.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/KeyboardAccessibilityDelegate.kt @@ -73,8 +73,7 @@ open class KeyboardAccessibilityDelegate(protected val mKeyb * @param text The text to send with the event. */ protected fun sendWindowStateChanged(text: String?) { - val stateChange = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) + val stateChange = AccessibilityUtils.obtainEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) mKeyboardView!!.onInitializeAccessibilityEvent(stateChange) stateChange.text.add(text) stateChange.contentDescription = null diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.kt b/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.kt index 9154e3d3c..b6c946f3c 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.kt @@ -1,10 +1,12 @@ 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 import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityRecord import androidx.core.view.ViewCompat import androidx.core.view.accessibility.AccessibilityEventCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat @@ -91,12 +93,12 @@ class KeyboardAccessibilityNodeProvider(keyboardView: KV, fun createAccessibilityEvent(key: Key, eventType: Int): AccessibilityEvent { val virtualViewId = getVirtualViewIdOf(key) val keyDescription = getKeyDescription(key) - val event = AccessibilityEvent.obtain(eventType) + val event = AccessibilityUtils.obtainEvent(eventType) event.packageName = mKeyboardView!!.context.packageName event.className = key.javaClass.name event.contentDescription = keyDescription event.isEnabled = true - val record = AccessibilityEventCompat.asRecord(event) + val record: AccessibilityRecord = event record.setSource(mKeyboardView, virtualViewId) return event } @@ -111,16 +113,16 @@ class KeyboardAccessibilityNodeProvider(keyboardView: KV, // announcements. mHoveringNodeId = id // Invalidate the node info of the key. - sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED) - sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER) + sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) + sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER) } 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)}. - sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED) - sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT) + sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) + sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) } /** @@ -292,8 +294,8 @@ class KeyboardAccessibilityNodeProvider(keyboardView: KV, } init { - mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.Companion.instance - mAccessibilityUtils = AccessibilityUtils.Companion.instance + mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.instance + mAccessibilityUtils = AccessibilityUtils.instance mKeyboardView = keyboardView mDelegate = delegate // Since this class is constructed lazily, we might not get a subsequent diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/compat/CompatUtils.kt b/app/src/main/java/org/dslul/openboard/inputmethod/compat/CompatUtils.kt index 3aab600be..95c46e644 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/compat/CompatUtils.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/compat/CompatUtils.kt @@ -24,8 +24,8 @@ object CompatUtils { } try { return targetClass.getMethod(name!!, *parameterTypes) - } catch (e: SecurityException) { // ignore - } catch (e: NoSuchMethodException) { + } catch (_: SecurityException) { // ignore + } catch (_: NoSuchMethodException) { } return null } @@ -36,8 +36,8 @@ object CompatUtils { } try { return targetClass.getField(name!!) - } catch (e: SecurityException) { // ignore - } catch (e: NoSuchFieldException) { + } catch (_: SecurityException) { // ignore + } catch (_: NoSuchFieldException) { } return null } @@ -49,8 +49,8 @@ object CompatUtils { } try { return targetClass.getConstructor(*types) - } catch (e: SecurityException) { // ignore - } catch (e: NoSuchMethodException) { + } catch (_: SecurityException) { // ignore + } catch (_: NoSuchMethodException) { } return null } @@ -153,9 +153,10 @@ object CompatUtils { } + @Suppress("unchecked_cast") class ToObjectMethodWrapper(private val mMethod: Method?, private val mDefaultValue: 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 } } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/compat/InputMethodSubtypeCompatUtils.kt b/app/src/main/java/org/dslul/openboard/inputmethod/compat/InputMethodSubtypeCompatUtils.kt index b48d8a2d9..96accb6bc 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/compat/InputMethodSubtypeCompatUtils.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/compat/InputMethodSubtypeCompatUtils.kt @@ -2,28 +2,24 @@ package org.dslul.openboard.inputmethod.compat import android.os.Build import android.os.Build.VERSION_CODES -import android.text.TextUtils -import android.util.Log 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.settings.locale import java.util.* object InputMethodSubtypeCompatUtils { // Note that InputMethodSubtype.getLanguageTag() is expected to be available in Android N+. 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. if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { val languageTag = CompatUtils.invoke(subtype, null, GET_LANGUAGE_TAG) as String? - if (!TextUtils.isEmpty(languageTag)) { + if (!languageTag.isNullOrEmpty()) { return Locale.forLanguageTag(languageTag) } } - return LocaleUtils.constructLocaleFromString(subtype.locale) + return LocaleUtils.constructLocaleFromString(subtype.locale()) } } \ No newline at end of file diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardHistoryView.kt b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardHistoryView.kt index 474c714cb..d084b734a 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardHistoryView.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardHistoryView.kt @@ -87,6 +87,7 @@ class ClipboardHistoryView @JvmOverloads constructor( layoutManager = StaggeredGridLayoutManager(colCount, StaggeredGridLayoutManager.VERTICAL) val dividerHeight = resources.getDimensionPixelSize(R.dimen.config_clipboard_divider_height) 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 clipboardLayoutParams.setListProperties(this) placeholderView = this@ClipboardHistoryView.placeholderView @@ -222,8 +223,8 @@ class ClipboardHistoryView @JvmOverloads constructor( clipboardRecyclerView.smoothScrollToPosition(at) } - override fun onClipboardHistoryEntriesRemoved(position: Int, count: Int) { - clipboardAdapter.notifyItemRangeRemoved(position, count) + override fun onClipboardHistoryEntriesRemoved(pos: Int, count: Int) { + clipboardAdapter.notifyItemRangeRemoved(pos, count) } override fun onClipboardHistoryEntryMoved(from: Int, to: Int) { diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/LanguageSettingsDialog.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/LanguageSettingsDialog.kt index 2bf514a0d..cf5868984 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/LanguageSettingsDialog.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/LanguageSettingsDialog.kt @@ -3,7 +3,6 @@ package org.dslul.openboard.inputmethod.latin.settings import android.content.Context import android.content.Intent import android.net.Uri -import android.text.Html import android.text.method.LinkMovementMethod import android.view.LayoutInflater import android.view.View @@ -179,7 +178,7 @@ class LanguageSettingsDialog( private fun fillDictionariesView(dictionariesView: LinearLayout) { dictionariesView.findViewById(R.id.add_dictionary).setOnClickListener { val link = "" + context.getString(R.string.dictionary_link_text) + "" - 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) .setTitle(R.string.add_new_dictionary_title) .setMessage(message) diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SubtypeSettings.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SubtypeSettings.kt index ce19b95cb..99503cadb 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SubtypeSettings.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SubtypeSettings.kt @@ -259,4 +259,8 @@ private val systemSubtypes = mutableListOf() private const val SUBTYPE_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 diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/DictionaryUtils.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/DictionaryUtils.kt index 244f9d603..f476ec903 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/DictionaryUtils.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/DictionaryUtils.kt @@ -1,7 +1,6 @@ package org.dslul.openboard.inputmethod.latin.utils import android.content.Context -import android.text.Html import android.text.method.LinkMovementMethod import android.view.View import android.widget.TextView @@ -49,7 +48,7 @@ fun showMissingDictionaryDialog(context: Context, locale: Locale) { val dictionaryLink = "" + context.getString( R.string.dictionary_link_text) + "" - val message = Html.fromHtml(context.getString( + val message = SpannableStringUtils.fromHtml(context.getString( R.string.no_dictionary_message, repositoryLink, locale.toString(), diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/SpannableStringUtils.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/SpannableStringUtils.java index cbea74bd0..8f5223b89 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/SpannableStringUtils.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/SpannableStringUtils.java @@ -16,6 +16,8 @@ package org.dslul.openboard.inputmethod.latin.utils; +import android.os.Build; +import android.text.Html; import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; @@ -50,8 +52,8 @@ public final class SpannableStringUtils { Spannable dest, int destoff) { Object[] spans = source.getSpans(start, end, SuggestionSpan.class); - for (int i = 0; i < spans.length; i++) { - int fl = source.getSpanFlags(spans[i]); + for (Object span : spans) { + int fl = source.getSpanFlags(span); // 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 // 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. fl &= ~Spanned.SPAN_PARAGRAPH; - int st = source.getSpanStart(spans[i]); - int en = source.getSpanEnd(spans[i]); + int st = source.getSpanStart(span); + int en = source.getSpanEnd(span); if (st < start) st = start; if (en > end) en = end; - dest.setSpan(spans[i], st - start + destoff, en - start + destoff, - fl); + dest.setSpan(span, st - start + destoff, en - start + destoff, fl); } } @@ -89,16 +90,16 @@ public final class SpannableStringUtils { } boolean spanned = false; - for (int i = 0; i < text.length; i++) { - if (text[i] instanceof Spanned) { + for (CharSequence value : text) { + if (value instanceof Spanned) { spanned = true; break; } } StringBuilder sb = new StringBuilder(); - for (int i = 0; i < text.length; i++) { - sb.append(text[i]); + for (CharSequence sequence : text) { + sb.append(sequence); } if (!spanned) { @@ -107,11 +108,11 @@ public final class SpannableStringUtils { SpannableString ss = new SpannableString(sb); int off = 0; - for (int i = 0; i < text.length; i++) { - int len = text[i].length(); + for (CharSequence charSequence : text) { + int len = charSequence.length(); - if (text[i] instanceof Spanned) { - copyNonParagraphSuggestionSpansFrom((Spanned) text[i], 0, len, ss, off); + if (charSequence instanceof Spanned) { + copyNonParagraphSuggestionSpansFrom((Spanned) charSequence, 0, len, ss, off); } off += len; @@ -120,6 +121,13 @@ public final class SpannableStringUtils { 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, final int startIndex, final int endIndex) { if (!(text instanceof Spanned)) { @@ -178,6 +186,6 @@ public final class SpannableStringUtils { sequences.remove(i); } } - return sequences.toArray(new CharSequence[sequences.size()]); + return sequences.toArray(new CharSequence[0]); } }