mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-06-09 16:17:44 +00:00
change what can be stored in KeyboardLayoutSet subtype extra value
and some more preparations for adjustments related to language/layout settings upgrade
This commit is contained in:
parent
f2f7426ee5
commit
201b430362
25 changed files with 313 additions and 371 deletions
|
@ -11,7 +11,7 @@ object HangulEventDecoder {
|
|||
|
||||
@JvmStatic
|
||||
fun decodeHardwareKeyEvent(subtype: RichInputMethodSubtype, event: KeyEvent, defaultEvent: () -> Event): Event {
|
||||
val layout = LAYOUTS[subtype.keyboardLayoutSetName] ?: return defaultEvent()
|
||||
val layout = LAYOUTS[subtype.mainLayoutName] ?: return defaultEvent()
|
||||
val codePoint = layout[event.keyCode]?.let { if (event.isShiftPressed) it.second else it.first } ?: return defaultEvent()
|
||||
val hardwareEvent = Event.createHardwareKeypressEvent(codePoint, event.keyCode, event.metaState, null, event.repeatCount != 0)
|
||||
return decodeSoftwareKeyEvent(hardwareEvent)
|
||||
|
|
|
@ -236,7 +236,7 @@ public final class KeyboardLayoutSet {
|
|||
final boolean asciiCapable = subtype.getRawSubtype().isAsciiCapable();
|
||||
final boolean forceAscii = (mParams.mEditorInfo.imeOptions & EditorInfo.IME_FLAG_FORCE_ASCII) != 0;
|
||||
mParams.mSubtype = (forceAscii && !asciiCapable)
|
||||
? RichInputMethodSubtype.getNoLanguageSubtype()
|
||||
? RichInputMethodSubtype.Companion.getNoLanguageSubtype()
|
||||
: subtype;
|
||||
return this;
|
||||
}
|
||||
|
@ -326,8 +326,8 @@ public final class KeyboardLayoutSet {
|
|||
public static KeyboardId getFakeKeyboardId(final int elementId) {
|
||||
final Params params = new Params();
|
||||
params.mEditorInfo = new EditorInfo();
|
||||
params.mSubtype = RichInputMethodSubtype.getEmojiSubtype();
|
||||
params.mSubtype.getKeyboardLayoutSetName();
|
||||
params.mSubtype = RichInputMethodSubtype.Companion.getEmojiSubtype();
|
||||
params.mSubtype.getMainLayoutName();
|
||||
return new KeyboardId(elementId, params);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
|
|||
try {
|
||||
final InputMethodSubtype qwerty = AdditionalSubtypeUtils.createEmojiCapableAdditionalSubtype(mRichImm.getCurrentSubtypeLocale(), "qwerty", true);
|
||||
mKeyboardLayoutSet = builder.setKeyboardGeometry(keyboardWidth, keyboardHeight)
|
||||
.setSubtype(new RichInputMethodSubtype(qwerty))
|
||||
.setSubtype(RichInputMethodSubtype.Companion.get(qwerty))
|
||||
.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey)
|
||||
.setNumberRowEnabled(settingsValues.mShowsNumberRow)
|
||||
.setLanguageSwitchKeyEnabled(settingsValues.isLanguageSwitchKeyEnabled())
|
||||
|
|
|
@ -89,7 +89,7 @@ public final class EmojiPalettesView extends LinearLayout
|
|||
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(context, null);
|
||||
final Resources res = context.getResources();
|
||||
mEmojiLayoutParams = new EmojiLayoutParams(res);
|
||||
builder.setSubtype(RichInputMethodSubtype.getEmojiSubtype());
|
||||
builder.setSubtype(RichInputMethodSubtype.Companion.getEmojiSubtype());
|
||||
builder.setKeyboardGeometry(ResourceUtils.getKeyboardWidth(context, Settings.getInstance().getCurrent()),
|
||||
mEmojiLayoutParams.getEmojiKeyboardHeight());
|
||||
final KeyboardLayoutSet layoutSet = builder.build();
|
||||
|
|
|
@ -311,8 +311,8 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co
|
|||
}
|
||||
|
||||
// some layouts have different number layout, and there we don't want the numbers on the top row
|
||||
// todo: actually should not be in here, but in subtype extra values
|
||||
private fun hasNumbersOnTopRow() = params.mId.mSubtype.keyboardLayoutSetName !in listOf("pcqwerty", "lao", "thai", "korean_sebeolsik_390", "korean_sebeolsik_final")
|
||||
// todo: this should be derived from main layout and popup / hint order settings
|
||||
private fun hasNumbersOnTopRow() = params.mId.mSubtype.mainLayoutName !in listOf("pcqwerty", "lao", "thai", "korean_sebeolsik_390", "korean_sebeolsik_final")
|
||||
|
||||
companion object {
|
||||
private const val TAG = "KeyboardParser"
|
||||
|
|
|
@ -123,8 +123,8 @@ object RawKeyboardParser {
|
|||
simpleKeyData.mapIndexedTo(mutableListOf()) { i, row ->
|
||||
val newRow = row.toMutableList()
|
||||
if (params.mId.isAlphabetKeyboard
|
||||
&& params.mId.mSubtype.keyboardLayoutSetName.endsWith("+")
|
||||
&& "$layoutName+" == params.mId.mSubtype.keyboardLayoutSetName
|
||||
&& params.mId.mSubtype.mainLayoutName.endsWith("+")
|
||||
&& "$layoutName+" == params.mId.mSubtype.mainLayoutName
|
||||
) {
|
||||
params.mLocaleKeyboardInfos.getExtraKeys(i+1)?.let { newRow.addAll(it) }
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ object RawKeyboardParser {
|
|||
KeyboardId.ELEMENT_PHONE_SYMBOLS -> LAYOUT_PHONE_SYMBOLS
|
||||
KeyboardId.ELEMENT_EMOJI_BOTTOM_ROW -> LAYOUT_EMOJI_BOTTOM_ROW
|
||||
KeyboardId.ELEMENT_CLIPBOARD_BOTTOM_ROW -> LAYOUT_CLIPBOARD_BOTTOM_ROW
|
||||
else -> params.mId.mSubtype.keyboardLayoutSetName.substringBeforeLast("+")
|
||||
else -> params.mId.mSubtype.mainLayoutName.substringBeforeLast("+")
|
||||
}
|
||||
|
||||
private fun getFunctionalLayoutName(params: KeyboardParams, context: Context): String {
|
||||
|
|
|
@ -857,7 +857,7 @@ public class LatinIME extends InputMethodService implements
|
|||
public void onCurrentInputMethodSubtypeChanged(final InputMethodSubtype subtype) {
|
||||
// Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
|
||||
// is not guaranteed. It may even be called at the same time on a different thread.
|
||||
if (subtype.hashCode() == 0xf000000f) {
|
||||
if (subtype.hashCode() == 0x7000000f) {
|
||||
// For some reason sometimes the system wants to set the dummy subtype, which messes with the currently enabled subtype.
|
||||
// Now that the dummy subtype has a fixed id, we can easily avoid enabling it.
|
||||
return;
|
||||
|
|
|
@ -206,14 +206,14 @@ public class RichInputMethodManager {
|
|||
updateCurrentSubtype(newSubtype);
|
||||
updateShortcutIme();
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "onSubtypeChanged: " + mCurrentRichInputMethodSubtype.getNameForLogging());
|
||||
Log.w(TAG, "onSubtypeChanged: " + mCurrentRichInputMethodSubtype);
|
||||
}
|
||||
}
|
||||
|
||||
private static RichInputMethodSubtype sForcedSubtypeForTesting = null;
|
||||
|
||||
static void forceSubtype(@NonNull final InputMethodSubtype subtype) {
|
||||
sForcedSubtypeForTesting = RichInputMethodSubtype.getRichInputMethodSubtype(subtype);
|
||||
sForcedSubtypeForTesting = RichInputMethodSubtype.Companion.get(subtype);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -357,7 +357,7 @@ public class RichInputMethodManager {
|
|||
|
||||
private void updateCurrentSubtype(final InputMethodSubtype subtype) {
|
||||
SubtypeSettingsKt.setSelectedSubtype(KtxKt.prefs(mContext), subtype);
|
||||
mCurrentRichInputMethodSubtype = RichInputMethodSubtype.getRichInputMethodSubtype(subtype);
|
||||
mCurrentRichInputMethodSubtype = RichInputMethodSubtype.Companion.get(subtype);
|
||||
}
|
||||
|
||||
public static boolean canSwitchLanguage() {
|
||||
|
|
|
@ -1,220 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
|
||||
package helium314.keyboard.latin;
|
||||
|
||||
import android.view.inputmethod.InputMethodSubtype;
|
||||
|
||||
import helium314.keyboard.latin.common.Constants;
|
||||
import helium314.keyboard.latin.common.LocaleUtils;
|
||||
import helium314.keyboard.latin.utils.CustomLayoutUtilsKt;
|
||||
import helium314.keyboard.latin.utils.Log;
|
||||
import helium314.keyboard.latin.utils.SubtypeLocaleUtils;
|
||||
import helium314.keyboard.latin.utils.SubtypeUtilsKt;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static helium314.keyboard.latin.common.Constants.Subtype.KEYBOARD_MODE;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Enrichment class for InputMethodSubtype to enable concurrent multi-lingual input.
|
||||
* <p>
|
||||
* Right now, this returns the extra value of its primary subtype.
|
||||
*/
|
||||
// non final for easy mocking.
|
||||
public class RichInputMethodSubtype {
|
||||
private static final String TAG = RichInputMethodSubtype.class.getSimpleName();
|
||||
|
||||
@NonNull
|
||||
private final InputMethodSubtype mSubtype;
|
||||
@NonNull
|
||||
private final Locale mLocale;
|
||||
// The subtype is considered RTL if the language of the main subtype is RTL.
|
||||
// Cached because it might get read frequently, e.g. when moving pointer with space bar
|
||||
private final boolean mIsRtl;
|
||||
|
||||
public RichInputMethodSubtype(@NonNull final InputMethodSubtype subtype) {
|
||||
mSubtype = subtype;
|
||||
mLocale = SubtypeUtilsKt.locale(mSubtype);
|
||||
mIsRtl = LocaleUtils.isRtlLanguage(mLocale);
|
||||
}
|
||||
|
||||
// Extra values are determined by the primary subtype. This is probably right, but
|
||||
// we may have to revisit this later.
|
||||
public String getExtraValueOf(@NonNull final String key) {
|
||||
return mSubtype.getExtraValueOf(key);
|
||||
}
|
||||
|
||||
public boolean hasExtraValue(@NonNull final String key) {
|
||||
return mSubtype.containsExtraValueKey(key);
|
||||
}
|
||||
|
||||
// The mode is also determined by the primary subtype.
|
||||
public String getMode() {
|
||||
return mSubtype.getMode();
|
||||
}
|
||||
|
||||
public boolean isNoLanguage() {
|
||||
return SubtypeLocaleUtils.NO_LANGUAGE.equals(mLocale.getLanguage());
|
||||
}
|
||||
|
||||
public boolean isCustom() {
|
||||
return getKeyboardLayoutSetName().startsWith(CustomLayoutUtilsKt.CUSTOM_LAYOUT_PREFIX);
|
||||
}
|
||||
|
||||
public String getNameForLogging() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
// InputMethodSubtype's display name for spacebar text in its locale.
|
||||
// isAdditionalSubtype (T=true, F=false)
|
||||
// locale layout | Middle Full
|
||||
// ------ ------- - --------- ----------------------
|
||||
// en_US qwerty F English English (US) exception
|
||||
// en_GB qwerty F English English (UK) exception
|
||||
// es_US spanish F Español Español (EE.UU.) exception
|
||||
// fr azerty F Français Français
|
||||
// fr_CA qwerty F Français Français (Canada)
|
||||
// fr_CH swiss F Français Français (Suisse)
|
||||
// de qwertz F Deutsch Deutsch
|
||||
// de_CH swiss T Deutsch Deutsch (Schweiz)
|
||||
// zz qwerty F QWERTY QWERTY
|
||||
// fr qwertz T Français Français
|
||||
// de qwerty T Deutsch Deutsch
|
||||
// en_US azerty T English English (US)
|
||||
// zz azerty T AZERTY AZERTY
|
||||
// Get the RichInputMethodSubtype's full display name in its locale.
|
||||
@NonNull
|
||||
public String getFullDisplayName() {
|
||||
if (isNoLanguage()) {
|
||||
return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
|
||||
}
|
||||
return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(mLocale);
|
||||
}
|
||||
|
||||
// Get the RichInputMethodSubtype's middle display name in its locale.
|
||||
@NonNull
|
||||
public String getMiddleDisplayName() {
|
||||
if (isNoLanguage()) {
|
||||
return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
|
||||
}
|
||||
return SubtypeLocaleUtils.getSubtypeLanguageDisplayName(mLocale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (!(o instanceof RichInputMethodSubtype)) {
|
||||
return false;
|
||||
}
|
||||
final RichInputMethodSubtype other = (RichInputMethodSubtype)o;
|
||||
return mSubtype.equals(other.mSubtype) && mLocale.equals(other.mLocale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mSubtype.hashCode() + mLocale.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Multi-lingual subtype: " + mSubtype + ", " + mLocale;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Locale getLocale() {
|
||||
return mLocale;
|
||||
}
|
||||
|
||||
public boolean isRtlSubtype() {
|
||||
return mIsRtl;
|
||||
}
|
||||
|
||||
// TODO: remove this method
|
||||
@NonNull
|
||||
public InputMethodSubtype getRawSubtype() { return mSubtype; }
|
||||
|
||||
@NonNull
|
||||
public String getKeyboardLayoutSetName() {
|
||||
return SubtypeLocaleUtils.getKeyboardLayoutSetName(mSubtype);
|
||||
}
|
||||
|
||||
public static RichInputMethodSubtype getRichInputMethodSubtype(
|
||||
@Nullable final InputMethodSubtype subtype) {
|
||||
if (subtype == null) {
|
||||
return getNoLanguageSubtype();
|
||||
} else {
|
||||
return new RichInputMethodSubtype(subtype);
|
||||
}
|
||||
}
|
||||
|
||||
// Dummy no language QWERTY subtype. See {@link R.xml.method}.
|
||||
private static final int SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE = 0xdde0bfd3;
|
||||
private static final String EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE =
|
||||
"KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
|
||||
+ "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
|
||||
+ "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE
|
||||
+ "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
|
||||
@NonNull
|
||||
private static final RichInputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE =
|
||||
new RichInputMethodSubtype(new InputMethodSubtype.InputMethodSubtypeBuilder()
|
||||
.setSubtypeNameResId(R.string.subtype_no_language_qwerty)
|
||||
.setSubtypeIconResId(R.drawable.ic_ime_switcher)
|
||||
.setSubtypeLocale(SubtypeLocaleUtils.NO_LANGUAGE)
|
||||
.setSubtypeMode(KEYBOARD_MODE)
|
||||
.setSubtypeExtraValue(EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE)
|
||||
.setIsAuxiliary(false)
|
||||
.setOverridesImplicitlyEnabledSubtype(false)
|
||||
.setSubtypeId(SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE)
|
||||
.setIsAsciiCapable(true)
|
||||
.build());
|
||||
|
||||
// Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}.
|
||||
// Dummy Emoji subtype. See {@link R.xml.method}.
|
||||
private static final int SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE = 0xd78b2ed0;
|
||||
private static final String EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE =
|
||||
"KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI
|
||||
+ "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
|
||||
@NonNull
|
||||
private static final RichInputMethodSubtype DUMMY_EMOJI_SUBTYPE =
|
||||
new RichInputMethodSubtype(new InputMethodSubtype.InputMethodSubtypeBuilder()
|
||||
.setSubtypeNameResId(R.string.subtype_emoji)
|
||||
.setSubtypeIconResId(R.drawable.ic_ime_switcher)
|
||||
.setSubtypeLocale(SubtypeLocaleUtils.NO_LANGUAGE)
|
||||
.setSubtypeMode(KEYBOARD_MODE)
|
||||
.setSubtypeExtraValue(EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE)
|
||||
.setIsAuxiliary(false)
|
||||
.setOverridesImplicitlyEnabledSubtype(false)
|
||||
.setSubtypeId(SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE)
|
||||
.build());
|
||||
private static RichInputMethodSubtype sNoLanguageSubtype;
|
||||
|
||||
@NonNull
|
||||
public static RichInputMethodSubtype getNoLanguageSubtype() {
|
||||
RichInputMethodSubtype noLanguageSubtype = sNoLanguageSubtype;
|
||||
if (noLanguageSubtype == null) {
|
||||
final InputMethodSubtype rawNoLanguageSubtype = RichInputMethodManager.getInstance()
|
||||
.findSubtypeByLocaleAndKeyboardLayoutSet(LocaleUtils.constructLocale(SubtypeLocaleUtils.NO_LANGUAGE), SubtypeLocaleUtils.QWERTY);
|
||||
if (rawNoLanguageSubtype != null) {
|
||||
noLanguageSubtype = new RichInputMethodSubtype(rawNoLanguageSubtype);
|
||||
}
|
||||
}
|
||||
if (noLanguageSubtype != null) {
|
||||
sNoLanguageSubtype = noLanguageSubtype;
|
||||
return noLanguageSubtype;
|
||||
}
|
||||
Log.w(TAG, "Can't find any language with QWERTY subtype");
|
||||
Log.w(TAG, "No input method subtype found; returning dummy subtype: " + DUMMY_NO_LANGUAGE_SUBTYPE);
|
||||
return DUMMY_NO_LANGUAGE_SUBTYPE;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static RichInputMethodSubtype getEmojiSubtype() {
|
||||
return DUMMY_EMOJI_SUBTYPE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
package helium314.keyboard.latin
|
||||
|
||||
import android.view.inputmethod.InputMethodSubtype
|
||||
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder
|
||||
import helium314.keyboard.latin.common.Constants
|
||||
import helium314.keyboard.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET
|
||||
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
||||
import helium314.keyboard.latin.common.LocaleUtils.isRtlLanguage
|
||||
import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX
|
||||
import helium314.keyboard.latin.utils.LayoutType
|
||||
import helium314.keyboard.latin.utils.Log
|
||||
import helium314.keyboard.latin.utils.SubtypeLocaleUtils
|
||||
import helium314.keyboard.latin.utils.locale
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Enrichment class for InputMethodSubtype that extracts settings from extra values
|
||||
*/
|
||||
class RichInputMethodSubtype private constructor(val rawSubtype: InputMethodSubtype) {
|
||||
val locale: Locale = rawSubtype.locale()
|
||||
|
||||
// The subtype is considered RTL if the language of the main subtype is RTL.
|
||||
val isRtlSubtype: Boolean = isRtlLanguage(locale)
|
||||
|
||||
fun getExtraValueOf(key: String): String? = rawSubtype.getExtraValueOf(key)
|
||||
|
||||
fun hasExtraValue(key: String): Boolean = rawSubtype.containsExtraValueKey(key)
|
||||
|
||||
val isNoLanguage: Boolean get() = SubtypeLocaleUtils.NO_LANGUAGE == locale.language
|
||||
|
||||
val mainLayoutName: String get() = layouts[LayoutType.MAIN] ?: "qwerty"
|
||||
|
||||
val layouts = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "")
|
||||
|
||||
val isCustom: Boolean get() = mainLayoutName.startsWith(CUSTOM_LAYOUT_PREFIX)
|
||||
|
||||
val fullDisplayName: String get() {
|
||||
if (isNoLanguage) {
|
||||
return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(rawSubtype)!!
|
||||
}
|
||||
return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale)
|
||||
}
|
||||
|
||||
val middleDisplayName: String
|
||||
// Get the RichInputMethodSubtype's middle display name in its locale.
|
||||
get() {
|
||||
if (isNoLanguage) {
|
||||
return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(rawSubtype)!!
|
||||
}
|
||||
return SubtypeLocaleUtils.getSubtypeLanguageDisplayName(locale)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is RichInputMethodSubtype) return false
|
||||
return rawSubtype == other.rawSubtype && locale == other.locale
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return rawSubtype.hashCode() + locale.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String = rawSubtype.extraValue
|
||||
|
||||
companion object {
|
||||
private val TAG: String = RichInputMethodSubtype::class.java.simpleName
|
||||
|
||||
fun get(subtype: InputMethodSubtype?): RichInputMethodSubtype =
|
||||
if (subtype == null) noLanguageSubtype
|
||||
else RichInputMethodSubtype(subtype)
|
||||
|
||||
// Dummy no language QWERTY subtype. See method_dummy.xml}.
|
||||
private const val EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE = ("KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
|
||||
+ "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
|
||||
+ "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE
|
||||
+ "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE)
|
||||
private val DUMMY_NO_LANGUAGE_SUBTYPE = RichInputMethodSubtype(
|
||||
InputMethodSubtypeBuilder()
|
||||
.setSubtypeNameResId(R.string.subtype_no_language_qwerty)
|
||||
.setSubtypeIconResId(R.drawable.ic_ime_switcher)
|
||||
.setSubtypeLocale(SubtypeLocaleUtils.NO_LANGUAGE)
|
||||
.setSubtypeMode(Constants.Subtype.KEYBOARD_MODE)
|
||||
.setSubtypeExtraValue(EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE)
|
||||
.setIsAuxiliary(false)
|
||||
.setOverridesImplicitlyEnabledSubtype(false)
|
||||
.setSubtypeId(0x7000000f)
|
||||
.setIsAsciiCapable(true)
|
||||
.build()
|
||||
)
|
||||
|
||||
// Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}.
|
||||
// Dummy Emoji subtype. See {@link R.xml.method}.
|
||||
private const val SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE = -0x2874d130
|
||||
private const val EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE = ("KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI
|
||||
+ "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE)
|
||||
val emojiSubtype: RichInputMethodSubtype = RichInputMethodSubtype(
|
||||
InputMethodSubtypeBuilder()
|
||||
.setSubtypeNameResId(R.string.subtype_emoji)
|
||||
.setSubtypeIconResId(R.drawable.ic_ime_switcher)
|
||||
.setSubtypeLocale(SubtypeLocaleUtils.NO_LANGUAGE)
|
||||
.setSubtypeMode(Constants.Subtype.KEYBOARD_MODE)
|
||||
.setSubtypeExtraValue(EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE)
|
||||
.setIsAuxiliary(false)
|
||||
.setOverridesImplicitlyEnabledSubtype(false)
|
||||
.setSubtypeId(SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE)
|
||||
.build()
|
||||
)
|
||||
private var sNoLanguageSubtype: RichInputMethodSubtype? = null
|
||||
|
||||
val noLanguageSubtype: RichInputMethodSubtype get() {
|
||||
sNoLanguageSubtype?.let { return it }
|
||||
var noLanguageSubtype = sNoLanguageSubtype
|
||||
val rawNoLanguageSubtype = RichInputMethodManager.getInstance()
|
||||
.findSubtypeByLocaleAndKeyboardLayoutSet(
|
||||
SubtypeLocaleUtils.NO_LANGUAGE.constructLocale(),
|
||||
SubtypeLocaleUtils.QWERTY
|
||||
)
|
||||
if (rawNoLanguageSubtype != null) {
|
||||
noLanguageSubtype = RichInputMethodSubtype(rawNoLanguageSubtype)
|
||||
}
|
||||
if (noLanguageSubtype != null) {
|
||||
sNoLanguageSubtype = noLanguageSubtype
|
||||
return noLanguageSubtype
|
||||
}
|
||||
Log.w(TAG, "Can't find any language with QWERTY subtype")
|
||||
Log.w(TAG, "No input method subtype found; returning dummy subtype: $DUMMY_NO_LANGUAGE_SUBTYPE")
|
||||
return DUMMY_NO_LANGUAGE_SUBTYPE
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,35 +41,20 @@ public final class Constants {
|
|||
}
|
||||
|
||||
public static final class Subtype {
|
||||
/**
|
||||
* The subtype mode used to indicate that the subtype is a keyboard.
|
||||
*/
|
||||
/** The subtype mode used to indicate that the subtype is a keyboard. */
|
||||
public static final String KEYBOARD_MODE = "keyboard";
|
||||
|
||||
// some extra values:
|
||||
// TrySuppressingImeSwitcher: not documented, but used in Android source
|
||||
// AsciiCapable: not used, but recommended for Android 9- because of known issues
|
||||
// SupportTouchPositionCorrection: never read, never used outside AOSP keyboard -> can be removed?
|
||||
// EmojiCapable: there is some description in Constants, but actually it's never read
|
||||
// KeyboardLayoutSet: obvious
|
||||
public static final class ExtraValue {
|
||||
/**
|
||||
* The subtype extra value used to indicate that this subtype is capable of
|
||||
* entering ASCII characters.
|
||||
*/
|
||||
/** Indicates that this subtype is capable of entering ASCII characters (not used, but recommended for Android 9 and older). */
|
||||
public static final String ASCII_CAPABLE = "AsciiCapable";
|
||||
|
||||
/**
|
||||
* The subtype extra value used to indicate that this subtype is enabled
|
||||
* when the default subtype is not marked as ascii capable.
|
||||
*/
|
||||
public static final String ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE =
|
||||
"EnabledWhenDefaultIsNotAsciiCapable";
|
||||
/** Indicates that this subtype is enabled when the default subtype is not marked as ascii capable (used where?). */
|
||||
public static final String ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = "EnabledWhenDefaultIsNotAsciiCapable";
|
||||
|
||||
/**
|
||||
* The subtype extra value used to indicate that this subtype is capable of
|
||||
* entering emoji characters.
|
||||
*/
|
||||
/** Indicates that this subtype is capable of entering emoji characters (always set?). */
|
||||
public static final String EMOJI_CAPABLE = "EmojiCapable";
|
||||
|
||||
/** Indicates that the subtype does not have a shift key */
|
||||
|
@ -84,26 +69,29 @@ public final class Constants {
|
|||
* this extra value.
|
||||
* This extra value is supported on JellyBean and later.
|
||||
*/
|
||||
public static final String UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
|
||||
"UntranslatableReplacementStringInSubtypeName";
|
||||
public static final String UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME = "UntranslatableReplacementStringInSubtypeName";
|
||||
|
||||
/**
|
||||
* The subtype extra value used to indicate this subtype keyboard layout set name.
|
||||
* This extra value is private to LatinIME.
|
||||
*/
|
||||
/** Contains the layouts used by this subtype. This extra value is private to LatinIME.*/
|
||||
public static final String KEYBOARD_LAYOUT_SET = "KeyboardLayoutSet";
|
||||
|
||||
/**
|
||||
* The subtype extra value used to indicate that this subtype is an additional subtype
|
||||
* that the user defined. This extra value is private to LatinIME.
|
||||
*/
|
||||
/** Indicates that this subtype is an additional subtype that the user defined. This extra value is private to LatinIME. */
|
||||
public static final String IS_ADDITIONAL_SUBTYPE = "isAdditionalSubtype";
|
||||
|
||||
/**
|
||||
* The subtype extra value used to specify the combining rules.
|
||||
*/
|
||||
/** The subtype extra value used to specify the combining rules (currently not used). */
|
||||
public static final String COMBINING_RULES = "CombiningRules";
|
||||
|
||||
/** Overrides the general popup order setting */
|
||||
public static final String POPUP_ORDER = "PopupOrder";
|
||||
|
||||
/** Overrides the general hint order / priority setting */
|
||||
public static final String HINT_ORDER = "HintOrder";
|
||||
|
||||
/** Language tags indicating enabled secondary locales */
|
||||
public static final String SECONDARY_LOCALES = "SecondaryLocales";
|
||||
|
||||
/** Overrides the general "more popups" setting */
|
||||
public static final String MORE_POPUPS = "MorePopups";
|
||||
|
||||
private ExtraValue() {
|
||||
// This utility class is not publicly instantiable.
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import helium314.keyboard.dictionarypack.DictionaryPackConstants
|
|||
import helium314.keyboard.keyboard.KeyboardLayoutSet
|
||||
import helium314.keyboard.keyboard.KeyboardSwitcher
|
||||
import helium314.keyboard.latin.R
|
||||
import helium314.keyboard.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET
|
||||
import helium314.keyboard.latin.common.LocaleUtils
|
||||
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
||||
import helium314.keyboard.latin.databinding.LanguageListItemBinding
|
||||
|
@ -138,10 +137,10 @@ class LanguageSettingsDialog(
|
|||
val layouts = mutableListOf<String>()
|
||||
val displayNames = mutableListOf<String>()
|
||||
infos.forEach {
|
||||
val layoutSetName = it.subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET)
|
||||
if (layoutSetName?.startsWith(CUSTOM_LAYOUT_PREFIX) == false // don't allow copying custom layout (at least for now)
|
||||
&& !layoutSetName.endsWith("+")) { // don't allow copying layouts only defined via extra keys
|
||||
layouts.add(layoutSetName)
|
||||
val mainLayoutName = it.subtype.mainLayoutName()
|
||||
if (!mainLayoutName.startsWith(CUSTOM_LAYOUT_PREFIX) // don't allow copying custom layout (at least for now)
|
||||
&& !mainLayoutName.endsWith("+")) { // don't allow copying layouts only defined via extra keys
|
||||
layouts.add(mainLayoutName)
|
||||
displayNames.add(it.subtype.displayName(context).toString())
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +168,7 @@ class LanguageSettingsDialog(
|
|||
|
||||
private fun addSubtypeToView(subtype: SubtypeInfo) {
|
||||
val row = LayoutInflater.from(context).inflate(R.layout.language_list_item, listView)
|
||||
val layoutSetName = subtype.subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "qwerty"
|
||||
val layoutSetName = subtype.subtype.mainLayoutName()
|
||||
row.findViewById<TextView>(R.id.language_name).text =
|
||||
SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype.subtype)
|
||||
?: subtype.subtype.displayName(context)
|
||||
|
|
|
@ -211,7 +211,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
|
|||
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(this, editorInfo);
|
||||
return builder
|
||||
.setKeyboardGeometry(SPELLCHECKER_DUMMY_KEYBOARD_WIDTH, SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT)
|
||||
.setSubtype(RichInputMethodSubtype.getRichInputMethodSubtype(subtype))
|
||||
.setSubtype(RichInputMethodSubtype.Companion.get(subtype))
|
||||
.setIsSpellChecker(true)
|
||||
.disableTouchPositionCorrectionData()
|
||||
.build();
|
||||
|
|
|
@ -81,7 +81,7 @@ public final class AdditionalSubtypeUtils {
|
|||
|
||||
private static String getPrefSubtype(final InputMethodSubtype subtype) {
|
||||
final String keyboardLayoutSetName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
|
||||
final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
|
||||
final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=MAIN:" + keyboardLayoutSetName;
|
||||
final String extraValue = StringUtils.removeFromCommaSplittableTextIfExists(
|
||||
layoutExtraValue, StringUtils.removeFromCommaSplittableTextIfExists(
|
||||
IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue()));
|
||||
|
@ -173,7 +173,7 @@ public final class AdditionalSubtypeUtils {
|
|||
final String keyboardLayoutSetName, final boolean isAsciiCapable,
|
||||
final boolean isEmojiCapable) {
|
||||
final ArrayList<String> extraValueItems = new ArrayList<>();
|
||||
extraValueItems.add(KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName);
|
||||
extraValueItems.add(KEYBOARD_LAYOUT_SET + "=MAIN:" + keyboardLayoutSetName);
|
||||
if (isAsciiCapable) {
|
||||
extraValueItems.add(ASCII_CAPABLE);
|
||||
}
|
||||
|
@ -213,7 +213,7 @@ public final class AdditionalSubtypeUtils {
|
|||
// - EmojiCapable
|
||||
// - isAdditionalSubtype
|
||||
final ArrayList<String> compatibilityExtraValueItems = new ArrayList<>();
|
||||
compatibilityExtraValueItems.add(KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName);
|
||||
compatibilityExtraValueItems.add(KEYBOARD_LAYOUT_SET + "=MAIN:" + keyboardLayoutSetName);
|
||||
compatibilityExtraValueItems.add(ASCII_CAPABLE);
|
||||
if (SubtypeLocaleUtils.isExceptionalLocale(locale)) {
|
||||
compatibilityExtraValueItems.add(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" +
|
||||
|
|
|
@ -224,7 +224,7 @@ fun getCustomFunctionalLayoutName(elementId: Int, subtype: InputMethodSubtype, c
|
|||
val customFunctionalLayoutNames = getCustomLayoutFiles(context).filter { it.name.contains("functional") }.map { it.name.substringBeforeLast(".") + "." }
|
||||
if (customFunctionalLayoutNames.isEmpty()) return null
|
||||
val languageTag = subtype.locale().toLanguageTag()
|
||||
val mainLayoutName = subtype.getExtraValueOf(Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET) ?: "qwerty"
|
||||
val mainLayoutName = subtype.mainLayoutName()
|
||||
|
||||
if (elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
|
||||
findMatchingLayout(customFunctionalLayoutNames.filter { it.startsWith(CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED) }, mainLayoutName, languageTag)
|
||||
|
|
|
@ -47,7 +47,7 @@ public final class LanguageOnSpacebarUtils {
|
|||
return FORMAT_TYPE_NONE;
|
||||
}
|
||||
final String keyboardLanguage = locale.getLanguage();
|
||||
final String keyboardLayout = subtype.getKeyboardLayoutSetName();
|
||||
final String keyboardLayout = subtype.getMainLayoutName();
|
||||
int sameLanguageAndLayoutCount = 0;
|
||||
for (final InputMethodSubtype ims : sEnabledSubtypes) {
|
||||
final String language = SubtypeUtilsKt.locale(ims).getLanguage();
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package helium314.keyboard.latin.utils
|
||||
|
||||
import java.util.EnumMap
|
||||
|
||||
enum class LayoutType {
|
||||
MAIN, SYMBOLS, MORE_SYMBOLS, FUNCTIONAL, NUMBER, NUMBER_ROW, NUMPAD,
|
||||
NUMPAD_LANDSCAPE, PHONE, PHONE_SYMBOLS, EMOJI_BOTTOM, CLIPBOARD_BOTTOM;
|
||||
|
||||
companion object {
|
||||
fun EnumMap<LayoutType, String>.toExtraValue() = map { "${it.key.name}:${it.value}" }.joinToString("|")
|
||||
|
||||
fun getLayoutMap(extraValue: String): EnumMap<LayoutType, String> {
|
||||
val map = EnumMap<LayoutType, String>(LayoutType::class.java)
|
||||
extraValue.split("|").forEach {
|
||||
val s = it.split(":")
|
||||
runCatching { map[LayoutType.valueOf(s[0])] = s[1] }
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ import java.util.HashMap;
|
|||
import java.util.Locale;
|
||||
|
||||
import static helium314.keyboard.latin.common.Constants.Subtype.ExtraValue.COMBINING_RULES;
|
||||
import static helium314.keyboard.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
|
||||
import static helium314.keyboard.latin.common.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -274,7 +273,7 @@ public final class SubtypeLocaleUtils {
|
|||
|
||||
@NonNull
|
||||
public static String getKeyboardLayoutSetName(final InputMethodSubtype subtype) {
|
||||
String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET);
|
||||
String keyboardLayoutSet = SubtypeUtilsKt.explicitMainLayoutName(subtype);
|
||||
if (keyboardLayoutSet == null && subtype.isAsciiCapable()) {
|
||||
keyboardLayoutSet = QWERTY;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ fun getAllAvailableSubtypes(): List<InputMethodSubtype> {
|
|||
|
||||
fun getMatchingLayoutSetNameForLocale(locale: Locale): String {
|
||||
val subtypes = resourceSubtypesByLocale.values.flatten()
|
||||
val name = LocaleUtils.getBestMatch(locale, subtypes) { it.locale() }?.getExtraValueOf(Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET)
|
||||
val name = LocaleUtils.getBestMatch(locale, subtypes) { it.locale() }?.explicitMainLayoutName()
|
||||
if (name != null) return name
|
||||
return when (locale.script()) {
|
||||
ScriptUtils.SCRIPT_LATIN -> "qwerty"
|
||||
|
|
|
@ -3,6 +3,7 @@ package helium314.keyboard.latin.utils
|
|||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.view.inputmethod.InputMethodSubtype
|
||||
import helium314.keyboard.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET
|
||||
import helium314.keyboard.latin.common.LocaleUtils
|
||||
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
||||
import java.util.Locale
|
||||
|
@ -15,6 +16,16 @@ fun InputMethodSubtype.locale(): Locale {
|
|||
@Suppress("deprecation") return locale.constructLocale()
|
||||
}
|
||||
|
||||
fun InputMethodSubtype.mainLayoutName(): String {
|
||||
val map = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "")
|
||||
return map[LayoutType.MAIN] ?: "qwerty"
|
||||
}
|
||||
|
||||
fun InputMethodSubtype.explicitMainLayoutName(): String? {
|
||||
val map = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "")
|
||||
return map[LayoutType.MAIN]
|
||||
}
|
||||
|
||||
/** Workaround for SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale ignoring custom layout names */
|
||||
// todo (later): this should be done properly and in SubtypeLocaleUtils
|
||||
fun InputMethodSubtype.displayName(context: Context): CharSequence {
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
|
@ -57,6 +56,7 @@ fun SettingsNavHost(
|
|||
onClickGestureTyping = { navController.navigate(SettingsDestination.GestureTyping) },
|
||||
onClickAdvanced = { navController.navigate(SettingsDestination.Advanced) },
|
||||
onClickAppearance = { navController.navigate(SettingsDestination.Appearance) },
|
||||
onClickLanguage = { navController.navigate(SettingsDestination.Languages) },
|
||||
onClickBack = ::goBack,
|
||||
)
|
||||
}
|
||||
|
@ -90,9 +90,7 @@ fun SettingsNavHost(
|
|||
// )
|
||||
}
|
||||
composable(SettingsDestination.Languages) {
|
||||
// LanguagesSettingsScreen(
|
||||
// onClickBack = ::goBack
|
||||
// )
|
||||
// LanguageScreen(onClickBack = ::goBack)
|
||||
}
|
||||
composable(SettingsDestination.Colors) {
|
||||
ColorsScreen(isNight = false, onClickBack = ::goBack)
|
||||
|
|
|
@ -39,6 +39,7 @@ fun MainSettingsScreen(
|
|||
onClickGestureTyping: () -> Unit,
|
||||
onClickAdvanced: () -> Unit,
|
||||
onClickAppearance: () -> Unit,
|
||||
onClickLanguage: () -> Unit,
|
||||
onClickBack: () -> Unit,
|
||||
) {
|
||||
val ctx = LocalContext.current
|
||||
|
@ -48,6 +49,17 @@ fun MainSettingsScreen(
|
|||
settings = emptyList(),
|
||||
) {
|
||||
Column(Modifier.verticalScroll(rememberScrollState())) {
|
||||
Preference(
|
||||
name = stringResource(R.string.language_and_layouts_title),
|
||||
onClick = onClickLanguage,
|
||||
icon = R.drawable.ic_settings_languages_foreground
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_arrow_left),
|
||||
modifier = Modifier.scale(-1f, 1f),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
Preference(
|
||||
name = stringResource(R.string.settings_screen_preferences),
|
||||
onClick = onClickPreferences,
|
||||
|
@ -169,7 +181,7 @@ fun MainSettingsScreen(
|
|||
private fun PreviewScreen() {
|
||||
Theme(true) {
|
||||
Surface {
|
||||
MainSettingsScreen({}, {}, {}, {}, {}, {}, {}, {})
|
||||
MainSettingsScreen({}, {}, {}, {}, {}, {}, {}, {}, {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue