From 210da880527d692e22e8e585efa67fe52da8fad0 Mon Sep 17 00:00:00 2001 From: darthpaul Date: Fri, 4 Nov 2022 23:11:43 +0000 Subject: [PATCH] feature: support switching language on longpress of emoji key --- .../keyboard/dialogs/ChangeLanguagePopup.kt | 57 +++++++++ .../dialogs/KeyboardRadioGroupDialog.kt | 83 ++++++++++++ .../keyboard/extensions/Context.kt | 119 +++++++++++++++++- .../keyboard/services/SimpleKeyboardIME.kt | 34 +++-- .../keyboard/views/MyKeyboardView.kt | 20 ++- .../main/res/layout/dialog_radio_group.xml | 16 +++ app/src/main/res/layout/radio_button_item.xml | 5 + app/src/main/res/values/styles.xml | 7 ++ 8 files changed, 325 insertions(+), 16 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/keyboard/dialogs/ChangeLanguagePopup.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/keyboard/dialogs/KeyboardRadioGroupDialog.kt create mode 100644 app/src/main/res/layout/dialog_radio_group.xml create mode 100644 app/src/main/res/layout/radio_button_item.xml diff --git a/app/src/main/kotlin/com/simplemobiletools/keyboard/dialogs/ChangeLanguagePopup.kt b/app/src/main/kotlin/com/simplemobiletools/keyboard/dialogs/ChangeLanguagePopup.kt new file mode 100644 index 0000000..af31f5f --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/keyboard/dialogs/ChangeLanguagePopup.kt @@ -0,0 +1,57 @@ +package com.simplemobiletools.keyboard.dialogs + +import android.view.View +import com.simplemobiletools.commons.models.RadioItem +import com.simplemobiletools.keyboard.R +import com.simplemobiletools.keyboard.extensions.config +import com.simplemobiletools.keyboard.helpers.* + +class ChangeLanguagePopup( + inputView: View, + private val onSelect: () -> Unit, +) { + private val context = inputView.context + private val config = context.config + + init { + val items = arrayListOf( + RadioItem(LANGUAGE_BENGALI, getKeyboardLanguageText(LANGUAGE_BENGALI)), + RadioItem(LANGUAGE_BULGARIAN, getKeyboardLanguageText(LANGUAGE_BULGARIAN)), + RadioItem(LANGUAGE_ENGLISH_QWERTY, getKeyboardLanguageText(LANGUAGE_ENGLISH_QWERTY)), + RadioItem(LANGUAGE_ENGLISH_QWERTZ, getKeyboardLanguageText(LANGUAGE_ENGLISH_QWERTZ)), + RadioItem(LANGUAGE_ENGLISH_DVORAK, getKeyboardLanguageText(LANGUAGE_ENGLISH_DVORAK)), + RadioItem(LANGUAGE_FRENCH, getKeyboardLanguageText(LANGUAGE_FRENCH)), + RadioItem(LANGUAGE_GERMAN, getKeyboardLanguageText(LANGUAGE_GERMAN)), + RadioItem(LANGUAGE_LITHUANIAN, getKeyboardLanguageText(LANGUAGE_LITHUANIAN)), + RadioItem(LANGUAGE_ROMANIAN, getKeyboardLanguageText(LANGUAGE_ROMANIAN)), + RadioItem(LANGUAGE_RUSSIAN, getKeyboardLanguageText(LANGUAGE_RUSSIAN)), + RadioItem(LANGUAGE_SLOVENIAN, getKeyboardLanguageText(LANGUAGE_SLOVENIAN)), + RadioItem(LANGUAGE_SPANISH, getKeyboardLanguageText(LANGUAGE_SPANISH)), + RadioItem(LANGUAGE_TURKISH_Q, getKeyboardLanguageText(LANGUAGE_TURKISH_Q)), + ) + + KeyboardRadioGroupDialog(inputView, items, config.keyboardLanguage) { + config.keyboardLanguage = it as Int + onSelect.invoke() + } + } + + + private fun getKeyboardLanguageText(language: Int): String { + return when (language) { + LANGUAGE_BENGALI -> context.getString(R.string.translation_bengali) + LANGUAGE_BULGARIAN -> context.getString(R.string.translation_bulgarian) + LANGUAGE_ENGLISH_DVORAK -> "${context.getString(R.string.translation_english)} (DVORAK)" + LANGUAGE_ENGLISH_QWERTZ -> "${context.getString(R.string.translation_english)} (QWERTZ)" + LANGUAGE_FRENCH -> context.getString(R.string.translation_french) + LANGUAGE_GERMAN -> context.getString(R.string.translation_german) + LANGUAGE_LITHUANIAN -> context.getString(R.string.translation_lithuanian) + LANGUAGE_ROMANIAN -> context.getString(R.string.translation_romanian) + LANGUAGE_RUSSIAN -> context.getString(R.string.translation_russian) + LANGUAGE_SLOVENIAN -> context.getString(R.string.translation_slovenian) + LANGUAGE_SPANISH -> context.getString(R.string.translation_spanish) + LANGUAGE_TURKISH_Q -> "${context.getString(R.string.translation_turkish)} (Q)" + else -> "${context.getString(R.string.translation_english)} (QWERTY)" + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/keyboard/dialogs/KeyboardRadioGroupDialog.kt b/app/src/main/kotlin/com/simplemobiletools/keyboard/dialogs/KeyboardRadioGroupDialog.kt new file mode 100644 index 0000000..f803483 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/keyboard/dialogs/KeyboardRadioGroupDialog.kt @@ -0,0 +1,83 @@ +package com.simplemobiletools.keyboard.dialogs + +import android.view.ContextThemeWrapper +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.RadioButton +import android.widget.RadioGroup +import androidx.appcompat.app.AlertDialog +import com.simplemobiletools.commons.extensions.onGlobalLayout +import com.simplemobiletools.commons.models.RadioItem +import com.simplemobiletools.keyboard.R +import com.simplemobiletools.keyboard.extensions.getKeyboardDialogBuilder +import com.simplemobiletools.keyboard.extensions.setupKeyboardDialogStuff +import kotlinx.android.synthetic.main.dialog_radio_group.view.dialog_radio_group +import kotlinx.android.synthetic.main.dialog_radio_group.view.dialog_radio_holder + +class KeyboardRadioGroupDialog( + private val inputView: View, + private val items: ArrayList, + private val checkedItemId: Int = -1, + private val titleId: Int = 0, + showOKButton: Boolean = false, + private val cancelCallback: (() -> Unit)? = null, + private val callback: (newValue: Any) -> Unit +) { + private val context = ContextThemeWrapper(inputView.context, R.style.MyKeyboard_Alert) + private var dialog: AlertDialog? = null + private var wasInit = false + private var selectedItemId = -1 + private val layoutInflater = LayoutInflater.from(context) + + init { + val view = layoutInflater.inflate(R.layout.dialog_radio_group, null) + view.dialog_radio_group.apply { + for (i in 0 until items.size) { + val radioButton = (layoutInflater.inflate(R.layout.radio_button, null) as RadioButton).apply { + text = items[i].title + isChecked = items[i].id == checkedItemId + id = i + setOnClickListener { itemSelected(i) } + } + + if (items[i].id == checkedItemId) { + selectedItemId = i + } + + addView(radioButton, RadioGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) + } + } + + val builder = context.getKeyboardDialogBuilder() + .setOnCancelListener { cancelCallback?.invoke() } + + if (selectedItemId != -1 && showOKButton) { + builder.setPositiveButton(R.string.ok) { _, _ -> itemSelected(selectedItemId) } + } + + builder.apply { + context.setupKeyboardDialogStuff(inputView.windowToken, view, this, titleId) { alertDialog -> + dialog = alertDialog + } + } + + + if (selectedItemId != -1) { + view.dialog_radio_holder.apply { + onGlobalLayout { + scrollY = view.dialog_radio_group.findViewById(selectedItemId).bottom - height + } + } + } + + wasInit = true + } + + private fun itemSelected(checkedId: Int) { + if (wasInit) { + callback(items[checkedId].value) + dialog?.dismiss() + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/keyboard/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/keyboard/extensions/Context.kt index 23451f9..8b4e193 100644 --- a/app/src/main/kotlin/com/simplemobiletools/keyboard/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/keyboard/extensions/Context.kt @@ -3,9 +3,18 @@ package com.simplemobiletools.keyboard.extensions import android.content.ClipboardManager import android.content.Context import android.graphics.Color -import com.simplemobiletools.commons.extensions.getProperBackgroundColor -import com.simplemobiletools.commons.extensions.isUsingSystemDarkTheme -import com.simplemobiletools.commons.extensions.lightenColor +import android.os.IBinder +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.Window +import android.view.WindowManager +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.core.content.res.ResourcesCompat +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.views.MyTextView import com.simplemobiletools.keyboard.R import com.simplemobiletools.keyboard.databases.ClipsDatabase import com.simplemobiletools.keyboard.helpers.Config @@ -36,3 +45,107 @@ fun Context.getStrokeColor(): Int { } } } + +fun Context.getKeyboardDialogBuilder() = if (baseConfig.isUsingSystemTheme) { + MaterialAlertDialogBuilder(this, R.style.MyKeyboard_Alert) +} else { + AlertDialog.Builder(this, R.style.MyKeyboard_Alert) +} + + +fun Context.setupKeyboardDialogStuff( + windowToken: IBinder, + view: View, + dialog: AlertDialog.Builder, + titleId: Int = 0, + titleText: String = "", + cancelOnTouchOutside: Boolean = true, + callback: ((alertDialog: AlertDialog) -> Unit)? = null +) { + + + val textColor = getProperTextColor() + val backgroundColor = getProperBackgroundColor() + val primaryColor = getProperPrimaryColor() + if (view is ViewGroup) { + updateTextColors(view) + } else if (view is MyTextView) { + view.setColors(textColor, primaryColor, backgroundColor) + } + + if (dialog is MaterialAlertDialogBuilder) { + dialog.create().apply { + if (titleId != 0) { + setTitle(titleId) + } else if (titleText.isNotEmpty()) { + setTitle(titleText) + } + + val lp = window?.attributes + lp?.token = windowToken + lp?.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG + window?.attributes = lp + window?.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) + + setView(view) + setCancelable(cancelOnTouchOutside) + show() + + val bgDrawable = when { + isBlackAndWhiteTheme() -> ResourcesCompat.getDrawable(resources, R.drawable.black_dialog_background, theme) + baseConfig.isUsingSystemTheme -> ResourcesCompat.getDrawable(resources, R.drawable.dialog_you_background, theme) + else -> resources.getColoredDrawableWithColor(R.drawable.dialog_bg, baseConfig.backgroundColor) + } + + window?.setBackgroundDrawable(bgDrawable) + callback?.invoke(this) + } + } else { + var title: TextView? = null + if (titleId != 0 || titleText.isNotEmpty()) { + title = LayoutInflater.from(this).inflate(R.layout.dialog_title, null) as TextView + title.apply { + if (titleText.isNotEmpty()) { + text = titleText + } else { + setText(titleId) + } + setTextColor(textColor) + } + } + + // if we use the same primary and background color, use the text color for dialog confirmation buttons + val dialogButtonColor = if (primaryColor == baseConfig.backgroundColor) { + textColor + } else { + primaryColor + } + + dialog.create().apply { + setView(view) + requestWindowFeature(Window.FEATURE_NO_TITLE) + setCustomTitle(title) + + val lp = window?.attributes + lp?.token = windowToken + lp?.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG + window?.attributes = lp + window?.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) + + setCanceledOnTouchOutside(cancelOnTouchOutside) + show() + getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(dialogButtonColor) + getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(dialogButtonColor) + getButton(AlertDialog.BUTTON_NEUTRAL).setTextColor(dialogButtonColor) + + val bgDrawable = when { + isBlackAndWhiteTheme() -> ResourcesCompat.getDrawable(resources, R.drawable.black_dialog_background, theme) + baseConfig.isUsingSystemTheme -> ResourcesCompat.getDrawable(resources, R.drawable.dialog_you_background, theme) + else -> resources.getColoredDrawableWithColor(R.drawable.dialog_bg, baseConfig.backgroundColor) + } + + window?.setBackgroundDrawable(bgDrawable) + callback?.invoke(this) + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/keyboard/services/SimpleKeyboardIME.kt b/app/src/main/kotlin/com/simplemobiletools/keyboard/services/SimpleKeyboardIME.kt index 877f906..90e960b 100644 --- a/app/src/main/kotlin/com/simplemobiletools/keyboard/services/SimpleKeyboardIME.kt +++ b/app/src/main/kotlin/com/simplemobiletools/keyboard/services/SimpleKeyboardIME.kt @@ -61,18 +61,7 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL inputTypeClass = attribute!!.inputType and TYPE_MASK_CLASS enterKeyType = attribute.imeOptions and (IME_MASK_ACTION or IME_FLAG_NO_ENTER_ACTION) - val keyboardXml = when (inputTypeClass) { - TYPE_CLASS_NUMBER, TYPE_CLASS_DATETIME, TYPE_CLASS_PHONE -> { - keyboardMode = KEYBOARD_SYMBOLS - R.xml.keys_symbols - } - else -> { - keyboardMode = KEYBOARD_LETTERS - getKeyboardLayoutXML() - } - } - - keyboard = MyKeyboard(this, keyboardXml, enterKeyType) + keyboard = getKeyBoard() keyboardView?.setKeyboard(keyboard!!) keyboardView?.setEditorInfo(attribute) updateShiftKeyState() @@ -220,6 +209,27 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL currentInputConnection?.commitText(text, 0) } + override fun reloadKeyboard() { + val keyboard = getKeyBoard() + this.keyboard = keyboard + keyboardView?.setKeyboard(keyboard) + } + + private fun getKeyBoard(): MyKeyboard { + val keyboardXml = when (inputTypeClass) { + TYPE_CLASS_NUMBER, TYPE_CLASS_DATETIME, TYPE_CLASS_PHONE -> { + keyboardMode = KEYBOARD_SYMBOLS + R.xml.keys_symbols + } + else -> { + keyboardMode = KEYBOARD_LETTERS + getKeyboardLayoutXML() + } + } + + return MyKeyboard(this, keyboardXml, enterKeyType) + } + override fun onUpdateSelection(oldSelStart: Int, oldSelEnd: Int, newSelStart: Int, newSelEnd: Int, candidatesStart: Int, candidatesEnd: Int) { super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd) if (newSelStart == newSelEnd) { diff --git a/app/src/main/kotlin/com/simplemobiletools/keyboard/views/MyKeyboardView.kt b/app/src/main/kotlin/com/simplemobiletools/keyboard/views/MyKeyboardView.kt index 649726c..e145897 100644 --- a/app/src/main/kotlin/com/simplemobiletools/keyboard/views/MyKeyboardView.kt +++ b/app/src/main/kotlin/com/simplemobiletools/keyboard/views/MyKeyboardView.kt @@ -37,6 +37,7 @@ import com.simplemobiletools.keyboard.activities.ManageClipboardItemsActivity import com.simplemobiletools.keyboard.activities.SettingsActivity import com.simplemobiletools.keyboard.adapters.ClipsKeyboardAdapter import com.simplemobiletools.keyboard.adapters.EmojisAdapter +import com.simplemobiletools.keyboard.dialogs.ChangeLanguagePopup import com.simplemobiletools.keyboard.extensions.* import com.simplemobiletools.keyboard.helpers.* import com.simplemobiletools.keyboard.helpers.MyKeyboard.Companion.KEYCODE_DELETE @@ -90,6 +91,11 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut * @param text the string to be displayed. */ fun onText(text: String) + + /** + * Called to force the KeyboardView to reload the keyboard + */ + fun reloadKeyboard() } private var mKeyboard: MyKeyboard? = null @@ -932,7 +938,15 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut } val popupKey = mKeys[mCurrentKey] - val result = onLongPress(popupKey, me) + val result = if (popupKey.code == KEYCODE_EMOJI) { + ChangeLanguagePopup(this, onSelect = { + mOnKeyboardActionListener?.reloadKeyboard() + }) + true + } else { + onLongPress(popupKey, me) + } + if (result) { mAbortKey = true showPreview(NOT_A_KEY) @@ -982,6 +996,10 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut override fun onText(text: String) { mOnKeyboardActionListener!!.onText(text) } + + override fun reloadKeyboard() { + mOnKeyboardActionListener!!.reloadKeyboard() + } } val keyboard = if (popupKey.popupCharacters != null) { diff --git a/app/src/main/res/layout/dialog_radio_group.xml b/app/src/main/res/layout/dialog_radio_group.xml new file mode 100644 index 0000000..d081a64 --- /dev/null +++ b/app/src/main/res/layout/dialog_radio_group.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/app/src/main/res/layout/radio_button_item.xml b/app/src/main/res/layout/radio_button_item.xml new file mode 100644 index 0000000..1e1ff71 --- /dev/null +++ b/app/src/main/res/layout/radio_button_item.xml @@ -0,0 +1,5 @@ + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 7b1d476..18a3639 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -13,4 +13,11 @@ + +