feature: support switching language on longpress of emoji key

This commit is contained in:
darthpaul 2022-11-04 23:11:43 +00:00
parent 3cfd184387
commit 210da88052
8 changed files with 325 additions and 16 deletions

View file

@ -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)"
}
}
}

View file

@ -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<RadioItem>,
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<View>(selectedItemId).bottom - height
}
}
}
wasInit = true
}
private fun itemSelected(checkedId: Int) {
if (wasInit) {
callback(items[checkedId].value)
dialog?.dismiss()
}
}
}

View file

@ -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)
}
}
}

View file

@ -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) {

View file

@ -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) {

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_radio_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioGroup
android:id="@+id/dialog_radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/activity_margin"
android:paddingTop="@dimen/normal_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingBottom="@dimen/normal_margin" />
</ScrollView>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_radio_button"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

View file

@ -13,4 +13,11 @@
<style name="MyKeyboardViewPopup" parent="MyKeyboard">
<item name="keyTextSize">@dimen/preview_text_size</item>
</style>
<style name="MyKeyboard.Alert" parent="Theme.Material3.Light.Dialog.Alert">
<item name="colorAccent">@color/color_primary</item>
<item name="android:buttonBarNegativeButtonStyle">@style/DialogButtonStyle</item>
<item name="android:buttonBarPositiveButtonStyle">@style/DialogButtonStyle</item>
<item name="android:buttonBarNeutralButtonStyle">@style/DialogButtonStyle</item>
</style>
</resources>