Skip to content

Commit

Permalink
Implement Drag-and-return & Circular drag (dessalines#854)
Browse files Browse the repository at this point in the history
* Implement Drag-and-return & Circular drag

* Fix translation strings of `(counter)clockwise_drag_action`

* Fix translation strings of `send_opposite_case` & `send_numeric`

* Rename `{secondary,tertiary}Key` `{oppositeCase,numeric}Key` and simplify passing them to `KeyboardKey`
  • Loading branch information
feathecutie committed Apr 24, 2024
1 parent 622e523 commit 0ab4b98
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 7 deletions.
54 changes: 53 additions & 1 deletion app/src/main/java/com/dessalines/thumbkey/db/AppDb.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ const val DEFAULT_BACKDROP_ENABLED = 0
const val DEFAULT_KEY_PADDING = 0
const val DEFAULT_KEY_BORDER_WIDTH = 1
const val DEFAULT_KEY_RADIUS = 0
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

@Entity
data class AppSettings(
Expand Down Expand Up @@ -202,6 +206,26 @@ data class AppSettings(
defaultValue = DEFAULT_KEY_RADIUS.toString(),
)
val keyRadius: Int,
@ColumnInfo(
name = "drag_return_enabled",
defaultValue = DEFAULT_DRAG_RETURN_ENABLED.toString(),
)
val dragReturnEnabled: Int,
@ColumnInfo(
name = "circular_drag_enabled",
defaultValue = DEFAULT_CIRCULAR_DRAG_ENABLED.toString(),
)
val circularDragEnabled: Int,
@ColumnInfo(
name = "clockwise_drag_action",
defaultValue = DEFAULT_CLOCKWISE_DRAG_ACTION.toString(),
)
val clockwiseDragAction: Int,
@ColumnInfo(
name = "counterclockwise_drag_action",
defaultValue = DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION.toString(),
)
val counterclockwiseDragAction: Int,
)

data class LayoutsUpdate(
Expand Down Expand Up @@ -302,6 +326,14 @@ data class BehaviorUpdate(
val autoCapitalize: Int,
@ColumnInfo(name = "spacebar_multitaps")
val spacebarMultiTaps: Int,
@ColumnInfo(name = "drag_return_enabled")
val dragReturnEnabled: Int,
@ColumnInfo(name = "circular_drag_enabled")
val circularDragEnabled: Int,
@ColumnInfo(name = "clockwise_drag_action")
val clockwiseDragAction: Int,
@ColumnInfo(name = "counterclockwise_drag_action")
val counterclockwiseDragAction: Int,
)

@Dao
Expand Down Expand Up @@ -508,8 +540,27 @@ val MIGRATION_13_14 =
}
}

val MIGRATION_14_15 =
object : Migration(14, 15) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"alter table AppSettings add column drag_return_enabled INTEGER NOT NULL default $DEFAULT_DRAG_RETURN_ENABLED",
)
db.execSQL(
"alter table AppSettings add column circular_drag_enabled INTEGER NOT NULL default $DEFAULT_CIRCULAR_DRAG_ENABLED",
)
db.execSQL(
"alter table AppSettings add column clockwise_drag_action INTEGER NOT NULL default $DEFAULT_CLOCKWISE_DRAG_ACTION",
)
db.execSQL(
"alter table AppSettings add column counterclockwise_drag_action INTEGER NOT NULL " +
"default $DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION",
)
}
}

@Database(
version = 14,
version = 15,
entities = [AppSettings::class],
exportSchema = true,
)
Expand Down Expand Up @@ -545,6 +596,7 @@ abstract class AppDB : RoomDatabase() {
MIGRATION_11_12,
MIGRATION_12_13,
MIGRATION_13_14,
MIGRATION_14_15,
)
// Necessary because it can't insert data on creation
.addCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.pointer.pointerInput
Expand All @@ -48,6 +49,8 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.lerp
import com.dessalines.thumbkey.IMEService
import com.dessalines.thumbkey.utils.CircularDirection
import com.dessalines.thumbkey.utils.CircularDragAction
import com.dessalines.thumbkey.utils.FontSizeVariant
import com.dessalines.thumbkey.utils.KeyAction
import com.dessalines.thumbkey.utils.KeyC
Expand All @@ -58,6 +61,7 @@ import com.dessalines.thumbkey.utils.Selection
import com.dessalines.thumbkey.utils.SlideType
import com.dessalines.thumbkey.utils.SwipeDirection
import com.dessalines.thumbkey.utils.buildTapActions
import com.dessalines.thumbkey.utils.circularDirection
import com.dessalines.thumbkey.utils.colorVariantToColor
import com.dessalines.thumbkey.utils.doneKeyAction
import com.dessalines.thumbkey.utils.fontSizeVariantToFontSize
Expand Down Expand Up @@ -106,13 +110,20 @@ fun KeyboardKey(
onAutoCapitalize: (enable: Boolean) -> Unit,
onSwitchLanguage: () -> Unit,
onSwitchPosition: () -> Unit,
oppositeCaseKey: KeyItemC? = null,
numericKey: KeyItemC? = null,
dragReturnEnabled: Boolean,
circularDragEnabled: Boolean,
clockwiseDragAction: CircularDragAction,
counterclockwiseDragAction: CircularDragAction,
) {
// Necessary for swipe settings to get updated correctly
val id =
key.toString() + animationHelperSpeed + animationSpeed + autoCapitalize +
vibrateOnTap + soundOnTap + legendHeight + legendWidth + minSwipeLength + slideSensitivity +
slideEnabled + slideCursorMovementMode + slideSpacebarDeadzoneEnabled +
slideBackspaceDeadzoneEnabled
slideBackspaceDeadzoneEnabled + dragReturnEnabled + circularDragEnabled +
clockwiseDragAction.ordinal + counterclockwiseDragAction.ordinal

val ctx = LocalContext.current
val ime = ctx as IMEService
Expand All @@ -136,6 +147,8 @@ fun KeyboardKey(
var offsetY by remember { mutableFloatStateOf(0f) }
var hasSlideMoveCursorTriggered by remember { mutableStateOf(false) }
var timeOfLastAccelerationInput by remember { mutableLongStateOf(0L) }
var positions by remember { mutableStateOf(listOf<Offset>()) }
var maxOffset by remember { mutableStateOf(Offset(0f, 0f)) }

var selection by remember { mutableStateOf(Selection()) }

Expand Down Expand Up @@ -249,6 +262,9 @@ fun KeyboardKey(
val (x, y) = dragAmount
offsetX += x
offsetY += y
val offset = Offset(offsetX, offsetY)
positions += offset
if (offset.getDistanceSquared() > maxOffset.getDistanceSquared()) maxOffset = offset

// First detection is large enough to preserve swipe actions.
val slideOffsetTrigger = (keySize.dp.toPx() * 0.75) + minSwipeLength
Expand Down Expand Up @@ -400,15 +416,52 @@ fun KeyboardKey(
},
onDragEnd = {
lateinit var action: KeyAction

if (key.slideType == SlideType.NONE ||
!slideEnabled ||
((key.slideType == SlideType.DELETE) && !selection.active) ||
((key.slideType == SlideType.MOVE_CURSOR) && !hasSlideMoveCursorTriggered)
) {
hasSlideMoveCursorTriggered = false
val swipeDirection =
swipeDirection(offsetX, offsetY, minSwipeLength, key.swipeType)
action = key.swipes?.get(swipeDirection)?.action ?: key.center.action

val finalOffset = positions.last()
val maxOffsetBigEnough = maxOffset.getDistanceSquared() >= (finalOffset * 2f).getDistanceSquared()
val finalOffsetSmallEnough = finalOffset.getDistance() <= keySize.dp.toPx() / 2
action =
(
if (maxOffsetBigEnough && finalOffsetSmallEnough) {
(
if (circularDragEnabled) {
val circularDragActions =
mapOf(
CircularDragAction.OppositeCase to oppositeCaseKey?.center?.action,
CircularDragAction.Numeric to numericKey?.center?.action,
)
circularDirection(positions, keySize)?.let {
when (it) {
CircularDirection.Clockwise -> circularDragActions[clockwiseDragAction]
CircularDirection.Counterclockwise ->
circularDragActions[counterclockwiseDragAction]
}
}
} else {
null
}
) ?: (
if (dragReturnEnabled) {
val swipeDirection =
swipeDirection(maxOffset.x, maxOffset.y, minSwipeLength, key.swipeType)
oppositeCaseKey?.swipes?.get(swipeDirection)?.action
} else {
null
}
)
} else {
val swipeDirection =
swipeDirection(offsetX, offsetY, minSwipeLength, key.swipeType)
key.swipes?.get(swipeDirection)?.action
}
) ?: key.center.action

performKeyAction(
action = action,
Expand Down Expand Up @@ -490,6 +543,8 @@ fun KeyboardKey(
// Reset the drags
offsetX = 0f
offsetY = 0f
maxOffset = Offset(0f, 0f)
positions = listOf()

// Reset selection
selection = Selection()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ import com.dessalines.thumbkey.db.DEFAULT_ANIMATION_HELPER_SPEED
import com.dessalines.thumbkey.db.DEFAULT_ANIMATION_SPEED
import com.dessalines.thumbkey.db.DEFAULT_AUTO_CAPITALIZE
import com.dessalines.thumbkey.db.DEFAULT_BACKDROP_ENABLED
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_HIDE_LETTERS
import com.dessalines.thumbkey.db.DEFAULT_HIDE_SYMBOLS
import com.dessalines.thumbkey.db.DEFAULT_KEYBOARD_LAYOUT
Expand All @@ -58,6 +62,7 @@ import com.dessalines.thumbkey.keyboards.EMOJI_BACK_KEY_ITEM
import com.dessalines.thumbkey.keyboards.KB_EN_THUMBKEY_MAIN
import com.dessalines.thumbkey.keyboards.NUMERIC_KEY_ITEM
import com.dessalines.thumbkey.keyboards.RETURN_KEY_ITEM
import com.dessalines.thumbkey.utils.CircularDragAction
import com.dessalines.thumbkey.utils.KeyAction
import com.dessalines.thumbkey.utils.KeyboardLayout
import com.dessalines.thumbkey.utils.KeyboardMode
Expand Down Expand Up @@ -133,6 +138,11 @@ fun KeyboardScreen(
val legendHeight = settings?.keySize ?: DEFAULT_KEY_SIZE
val legendWidth = settings?.keyWidth ?: legendHeight
val keyRadius = settings?.keyRadius ?: DEFAULT_KEY_RADIUS
val dragReturnEnabled = (settings?.dragReturnEnabled ?: DEFAULT_DRAG_RETURN_ENABLED).toBool()
val circularDragEnabled = (settings?.circularDragEnabled ?: DEFAULT_CIRCULAR_DRAG_ENABLED).toBool()
val clockwiseDragAction = CircularDragAction.entries[settings?.clockwiseDragAction ?: DEFAULT_CLOCKWISE_DRAG_ACTION]
val counterclockwiseDragAction =
CircularDragAction.entries[settings?.counterclockwiseDragAction ?: DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION]

val keyBorderWidthFloat = keyBorderWidth / 10.0f
val keyBorderColour = MaterialTheme.colorScheme.outline
Expand Down Expand Up @@ -299,6 +309,10 @@ fun KeyboardScreen(
},
onSwitchLanguage = onSwitchLanguage,
onSwitchPosition = onSwitchPosition,
dragReturnEnabled = dragReturnEnabled,
circularDragEnabled = circularDragEnabled,
clockwiseDragAction = clockwiseDragAction,
counterclockwiseDragAction = counterclockwiseDragAction,
)
}
}
Expand Down Expand Up @@ -345,9 +359,9 @@ fun KeyboardScreen(
},
),
) {
keyboard.arr.forEach { row ->
keyboard.arr.forEachIndexed { i, row ->
Row {
row.forEach { key ->
row.forEachIndexed { j, key ->
Column {
KeyboardKey(
key = key,
Expand Down Expand Up @@ -419,6 +433,21 @@ fun KeyboardScreen(
},
onSwitchLanguage = onSwitchLanguage,
onSwitchPosition = onSwitchPosition,
oppositeCaseKey =
when (mode) {
KeyboardMode.MAIN -> keyboardDefinition.modes.shifted
KeyboardMode.SHIFTED -> keyboardDefinition.modes.main
else -> null
}?.arr?.get(i)?.get(j),
numericKey =
when (mode) {
KeyboardMode.MAIN, KeyboardMode.SHIFTED -> keyboardDefinition.modes.numeric.arr[i][j]
else -> null
},
dragReturnEnabled = dragReturnEnabled,
circularDragEnabled = circularDragEnabled,
clockwiseDragAction = clockwiseDragAction,
counterclockwiseDragAction = counterclockwiseDragAction,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ import com.dessalines.thumbkey.db.DEFAULT_ANIMATION_HELPER_SPEED
import com.dessalines.thumbkey.db.DEFAULT_ANIMATION_SPEED
import com.dessalines.thumbkey.db.DEFAULT_AUTO_CAPITALIZE
import com.dessalines.thumbkey.db.DEFAULT_BACKDROP_ENABLED
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_HIDE_LETTERS
import com.dessalines.thumbkey.db.DEFAULT_HIDE_SYMBOLS
import com.dessalines.thumbkey.db.DEFAULT_KEYBOARD_LAYOUT
Expand Down Expand Up @@ -298,6 +302,10 @@ private fun resetAppSettingsToDefault(appSettingsViewModel: AppSettingsViewModel
keyPadding = DEFAULT_KEY_PADDING,
keyBorderWidth = DEFAULT_KEY_BORDER_WIDTH,
keyRadius = DEFAULT_KEY_RADIUS,
dragReturnEnabled = DEFAULT_DRAG_RETURN_ENABLED,
circularDragEnabled = DEFAULT_CIRCULAR_DRAG_ENABLED,
clockwiseDragAction = DEFAULT_CLOCKWISE_DRAG_ACTION,
counterclockwiseDragAction = DEFAULT_COUNTERCLOCKWISE_DRAG_ACTION,
),
)
}
Expand Down
Loading

0 comments on commit 0ab4b98

Please sign in to comment.