Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 0 additions & 45 deletions app/src/main/java/be/scri/helpers/KeyboardBase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -660,51 +660,6 @@ class KeyboardBase {
}
}

if (currentKeyboardMode == keyboardLettersMode && mKeys != null) {
val spaceKey = mKeys!!.find { it?.code == 32 } ?: return
val emojiKey = mKeys!!.find { it?.code == KEYCODE_EMOJI } ?: return
val commaKey = mKeys!!.find { it?.code == ','.code } ?: return
val periodKey = mKeys!!.find { it?.code == '.'.code }
val enterKey = mKeys!!.find { it?.code == KEYCODE_ENTER }
val modeChangeKey = mKeys!!.find { it?.code == KEYCODE_MODE_CHANGE }

val row = mRows.lastOrNull() ?: return

val clipWidth = (mDisplayWidth * 0.10).toInt()

val clipKey = Key(row)
clipKey.code = KEYCODE_CLIPBOARD
clipKey.width = clipWidth
clipKey.height = spaceKey.height
clipKey.gap = spaceKey.gap
clipKey.icon = context.resources.getDrawable(R.drawable.ic_clipboard_vector, context.theme)
clipKey.icon?.setBounds(0, 0, clipKey.icon!!.intrinsicWidth, clipKey.icon!!.intrinsicHeight)

spaceKey.width -= (clipWidth + clipKey.gap)

val emojiIdxInList = mKeys!!.indexOf(emojiKey)
val emojiIdxInRow = row.mKeys.indexOf(emojiKey)

if (emojiIdxInList != -1 && emojiIdxInRow != -1) {
modeChangeKey?.let { emojiKey.x = it.x + it.width + it.gap }

clipKey.x = emojiKey.x + emojiKey.width + emojiKey.gap
clipKey.y = emojiKey.y

commaKey.x = clipKey.x + clipKey.width + clipKey.gap
commaKey.y = emojiKey.y

spaceKey.x = commaKey.x + commaKey.width + commaKey.gap
periodKey?.x = spaceKey.x + spaceKey.width + spaceKey.gap

val lastPositionedKey = periodKey ?: spaceKey
enterKey?.x = lastPositionedKey.x + lastPositionedKey.width + lastPositionedKey.gap

mKeys!!.add(emojiIdxInList + 1, clipKey)
row.mKeys.add(emojiIdxInRow + 1, clipKey)
}
}

mHeight = y
}

Expand Down
158 changes: 108 additions & 50 deletions app/src/main/java/be/scri/views/KeyboardView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import be.scri.helpers.KeyboardBase.Companion.KEYCODE_TAB
import be.scri.helpers.KeyboardBase.Companion.SHIFT_LOCKED
import be.scri.helpers.KeyboardBase.MyCustomActions
import be.scri.helpers.MAX_KEYS_PER_MINI_ROW
import be.scri.helpers.PreferencesHelper
import be.scri.helpers.SHIFT_OFF
import be.scri.helpers.SHIFT_ON_ONE_CHAR
import be.scri.helpers.SHIFT_ON_PERMANENT
Expand Down Expand Up @@ -1151,12 +1152,41 @@ class KeyboardView
}

// Controls where icons are located on their keys.
val drawableX = (key.width - key.icon!!.intrinsicWidth) / 2
val drawableY = (key.height - key.icon!!.intrinsicHeight) / 2
var iconWidth = key.icon!!.intrinsicWidth
var iconHeight = key.icon!!.intrinsicHeight
val isEmojiOrClipboard = code == KeyboardBase.KEYCODE_EMOJI || code == KeyboardBase.KEYCODE_CLIPBOARD
val scaleFactor = if (isEmojiOrClipboard) 0.45f else 0.6f
val maxIconWidth = (key.width * scaleFactor).toInt()
val maxIconHeight = (key.height * scaleFactor).toInt()
if (iconWidth > maxIconWidth || iconHeight > maxIconHeight) {
val ratio = iconWidth.toFloat() / iconHeight.toFloat()
if (ratio > 1) {
iconWidth = maxIconWidth
iconHeight = (maxIconWidth / ratio).toInt()
} else {
iconHeight = maxIconHeight
iconWidth = (maxIconHeight * ratio).toInt()
}
}
val drawableX = (key.width - iconWidth) / 2
val drawableY = (key.height - iconHeight) / 2
canvas.translate(drawableX.toFloat(), drawableY.toFloat())
key.icon!!.setBounds(0, 0, key.icon!!.intrinsicWidth, key.icon!!.intrinsicHeight)
key.icon!!.setBounds(0, 0, iconWidth, iconHeight)
key.icon!!.draw(canvas)
canvas.translate(-drawableX.toFloat(), -drawableY.toFloat())

if (code == KeyboardBase.KEYCODE_EMOJI) {
val settingsIcon = resources.getDrawable(R.drawable.ic_settings_cog_vector, context.theme)
settingsIcon.applyColorFilter(mTextColor)
val density = context.resources.displayMetrics.density
val cogSize = (12 * density).toInt()
val rightPadding = keyMargin - shadowOffset + padding + (2 * density).toInt()
val topPadding = keyMargin - shadowOffset + padding + (2 * density).toInt()
val cogX = key.width - cogSize - rightPadding
val cogY = topPadding
settingsIcon.setBounds(cogX, cogY, cogX + cogSize, cogY + cogSize)
settingsIcon.draw(canvas)
}
}
canvas.translate(-key.x.toFloat(), -key.y.toFloat())
}
Expand Down Expand Up @@ -1396,6 +1426,16 @@ class KeyboardView
}

private fun openPopupIfRequired(me: MotionEvent): Boolean {
val currentKey = if (mCurrentKey in mKeys.indices) mKeys[mCurrentKey] else null
if (currentKey?.code == KeyboardBase.KEYCODE_EMOJI) {
val result = onLongPress(currentKey, me)
if (result) {
mAbortKey = true
showPreview(NOT_A_KEY)
}
return result
}

if (mPopupLayout == 0 || mCurrentKey !in mKeys.indices) {
return false
}
Expand All @@ -1410,21 +1450,14 @@ class KeyboardView
return result
}

/**
* Called when a key is long pressed.
* By default this will open any popup keyboard associated with this key through the attributes
* popupLayout and popupCharacters.
*
* @param popupKey The key that was long pressed.
*
* @return true if the long press is handled, false otherwise.
* Subclasses should call the method on the base class if the subclass doesn't wish to
* handle the call.
*/
private fun onLongPress(
popupKey: KeyboardBase.Key,
me: MotionEvent,
): Boolean {
if (popupKey.code == KeyboardBase.KEYCODE_EMOJI) {
popupKey.popupResId = R.xml.keys_emoji_popup
}

val popupKeyboardId = popupKey.popupResId
if (popupKeyboardId != 0) {
mMiniKeyboardContainer = mMiniKeyboardCache[popupKey]
Expand Down Expand Up @@ -1517,11 +1550,9 @@ class KeyboardView
mPopupX = popupKey.x
mPopupY = popupKey.y

val widthToUse =
mMiniKeyboardContainer!!.measuredWidth -
(popupKey.popupCharacters!!.length / 2) *
popupKey.width
mPopupX = mPopupX + popupKey.width - widthToUse
var leftX = popupKey.x + (popupKey.width - mMiniKeyboardContainer!!.measuredWidth) / 2
leftX = leftX.coerceIn(0, (width - mMiniKeyboardContainer!!.measuredWidth).coerceAtLeast(0))
mPopupX = leftX
mPopupY -= mMiniKeyboardContainer!!.measuredHeight
val x = mPopupX + mCoordinates[0]
val y = mPopupY + mCoordinates[1]
Expand All @@ -1544,7 +1575,14 @@ class KeyboardView
}
selectedKeyIndex = Math.max(0, Math.min(selectedKeyIndex, keysCnt - 1))

if (setHoldForAltCharacters) {
val isEmojiPopup = mMiniKeyboard!!.mKeys.any { it.code == KeyboardBase.KEYCODE_EMOJI || it.code == KeyboardBase.KEYCODE_CLIPBOARD }
if (isEmojiPopup) {
// Emoji popup: start with no pre-selection; user slides to choose and lifts to confirm.
for (i in 0 until keysCnt) {
mMiniKeyboard!!.mKeys[i].focused = false
}
mMiniKeyboardSelectedKeyIndex = -1
} else if (setHoldForAltCharacters) {
for (i in 0 until keysCnt) {
mMiniKeyboard!!.mKeys[i].focused = i == selectedKeyIndex
}
Expand Down Expand Up @@ -1591,6 +1629,7 @@ class KeyboardView
}

if (mPopupKeyboard.isShowing) {
val isEmojiPopup = mMiniKeyboard?.mKeys?.any { it.code == KeyboardBase.KEYCODE_EMOJI || it.code == KeyboardBase.KEYCODE_CLIPBOARD } == true
when (action) {
MotionEvent.ACTION_MOVE -> {
val miniKeyboard = mMiniKeyboard
Expand All @@ -1601,11 +1640,11 @@ class KeyboardView
miniKeyboard.getGlobalVisibleRect(popupRect)
val widthPerKey = miniKeyboard.width / keysCnt.toFloat()

var selectedKeyIndex = ((me.x - popupRect.left) / widthPerKey).toInt()
var selectedKeyIndex = ((me.rawX - popupRect.left) / widthPerKey).toInt()
selectedKeyIndex = selectedKeyIndex.coerceIn(0, keysCnt - 1)

if (selectedKeyIndex != mMiniKeyboardSelectedKeyIndex) {
if (setHoldForAltCharacters) {
if (setHoldForAltCharacters || isEmojiPopup) {
for (i in 0 until keysCnt) {
miniKeyboard.mKeys[i].focused = i == selectedKeyIndex
}
Expand All @@ -1624,35 +1663,54 @@ class KeyboardView

mMiniKeyboardSelectedKeyIndex = selectedKeyIndex

if (setHoldForAltCharacters) {
// Hold mode, so use long delay.
hoverRunnable =
Runnable {
val key = miniKeyboard.mKeys[mMiniKeyboardSelectedKeyIndex]
key.focused = false
miniKeyboard.invalidateAllKeys()

mOnKeyboardActionListener?.onKey(key.code)
mMiniKeyboardSelectedKeyIndex = -1
hoverRunnable = null
dismissPopupKeyboard()
}
hoverHandler?.postDelayed(hoverRunnable!!, hoverDelay)
} else {
hoverRunnable =
Runnable {
val key = miniKeyboard.mKeys[mMiniKeyboardSelectedKeyIndex]
key.focused = false
miniKeyboard.invalidateAllKeys()

mOnKeyboardActionListener?.onKey(key.code)
mMiniKeyboardSelectedKeyIndex = -1
hoverRunnable = null
dismissPopupKeyboard()
}
hoverHandler?.postDelayed(hoverRunnable!!, 220L)
if (!isEmojiPopup) {
// Non-emoji popup: auto-fire after hover delay.
if (setHoldForAltCharacters) {
hoverRunnable =
Runnable {
val key = miniKeyboard.mKeys[mMiniKeyboardSelectedKeyIndex]
key.focused = false
miniKeyboard.invalidateAllKeys()

mOnKeyboardActionListener?.onKey(key.code)
mMiniKeyboardSelectedKeyIndex = -1
hoverRunnable = null
dismissPopupKeyboard()
}
hoverHandler?.postDelayed(hoverRunnable!!, hoverDelay)
} else {
hoverRunnable =
Runnable {
val key = miniKeyboard.mKeys[mMiniKeyboardSelectedKeyIndex]
key.focused = false
miniKeyboard.invalidateAllKeys()

mOnKeyboardActionListener?.onKey(key.code)
mMiniKeyboardSelectedKeyIndex = -1
hoverRunnable = null
dismissPopupKeyboard()
}
hoverHandler?.postDelayed(hoverRunnable!!, 220L)
}
}
// Emoji popup: no auto-fire on hover; wait for finger lift (ACTION_UP).
}
}
}

MotionEvent.ACTION_UP -> {
if (isEmojiPopup) {
// Fire whichever key is currently highlighted when the finger lifts.
val idx = mMiniKeyboardSelectedKeyIndex
if (idx >= 0 && idx < (mMiniKeyboard?.mKeys?.size ?: 0)) {
val key = mMiniKeyboard!!.mKeys[idx]
key.focused = false
mMiniKeyboard!!.invalidateAllKeys()
mOnKeyboardActionListener?.onKey(key.code)
}
mMiniKeyboardSelectedKeyIndex = -1
dismissPopupKeyboard()
return true
}
}

Expand All @@ -1664,7 +1722,7 @@ class KeyboardView
return onModifiedTouchEvent(me)
}

if (setHoldForAltCharacters) {
if (!isEmojiPopup && setHoldForAltCharacters) {
if (mMiniKeyboardSelectedKeyIndex >= 0) {
val key = mMiniKeyboard!!.mKeys[mMiniKeyboardSelectedKeyIndex]
mOnKeyboardActionListener?.onKey(key.code)
Expand Down
19 changes: 17 additions & 2 deletions app/src/main/res/drawable/ic_settings_cog_vector.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
</selector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportWidth="100"
android:viewportHeight="100">
<group>
<clip-path android:pathData="M0,0h100v100h-100z" />
<path
android:pathData="M50,29.71C47.34,29.71 44.7,30.24 42.24,31.26C39.78,32.28 37.54,33.77 35.65,35.65C33.77,37.54 32.28,39.77 31.26,42.24C30.24,44.7 29.71,47.34 29.71,50C29.71,52.66 30.24,55.3 31.26,57.76C32.28,60.22 33.77,62.46 35.65,64.35C37.54,66.23 39.78,67.72 42.24,68.74C44.7,69.76 47.34,70.29 50,70.29C55.38,70.29 60.54,68.15 64.35,64.35C68.15,60.54 70.29,55.38 70.29,50C70.29,44.62 68.15,39.46 64.35,35.65C60.54,31.85 55.38,29.71 50,29.71ZM35.96,50C35.96,46.28 37.44,42.71 40.07,40.07C42.71,37.44 46.28,35.96 50,35.96C53.72,35.96 57.29,37.44 59.93,40.07C62.56,42.71 64.04,46.28 64.04,50C64.04,53.72 62.56,57.29 59.93,59.93C57.29,62.56 53.72,64.04 50,64.04C46.28,64.04 42.71,62.56 40.07,59.93C37.44,57.29 35.96,53.72 35.96,50Z"
android:fillColor="@android:color/white"
android:fillAlpha="0.9" />
<path
android:pathData="M61.23,8.39C57.93,-2.79 42.07,-2.79 38.78,8.39L38.19,10.39C37.96,11.17 37.56,11.89 37.01,12.49C36.47,13.1 35.79,13.57 35.04,13.89C34.29,14.2 33.48,14.34 32.67,14.29C31.85,14.25 31.06,14.03 30.34,13.64L28.52,12.64C18.27,7.06 7.06,18.27 12.64,28.52L13.64,30.34C14.03,31.06 14.25,31.85 14.3,32.66C14.34,33.48 14.2,34.29 13.89,35.04C13.58,35.79 13.1,36.47 12.49,37.01C11.89,37.56 11.17,37.96 10.39,38.19L8.39,38.78C-2.79,42.07 -2.79,57.93 8.39,61.22L10.39,61.81C11.17,62.04 11.89,62.44 12.49,62.99C13.1,63.53 13.58,64.21 13.89,64.96C14.2,65.71 14.34,66.52 14.3,67.34C14.25,68.15 14.03,68.94 13.64,69.66L12.64,71.48C7.06,81.73 18.27,92.94 28.52,87.36L30.34,86.36C31.06,85.97 31.85,85.75 32.67,85.71C33.48,85.66 34.29,85.8 35.04,86.11C35.79,86.43 36.47,86.9 37.01,87.51C37.56,88.11 37.96,88.83 38.19,89.61L38.78,91.61C42.07,102.79 57.93,102.79 61.23,91.61L61.81,89.61C62.04,88.83 62.44,88.11 62.99,87.51C63.53,86.9 64.21,86.43 64.96,86.11C65.71,85.8 66.52,85.66 67.34,85.71C68.15,85.75 68.94,85.97 69.66,86.36L71.48,87.36C81.73,92.94 92.94,81.73 87.36,71.48L86.36,69.66C85.97,68.94 85.75,68.15 85.71,67.34C85.66,66.52 85.8,65.71 86.11,64.96C86.43,64.21 86.9,63.53 87.51,62.99C88.11,62.44 88.83,62.04 89.61,61.81L91.61,61.22C102.79,57.93 102.79,42.07 91.61,38.78L89.61,38.19C88.83,37.96 88.11,37.56 87.51,37.01C86.9,36.47 86.43,35.79 86.11,35.04C85.8,34.29 85.66,33.48 85.71,32.66C85.75,31.85 85.97,31.06 86.36,30.34L87.36,28.52C92.94,18.27 81.73,7.06 71.48,12.64L69.66,13.64C68.94,14.03 68.15,14.25 67.34,14.29C66.52,14.34 65.71,14.2 64.96,13.89C64.21,13.57 63.53,13.1 62.99,12.49C62.44,11.89 62.04,11.17 61.81,10.39L61.23,8.39ZM44.77,10.16C46.31,4.94 53.69,4.94 55.23,10.16L55.82,12.16C56.31,13.83 57.18,15.38 58.34,16.67C59.51,17.97 60.96,18.99 62.57,19.66C64.18,20.33 65.93,20.63 67.67,20.54C69.42,20.44 71.12,19.96 72.65,19.13L74.47,18.13C79.24,15.53 84.47,20.75 81.87,25.53L80.88,27.36C80.04,28.89 79.56,30.59 79.47,32.34C79.38,34.08 79.68,35.82 80.34,37.44C81.01,39.05 82.03,40.49 83.33,41.66C84.63,42.83 86.18,43.69 87.85,44.18L89.84,44.77C95.06,46.31 95.06,53.69 89.84,55.23L87.84,55.82C86.17,56.31 84.63,57.18 83.33,58.34C82.03,59.51 81.01,60.96 80.34,62.57C79.67,64.18 79.37,65.93 79.46,67.67C79.56,69.41 80.04,71.12 80.88,72.65L81.88,74.47C84.47,79.24 79.25,84.47 74.47,81.87L72.65,80.88C71.12,80.04 69.41,79.56 67.67,79.46C65.93,79.37 64.18,79.67 62.57,80.34C60.95,81.01 59.51,82.03 58.34,83.33C57.17,84.63 56.31,86.17 55.82,87.85L55.23,89.84C53.69,95.06 46.31,95.06 44.77,89.84L44.18,87.84C43.69,86.17 42.83,84.63 41.66,83.33C40.49,82.03 39.05,81.01 37.43,80.34C35.82,79.67 34.08,79.37 32.33,79.47C30.59,79.56 28.89,80.04 27.36,80.88L25.53,81.88C20.76,84.47 15.53,79.25 18.13,74.47L19.13,72.65C19.96,71.12 20.45,69.41 20.54,67.67C20.63,65.92 20.33,64.18 19.67,62.57C19,60.95 17.98,59.51 16.68,58.34C15.38,57.17 13.83,56.31 12.16,55.81L10.16,55.23C4.94,53.69 4.94,46.3 10.16,44.76L12.16,44.17C13.83,43.68 15.37,42.82 16.67,41.65C17.97,40.48 18.99,39.04 19.66,37.43C20.32,35.82 20.62,34.08 20.53,32.33C20.44,30.59 19.96,28.89 19.13,27.36L18.13,25.53C15.53,20.76 20.75,15.53 25.53,18.13L27.36,19.13C28.89,19.96 30.59,20.44 32.33,20.53C34.08,20.63 35.82,20.33 37.43,19.66C39.05,18.99 40.49,17.97 41.66,16.67C42.83,15.37 43.69,13.83 44.18,12.16L44.77,10.16Z"
android:fillColor="@android:color/white"
android:fillAlpha="0.9" />
</group>
</vector>
Loading
Loading