diff --git a/app/src/main/java/com/dessalines/thumbkey/db/AppDb.kt b/app/src/main/java/com/dessalines/thumbkey/db/AppDb.kt index 993dfb60..e0cb3522 100644 --- a/app/src/main/java/com/dessalines/thumbkey/db/AppDb.kt +++ b/app/src/main/java/com/dessalines/thumbkey/db/AppDb.kt @@ -47,6 +47,8 @@ const val DEFAULT_HIDE_LETTERS = 0 const val DEFAULT_HIDE_SYMBOLS = 0 const val DEFAULT_KEY_BORDERS = 1 const val DEFAULT_SPACEBAR_MULTITAPS = 1 +const val DEFAULT_SLIDE_SENSITIVITY = 9 +const val DEFAULT_SLIDE_ENABLED = 0 @Entity data class AppSettings( @@ -90,6 +92,11 @@ data class AppSettings( name = "sound_on_tap", defaultValue = DEFAULT_SOUND_ON_TAP.toString(), ) + val slideEnabled: Int, + @ColumnInfo( + name = "slide_enabled", + defaultValue = DEFAULT_SLIDE_ENABLED.toString(), + ) val soundOnTap: Int, @ColumnInfo( name = "theme", @@ -112,6 +119,11 @@ data class AppSettings( defaultValue = DEFAULT_MIN_SWIPE_LENGTH.toString(), ) val minSwipeLength: Int, + @ColumnInfo( + name = "slide_sensitivity", + defaultValue = DEFAULT_SLIDE_SENSITIVITY.toString(), + ) + val slideSensitivity: Int, @ColumnInfo( name = "pushup_size", defaultValue = DEFAULT_PUSHUP_SIZE.toString(), @@ -275,8 +287,19 @@ val MIGRATION_8_9 = object : Migration(8, 9) { } } +val MIGRATION_9_10 = object : Migration(9, 10) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + "alter table AppSettings add column slide_enabled INTEGER NOT NULL default $DEFAULT_SLIDE_ENABLED", + ) + database.execSQL( + "alter table AppSettings add column slide_sensitivity INTEGER NOT NULL default $DEFAULT_SLIDE_SENSITIVITY", + ) + } +} + @Database( - version = 9, + version = 10, entities = [AppSettings::class], exportSchema = true, ) @@ -308,6 +331,7 @@ abstract class AppDB : RoomDatabase() { MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9, + MIGRATION_9_10, ) // Necessary because it can't insert data on creation .addCallback(object : Callback() { diff --git a/app/src/main/java/com/dessalines/thumbkey/keyboards/CommonKeys.kt b/app/src/main/java/com/dessalines/thumbkey/keyboards/CommonKeys.kt index 7bce75ad..9d201549 100644 --- a/app/src/main/java/com/dessalines/thumbkey/keyboards/CommonKeys.kt +++ b/app/src/main/java/com/dessalines/thumbkey/keyboards/CommonKeys.kt @@ -21,6 +21,7 @@ import com.dessalines.thumbkey.utils.KeyAction import com.dessalines.thumbkey.utils.KeyC import com.dessalines.thumbkey.utils.KeyDisplay import com.dessalines.thumbkey.utils.KeyItemC +import com.dessalines.thumbkey.utils.SlideType import com.dessalines.thumbkey.utils.SwipeDirection import com.dessalines.thumbkey.utils.SwipeNWay @@ -117,6 +118,7 @@ val SPACEBAR_KEY_ITEM = action = KeyAction.CommitText(" "), ), swipeType = SwipeNWay.TWO_WAY_HORIZONTAL, + slideType = SlideType.MOVE_CURSOR, swipes = mapOf( SwipeDirection.LEFT to KeyC( action = KeyAction.SendEvent( @@ -157,6 +159,7 @@ val SPACEBAR_PROGRAMMER_KEY_ITEM = action = KeyAction.CommitText(" "), ), swipeType = SwipeNWay.FOUR_WAY_CROSS, + slideType = SlideType.MOVE_CURSOR, swipes = mapOf( SwipeDirection.LEFT to KeyC( action = KeyAction.SendEvent( diff --git a/app/src/main/java/com/dessalines/thumbkey/ui/components/keyboard/KeyboardKey.kt b/app/src/main/java/com/dessalines/thumbkey/ui/components/keyboard/KeyboardKey.kt index f61a9588..b38cf552 100644 --- a/app/src/main/java/com/dessalines/thumbkey/ui/components/keyboard/KeyboardKey.kt +++ b/app/src/main/java/com/dessalines/thumbkey/ui/components/keyboard/KeyboardKey.kt @@ -1,6 +1,7 @@ package com.dessalines.thumbkey.ui.components.keyboard import android.content.Context import android.media.AudioManager +import android.view.KeyEvent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.EnterTransition import androidx.compose.animation.core.tween @@ -44,6 +45,7 @@ import com.dessalines.thumbkey.utils.KeyAction import com.dessalines.thumbkey.utils.KeyC import com.dessalines.thumbkey.utils.KeyDisplay import com.dessalines.thumbkey.utils.KeyItemC +import com.dessalines.thumbkey.utils.SlideType import com.dessalines.thumbkey.utils.SwipeDirection import com.dessalines.thumbkey.utils.buildTapActions import com.dessalines.thumbkey.utils.colorVariantToColor @@ -51,6 +53,7 @@ import com.dessalines.thumbkey.utils.doneKeyAction import com.dessalines.thumbkey.utils.fontSizeVariantToFontSize import com.dessalines.thumbkey.utils.performKeyAction import com.dessalines.thumbkey.utils.swipeDirection +import kotlin.math.abs @Composable fun KeyboardKey( @@ -68,6 +71,8 @@ fun KeyboardKey( capsLock: Boolean, keySize: Int, minSwipeLength: Int, + slideSensitivity: Int, + slideEnabled: Boolean, onToggleShiftMode: (enable: Boolean) -> Unit, onToggleNumericMode: (enable: Boolean) -> Unit, onToggleCapsLock: () -> Unit, @@ -76,7 +81,7 @@ fun KeyboardKey( onSwitchPosition: () -> Unit, ) { // Necessary for swipe settings to get updated correctly - val id = key.toString() + animationHelperSpeed + animationSpeed + autoCapitalize + vibrateOnTap + soundOnTap + keySize + minSwipeLength + val id = key.toString() + animationHelperSpeed + animationSpeed + autoCapitalize + vibrateOnTap + soundOnTap + keySize + minSwipeLength + slideSensitivity + slideEnabled val ctx = LocalContext.current val ime = ctx as IMEService @@ -173,30 +178,85 @@ fun KeyboardKey( val (x, y) = dragAmount offsetX += x offsetY += y + if (key.slideType == SlideType.MOVE_CURSOR && slideEnabled) { + if (abs(offsetX) > slideSensitivity) { + var direction = 0 + var shouldMove = false + if (offsetX < 0.00) { + // move left + if (ime.currentInputConnection.getTextBeforeCursor(1, 0)?.length != 0) { + shouldMove = true + } + direction = KeyEvent.KEYCODE_DPAD_LEFT + } else { + // move right + if (ime.currentInputConnection.getTextAfterCursor(1, 0)?.length != 0) { + shouldMove = true + } + direction = KeyEvent.KEYCODE_DPAD_RIGHT + } + if (shouldMove) { + val action = KeyAction.SendEvent( + KeyEvent( + KeyEvent.ACTION_DOWN, + direction, + ), + ) + performKeyAction( + action = action, + ime = ime, + autoCapitalize = autoCapitalize, + onToggleShiftMode = onToggleShiftMode, + onToggleNumericMode = onToggleNumericMode, + onToggleCapsLock = onToggleCapsLock, + onAutoCapitalize = onAutoCapitalize, + onSwitchLanguage = onSwitchLanguage, + onSwitchPosition = onSwitchPosition, + ) + } + offsetX = 0f + offsetY = 0f + } + } }, onDragEnd = { - val swipeDirection = swipeDirection(offsetX, offsetY, minSwipeLength, key.swipeType) - val action = key.swipes?.get(swipeDirection)?.action ?: key.center.action + if (key.slideType == SlideType.NONE || !slideEnabled) { + val swipeDirection = swipeDirection(offsetX, offsetY, minSwipeLength, key.swipeType) + val action = key.swipes?.get(swipeDirection)?.action ?: key.center.action - performKeyAction( - action = action, - ime = ime, - autoCapitalize = autoCapitalize, - onToggleShiftMode = onToggleShiftMode, - onToggleNumericMode = onToggleNumericMode, - onToggleCapsLock = onToggleCapsLock, - onAutoCapitalize = onAutoCapitalize, - onSwitchLanguage = onSwitchLanguage, - onSwitchPosition = onSwitchPosition, - ) - tapCount = 0 - lastAction.value = action + performKeyAction( + action = action, + ime = ime, + autoCapitalize = autoCapitalize, + onToggleShiftMode = onToggleShiftMode, + onToggleNumericMode = onToggleNumericMode, + onToggleCapsLock = onToggleCapsLock, + onAutoCapitalize = onAutoCapitalize, + onSwitchLanguage = onSwitchLanguage, + onSwitchPosition = onSwitchPosition, + ) + tapCount = 0 + lastAction.value = action - // Reset the drags - offsetX = 0f - offsetY = 0f + // Reset the drags + offsetX = 0f + offsetY = 0f - doneKeyAction(scope, action, isDragged, releasedKey, animationHelperSpeed) + doneKeyAction(scope, action, isDragged, releasedKey, animationHelperSpeed) + } else { + doneKeyAction( + scope, + KeyAction.SendEvent( + KeyEvent( + KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_DPAD_RIGHT, + ), + ), + isDragged, + releasedKey, + animationHelperSpeed, + ) + } }, ) } diff --git a/app/src/main/java/com/dessalines/thumbkey/ui/components/keyboard/KeyboardScreen.kt b/app/src/main/java/com/dessalines/thumbkey/ui/components/keyboard/KeyboardScreen.kt index 989044f8..75325db6 100644 --- a/app/src/main/java/com/dessalines/thumbkey/ui/components/keyboard/KeyboardScreen.kt +++ b/app/src/main/java/com/dessalines/thumbkey/ui/components/keyboard/KeyboardScreen.kt @@ -27,6 +27,8 @@ import com.dessalines.thumbkey.db.DEFAULT_KEY_SIZE import com.dessalines.thumbkey.db.DEFAULT_MIN_SWIPE_LENGTH import com.dessalines.thumbkey.db.DEFAULT_POSITION import com.dessalines.thumbkey.db.DEFAULT_PUSHUP_SIZE +import com.dessalines.thumbkey.db.DEFAULT_SLIDE_ENABLED +import com.dessalines.thumbkey.db.DEFAULT_SLIDE_SENSITIVITY import com.dessalines.thumbkey.db.DEFAULT_SOUND_ON_TAP import com.dessalines.thumbkey.db.DEFAULT_SPACEBAR_MULTITAPS import com.dessalines.thumbkey.db.DEFAULT_VIBRATE_ON_TAP @@ -83,6 +85,7 @@ fun KeyboardScreen( val autoCapitalize = (settings?.autoCapitalize ?: DEFAULT_AUTO_CAPITALIZE).toBool() val spacebarMultiTaps = (settings?.spacebarMultiTaps ?: DEFAULT_SPACEBAR_MULTITAPS).toBool() + val slideEnabled = (settings?.slideEnabled ?: DEFAULT_SLIDE_ENABLED).toBool() val keyBorders = (settings?.keyBorders ?: DEFAULT_KEY_BORDERS).toBool() val vibrateOnTap = (settings?.vibrateOnTap ?: DEFAULT_VIBRATE_ON_TAP).toBool() val soundOnTap = (settings?.soundOnTap ?: DEFAULT_SOUND_ON_TAP).toBool() @@ -119,6 +122,8 @@ fun KeyboardScreen( animationHelperSpeed = settings?.animationHelperSpeed ?: DEFAULT_ANIMATION_HELPER_SPEED, minSwipeLength = settings?.minSwipeLength ?: DEFAULT_MIN_SWIPE_LENGTH, + slideSensitivity = settings?.slideSensitivity ?: DEFAULT_SLIDE_SENSITIVITY, + slideEnabled = slideEnabled, onToggleShiftMode = { enable -> mode = if (enable) { KeyboardMode.SHIFTED diff --git a/app/src/main/java/com/dessalines/thumbkey/ui/components/settings/lookandfeel/LookAndFeelActivity.kt b/app/src/main/java/com/dessalines/thumbkey/ui/components/settings/lookandfeel/LookAndFeelActivity.kt index 173a5c5c..47ae20a6 100755 --- a/app/src/main/java/com/dessalines/thumbkey/ui/components/settings/lookandfeel/LookAndFeelActivity.kt +++ b/app/src/main/java/com/dessalines/thumbkey/ui/components/settings/lookandfeel/LookAndFeelActivity.kt @@ -20,6 +20,7 @@ import androidx.compose.material.icons.outlined.MusicNote import androidx.compose.material.icons.outlined.Palette import androidx.compose.material.icons.outlined.ResetTv import androidx.compose.material.icons.outlined.SpaceBar +import androidx.compose.material.icons.outlined.SwapHoriz import androidx.compose.material.icons.outlined.Swipe import androidx.compose.material.icons.outlined.VerticalAlignTop import androidx.compose.material.icons.outlined.Vibration @@ -67,6 +68,8 @@ import com.dessalines.thumbkey.db.DEFAULT_KEY_SIZE import com.dessalines.thumbkey.db.DEFAULT_MIN_SWIPE_LENGTH import com.dessalines.thumbkey.db.DEFAULT_POSITION import com.dessalines.thumbkey.db.DEFAULT_PUSHUP_SIZE +import com.dessalines.thumbkey.db.DEFAULT_SLIDE_ENABLED +import com.dessalines.thumbkey.db.DEFAULT_SLIDE_SENSITIVITY import com.dessalines.thumbkey.db.DEFAULT_SOUND_ON_TAP import com.dessalines.thumbkey.db.DEFAULT_SPACEBAR_MULTITAPS import com.dessalines.thumbkey.db.DEFAULT_THEME @@ -109,6 +112,12 @@ fun LookAndFeelActivity( val minSwipeLengthState = rememberFloatSettingState( (settings?.minSwipeLength ?: DEFAULT_MIN_SWIPE_LENGTH).toFloat(), ) + val slideSensitivityState = rememberFloatSettingState( + (settings?.slideSensitivity ?: DEFAULT_SLIDE_SENSITIVITY).toFloat(), + ) + val slideEnabledState = rememberBooleanSettingState( + (settings?.slideEnabled ?: DEFAULT_SLIDE_ENABLED).toBool(), + ) val positionState = rememberIntSettingState( settings?.position ?: DEFAULT_POSITION, ) @@ -182,6 +191,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -234,6 +245,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -269,6 +282,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -304,6 +319,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -339,6 +356,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -372,6 +391,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -405,6 +426,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -438,6 +461,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -471,6 +496,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -504,6 +531,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -537,6 +566,43 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, + positionState, + autoCapitalizeState, + spacebarMultiTapsState, + keyBordersState, + vibrateOnTapState, + soundOnTapState, + hideLettersState, + hideSymbolsState, + keyboardLayoutsState, + themeState, + themeColorState, + ) + }, + ) + SettingsCheckbox( + state = slideEnabledState, + icon = { + Icon( + imageVector = Icons.Outlined.SpaceBar, + contentDescription = stringResource(R.string.spacebar_slide), + ) + }, + title = { + Text(stringResource(R.string.spacebar_slide)) + }, + onCheckedChange = { + updateAppSettings( + appSettingsViewModel, + keySizeState, + pushupSizeState, + animationSpeedState, + animationHelperSpeedState, + minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -570,6 +636,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -605,6 +673,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -640,6 +710,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -675,6 +747,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -710,6 +784,8 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -750,6 +826,50 @@ fun LookAndFeelActivity( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, + positionState, + autoCapitalizeState, + spacebarMultiTapsState, + keyBordersState, + vibrateOnTapState, + soundOnTapState, + hideLettersState, + hideSymbolsState, + keyboardLayoutsState, + themeState, + themeColorState, + ) + }, + ) + val slideSensitivityStr = stringResource( + R.string.slide_sensitivity, + slideSensitivityState + .value + .toInt().toString(), + ) + SettingsSlider( + valueRange = 1f..50f, + state = slideSensitivityState, + icon = { + Icon( + imageVector = Icons.Outlined.SwapHoriz, + contentDescription = slideSensitivityStr, + ) + }, + title = { + Text(slideSensitivityStr) + }, + onValueChangeFinished = { + updateAppSettings( + appSettingsViewModel, + keySizeState, + pushupSizeState, + animationSpeedState, + animationHelperSpeedState, + minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, @@ -798,6 +918,8 @@ private fun updateAppSettings( animationSpeedState: SettingValueState, animationHelperSpeedState: SettingValueState, minSwipeLengthState: SettingValueState, + slideSensitivityState: SettingValueState, + slideEnabledState: SettingValueState, positionState: SettingValueState, autoCapitalizeState: SettingValueState, spacebarMultiTapsState: SettingValueState, @@ -818,6 +940,8 @@ private fun updateAppSettings( animationSpeed = animationSpeedState.value.toInt(), animationHelperSpeed = animationHelperSpeedState.value.toInt(), minSwipeLength = minSwipeLengthState.value.toInt(), + slideSensitivity = slideSensitivityState.value.toInt(), + slideEnabled = slideEnabledState.value.toInt(), position = positionState.value, autoCapitalize = autoCapitalizeState.value.toInt(), spacebarMultiTaps = spacebarMultiTapsState.value.toInt(), @@ -845,6 +969,8 @@ private fun resetAppSettingsToDefault( animationSpeedState: SettingValueState, animationHelperSpeedState: SettingValueState, minSwipeLengthState: SettingValueState, + slideSensitivityState: SettingValueState, + slideEnabledState: SettingValueState, positionState: SettingValueState, autoCapitalizeState: SettingValueState, spacebarMultiTapsState: SettingValueState, @@ -862,6 +988,7 @@ private fun resetAppSettingsToDefault( animationSpeedState.value = DEFAULT_ANIMATION_SPEED.toFloat() animationHelperSpeedState.value = DEFAULT_ANIMATION_HELPER_SPEED.toFloat() minSwipeLengthState.value = DEFAULT_MIN_SWIPE_LENGTH.toFloat() + slideSensitivityState.value = DEFAULT_SLIDE_SENSITIVITY.toFloat() positionState.value = DEFAULT_POSITION autoCapitalizeState.value = DEFAULT_AUTO_CAPITALIZE.toBool() spacebarMultiTapsState.value = DEFAULT_SPACEBAR_MULTITAPS.toBool() @@ -880,6 +1007,8 @@ private fun resetAppSettingsToDefault( animationSpeedState, animationHelperSpeedState, minSwipeLengthState, + slideSensitivityState, + slideEnabledState, positionState, autoCapitalizeState, spacebarMultiTapsState, diff --git a/app/src/main/java/com/dessalines/thumbkey/utils/Types.kt b/app/src/main/java/com/dessalines/thumbkey/utils/Types.kt index f0235e01..ce3a279f 100644 --- a/app/src/main/java/com/dessalines/thumbkey/utils/Types.kt +++ b/app/src/main/java/com/dessalines/thumbkey/utils/Types.kt @@ -18,6 +18,7 @@ data class KeyItemC( val widthMultiplier: Int = 1, val backgroundColor: ColorVariant = ColorVariant.SURFACE, val swipeType: SwipeNWay = SwipeNWay.EIGHT_WAY, + val slideType: SlideType = SlideType.NONE, ) data class KeyC( @@ -172,5 +173,14 @@ enum class KeyboardPosition(private val stringId: Int) { } enum class SwipeNWay { - EIGHT_WAY, FOUR_WAY_CROSS, FOUR_WAY_DIAGONAL, TWO_WAY_VERTICAL, TWO_WAY_HORIZONTAL + EIGHT_WAY, + FOUR_WAY_CROSS, + FOUR_WAY_DIAGONAL, + TWO_WAY_VERTICAL, + TWO_WAY_HORIZONTAL, +} + +enum class SlideType { + NONE, + MOVE_CURSOR, } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 32a2d88d..5c07c524 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,6 +40,8 @@ Minimum Swipe Length: %1$s Animation Speed: %1$s Animation Helper Speed: %1$s + Slide sensitivity: %1$s + Spacebar Sliding Cursor Reset to defaults Are you sure you want to reset settings to defaults? Reset