Added ghost key functionality. (#961)

* Added ghost key functionality. This lets you swipe symbol keys which are not visible, but exist in the corresponding location in the numerical view.

* Added comments and descriptions, simplified key composition logic, ran linter
This commit is contained in:
Matthew Sirman 2024-07-08 00:47:25 +01:00 committed by GitHub
parent 09dec63ce8
commit eb252a5d78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 78 additions and 4 deletions

View File

@ -104,6 +104,10 @@ Enabling `Backspace: Allow normal swipes to work with slide gestures`, in keyboa
- You can type the opposite-case letter by dragging away from, then back to the original key.
- Clockwise and counter-clockwise drags can be configured to type the opposite-case letter, or the equivalent center key on the numeric layout.
### Ghost keys
Enabling `Ghost keys` in keyboard settings will enable swiping hidden symbol keys without switching to the numeric layout.
### Coming from MessagEase Keyboard Tips
- For those use to tapping on the hand key to hide the MessagEase Keyboard and make more screen real estate available, this can be done by using the Android back key or on Android 10 and higher using the guesture navigation for back.

View File

@ -56,6 +56,7 @@ const val DEFAULT_DRAG_RETURN_ENABLED = 1
const val DEFAULT_CIRCULAR_DRAG_ENABLED = 1
const val DEFAULT_CLOCKWISE_DRAG_ACTION = 0
const val DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION = 1
const val DEFAULT_GHOST_KEYS_ENABLED = 0
@Entity
data class AppSettings(
@ -226,6 +227,11 @@ data class AppSettings(
defaultValue = DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION.toString(),
)
val counterclockwiseDragAction: Int,
@ColumnInfo(
name = "ghost_keys_enabled",
defaultValue = DEFAULT_GHOST_KEYS_ENABLED.toString(),
)
val ghostKeysEnabled: Int,
)
data class LayoutsUpdate(
@ -334,6 +340,8 @@ data class BehaviorUpdate(
val clockwiseDragAction: Int,
@ColumnInfo(name = "counterclockwise_drag_action")
val counterclockwiseDragAction: Int,
@ColumnInfo(name = "ghost_keys_enabled")
val ghostKeysEnabled: Int,
)
@Dao
@ -565,8 +573,17 @@ val MIGRATION_14_15 =
}
}
val MIGRATION_15_16 =
object : Migration(15, 16) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"alter table AppSettings add column ghost_keys_enabled INTEGER NOT NULL default $DEFAULT_GHOST_KEYS_ENABLED",
)
}
}
@Database(
version = 15,
version = 16,
entities = [AppSettings::class],
exportSchema = true,
)
@ -603,6 +620,7 @@ abstract class AppDB : RoomDatabase() {
MIGRATION_12_13,
MIGRATION_13_14,
MIGRATION_14_15,
MIGRATION_15_16,
)
// Necessary because it can't insert data on creation
.addCallback(

View File

@ -60,6 +60,7 @@ import com.dessalines.thumbkey.utils.KeyboardDefinitionSettings
import com.dessalines.thumbkey.utils.Selection
import com.dessalines.thumbkey.utils.SlideType
import com.dessalines.thumbkey.utils.SwipeDirection
import com.dessalines.thumbkey.utils.SwipeNWay
import com.dessalines.thumbkey.utils.buildTapActions
import com.dessalines.thumbkey.utils.circularDirection
import com.dessalines.thumbkey.utils.colorVariantToColor
@ -80,6 +81,10 @@ import kotlin.math.min
@Composable
fun KeyboardKey(
key: KeyItemC,
// Hidden background key to detect swipes for. When a swipe isn't captured by the key, the ghost
// key will attempt to capture it instead. This is derived automatically from the keyboard
// layout, and should not be set directly in the keyboard definition.
ghostKey: KeyItemC? = null,
lastAction: MutableState<KeyAction?>,
animationHelperSpeed: Int,
animationSpeed: Int,
@ -120,7 +125,7 @@ fun KeyboardKey(
) {
// Necessary for swipe settings to get updated correctly
val id =
key.toString() + animationHelperSpeed + animationSpeed + autoCapitalize +
key.toString() + ghostKey.toString() + animationHelperSpeed + animationSpeed + autoCapitalize +
vibrateOnTap + soundOnTap + legendHeight + legendWidth + minSwipeLength + slideSensitivity +
slideEnabled + slideCursorMovementMode + slideSpacebarDeadzoneEnabled +
slideBackspaceDeadzoneEnabled + dragReturnEnabled + circularDragEnabled +
@ -469,8 +474,15 @@ fun KeyboardKey(
)
} else {
val swipeDirection =
swipeDirection(offsetX, offsetY, minSwipeLength, key.swipeType)
key.swipes?.get(swipeDirection)?.action
swipeDirection(
offsetX,
offsetY,
minSwipeLength,
if (ghostKey == null) key.swipeType else SwipeNWay.EIGHT_WAY,
)
key.swipes?.get(swipeDirection)?.action ?: (
ghostKey?.swipes?.get(swipeDirection)?.action
)
}
) ?: key.center.action

View File

@ -39,6 +39,7 @@ import com.dessalines.thumbkey.db.DEFAULT_CIRCULAR_DRAG_ENABLED
import com.dessalines.thumbkey.db.DEFAULT_CLOCKWISE_DRAG_ACTION
import com.dessalines.thumbkey.db.DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION
import com.dessalines.thumbkey.db.DEFAULT_DRAG_RETURN_ENABLED
import com.dessalines.thumbkey.db.DEFAULT_GHOST_KEYS_ENABLED
import com.dessalines.thumbkey.db.DEFAULT_HIDE_LETTERS
import com.dessalines.thumbkey.db.DEFAULT_HIDE_SYMBOLS
import com.dessalines.thumbkey.db.DEFAULT_KEYBOARD_LAYOUT
@ -143,6 +144,7 @@ fun KeyboardScreen(
val clockwiseDragAction = CircularDragAction.entries[settings?.clockwiseDragAction ?: DEFAULT_CLOCKWISE_DRAG_ACTION]
val counterclockwiseDragAction =
CircularDragAction.entries[settings?.counterclockwiseDragAction ?: DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION]
val ghostKeysEnabled = (settings?.ghostKeysEnabled ?: DEFAULT_GHOST_KEYS_ENABLED).toBool()
val keyBorderWidthFloat = keyBorderWidth / 10.0f
val keyBorderColour = MaterialTheme.colorScheme.outline
@ -362,8 +364,19 @@ fun KeyboardScreen(
Row {
row.forEachIndexed { j, key ->
Column {
val ghostKey =
if (ghostKeysEnabled) {
when (mode) {
KeyboardMode.MAIN -> keyboardDefinition.modes.numeric
KeyboardMode.SHIFTED -> keyboardDefinition.modes.numeric
else -> null
}?.arr?.getOrNull(i)?.getOrNull(j)
} else {
null
}
KeyboardKey(
key = key,
ghostKey = ghostKey,
lastAction = lastAction,
legendHeight = legendHeight,
legendWidth = legendWidth,

View File

@ -46,6 +46,7 @@ import com.dessalines.thumbkey.db.DEFAULT_CIRCULAR_DRAG_ENABLED
import com.dessalines.thumbkey.db.DEFAULT_CLOCKWISE_DRAG_ACTION
import com.dessalines.thumbkey.db.DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION
import com.dessalines.thumbkey.db.DEFAULT_DRAG_RETURN_ENABLED
import com.dessalines.thumbkey.db.DEFAULT_GHOST_KEYS_ENABLED
import com.dessalines.thumbkey.db.DEFAULT_HIDE_LETTERS
import com.dessalines.thumbkey.db.DEFAULT_HIDE_SYMBOLS
import com.dessalines.thumbkey.db.DEFAULT_KEYBOARD_LAYOUT
@ -306,6 +307,7 @@ private fun resetAppSettingsToDefault(appSettingsViewModel: AppSettingsViewModel
circularDragEnabled = DEFAULT_CIRCULAR_DRAG_ENABLED,
clockwiseDragAction = DEFAULT_CLOCKWISE_DRAG_ACTION,
counterclockwiseDragAction = DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION,
ghostKeysEnabled = DEFAULT_GHOST_KEYS_ENABLED,
),
)
}

View File

@ -12,6 +12,7 @@ import androidx.compose.material.icons.automirrored.outlined.Backspace
import androidx.compose.material.icons.automirrored.outlined.RotateLeft
import androidx.compose.material.icons.automirrored.outlined.RotateRight
import androidx.compose.material.icons.outlined.Abc
import androidx.compose.material.icons.outlined.BorderInner
import androidx.compose.material.icons.outlined.Circle
import androidx.compose.material.icons.outlined.SpaceBar
import androidx.compose.material.icons.outlined.SwapHoriz
@ -43,6 +44,7 @@ import com.dessalines.thumbkey.db.DEFAULT_CIRCULAR_DRAG_ENABLED
import com.dessalines.thumbkey.db.DEFAULT_CLOCKWISE_DRAG_ACTION
import com.dessalines.thumbkey.db.DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION
import com.dessalines.thumbkey.db.DEFAULT_DRAG_RETURN_ENABLED
import com.dessalines.thumbkey.db.DEFAULT_GHOST_KEYS_ENABLED
import com.dessalines.thumbkey.db.DEFAULT_MIN_SWIPE_LENGTH
import com.dessalines.thumbkey.db.DEFAULT_SLIDE_BACKSPACE_DEADZONE_ENABLED
import com.dessalines.thumbkey.db.DEFAULT_SLIDE_CURSOR_MOVEMENT_MODE
@ -94,6 +96,7 @@ fun BehaviorActivity(
var clockwiseDragActionState = CircularDragAction.entries[settings?.clockwiseDragAction ?: DEFAULT_CLOCKWISE_DRAG_ACTION]
var counterclockwiseDragActionState =
CircularDragAction.entries[settings?.counterclockwiseDragAction ?: DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION]
var ghostKeysEnabledState = (settings?.ghostKeysEnabled ?: DEFAULT_GHOST_KEYS_ENABLED).toBool()
val snackbarHostState = remember { SnackbarHostState() }
@ -117,6 +120,7 @@ fun BehaviorActivity(
circularDragEnabled = circularDragEnabledState.toInt(),
clockwiseDragAction = clockwiseDragActionState.ordinal,
counterclockwiseDragAction = counterclockwiseDragActionState.ordinal,
ghostKeysEnabled = ghostKeysEnabledState.toInt(),
),
)
}
@ -381,6 +385,25 @@ fun BehaviorActivity(
)
},
)
SwitchPreference(
value = ghostKeysEnabledState,
onValueChange = {
ghostKeysEnabledState = it
updateBehavior()
},
title = {
Text(stringResource(R.string.ghost_keys_enable))
},
summary = {
Text(stringResource(R.string.ghost_keys_description))
},
icon = {
Icon(
imageVector = Icons.Outlined.BorderInner,
contentDescription = stringResource(R.string.ghost_keys_enable),
)
},
)
SettingsDivider()
TestOutTextField()
}

View File

@ -85,4 +85,6 @@
<string name="send_oppsite_case">Opposite case letter</string>
<string name="send_numeric">Number key</string>
<string name="circular_drag_enable">Circular drag</string>
<string name="ghost_keys_enable">Ghost keys</string>
<string name="ghost_keys_description">Access hidden symbol keys without switching to numeric view</string>
</resources>