-
Notifications
You must be signed in to change notification settings - Fork 234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Abbreviations #1208
base: main
Are you sure you want to change the base?
Abbreviations #1208
Changes from 12 commits
e6f365c
0a0297f
57c16ff
e1f2729
80b3422
339eeb2
3f7ae1e
99f1f71
b932e6d
e932171
ac593d5
82c7315
17d51cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package com.dessalines.thumbkey.db | ||
|
||
import androidx.annotation.WorkerThread | ||
import androidx.lifecycle.LiveData | ||
import androidx.lifecycle.ViewModel | ||
import androidx.lifecycle.ViewModelProvider | ||
import androidx.lifecycle.viewModelScope | ||
import kotlinx.coroutines.launch | ||
|
||
class AbbreviationRepository( | ||
private val abbreviationDao: AbbreviationDao, | ||
) { | ||
val allAbbreviations: LiveData<List<Abbreviation>> = abbreviationDao.getAllAbbreviations() | ||
|
||
@WorkerThread | ||
suspend fun insertOrUpdate( | ||
abbr: String, | ||
expansion: String, | ||
id: Int? = null, | ||
) { | ||
if (id != null) { | ||
abbreviationDao.update(id, abbr, expansion) | ||
} else { | ||
abbreviationDao.insert(abbr, expansion) | ||
} | ||
} | ||
|
||
@WorkerThread | ||
suspend fun delete(id: Int) { | ||
abbreviationDao.deleteById(id) | ||
} | ||
|
||
@WorkerThread | ||
fun getAbbreviation(abbr: String): Abbreviation? = abbreviationDao.getAbbreviation(abbr) | ||
} | ||
|
||
class AbbreviationViewModel( | ||
private val repository: AbbreviationRepository, | ||
) : ViewModel() { | ||
val allAbbreviations: LiveData<List<Abbreviation>> = repository.allAbbreviations | ||
|
||
fun insertOrUpdate( | ||
abbr: String, | ||
expansion: String, | ||
id: Int? = null, | ||
) = viewModelScope.launch { | ||
repository.insertOrUpdate(abbr, expansion, id) | ||
} | ||
|
||
fun delete(id: Int) = | ||
viewModelScope.launch { | ||
repository.delete(id) | ||
} | ||
|
||
fun getAbbreviation(abbr: String): Abbreviation? = repository.getAbbreviation(abbr) | ||
} | ||
|
||
class AbbreviationViewModelFactory( | ||
private val repository: AbbreviationRepository, | ||
) : ViewModelProvider.Factory { | ||
override fun <T : ViewModel> create(modelClass: Class<T>): T { | ||
if (modelClass.isAssignableFrom(AbbreviationViewModel::class.java)) { | ||
@Suppress("UNCHECKED_CAST") | ||
return AbbreviationViewModel(repository) as T | ||
} | ||
throw IllegalArgumentException("Unknown ViewModel class") | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -344,6 +344,34 @@ data class BehaviorUpdate( | |
val ghostKeysEnabled: Int, | ||
) | ||
|
||
@Dao | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might as well put all the abbreviation stuff in one file. So rename the |
||
interface AbbreviationDao { | ||
@Query("SELECT * FROM Abbreviation") | ||
fun getAllAbbreviations(): LiveData<List<Abbreviation>> | ||
|
||
@Query("SELECT * FROM Abbreviation WHERE lower(abbreviation) = lower(:abbr) LIMIT 1") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hrm... I'm not sure what the best way to handle case would be. I spose this is fine, but we should decide first whether we should allow upper case abbreviations at all, in which case it'd be better to add a table constraint. |
||
fun getAbbreviation(abbr: String): Abbreviation? | ||
|
||
@Query("SELECT * FROM Abbreviation WHERE lower(abbreviation) = lower(:abbr) LIMIT 1") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This query is duped, so you might as well make the string a const. |
||
suspend fun getAbbreviationAsync(abbr: String): Abbreviation? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer if async was the default, and change EDIT: Actually both of these should be removed. All the abbrevations should only be read once as a list, and passed around as necessary. |
||
|
||
@Query("UPDATE Abbreviation SET abbreviation = :newAbbr, expansion = :expansion WHERE id = :id") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't need this string, read about room updates. It should be enough to do |
||
suspend fun update( | ||
id: Int, | ||
newAbbr: String, | ||
expansion: String, | ||
) | ||
|
||
@Query("INSERT INTO Abbreviation (abbreviation, expansion) VALUES (:abbr, :expansion)") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same with this insert line. Read through an up to date room tutorial. |
||
suspend fun insert( | ||
abbr: String, | ||
expansion: String, | ||
) | ||
|
||
@Query("DELETE FROM Abbreviation WHERE id = :id") | ||
suspend fun deleteById(id: Int) | ||
} | ||
|
||
@Dao | ||
interface AppSettingsDao { | ||
@Query("SELECT * FROM AppSettings limit 1") | ||
|
@@ -582,14 +610,67 @@ val MIGRATION_15_16 = | |
} | ||
} | ||
|
||
val MIGRATION_16_17 = | ||
object : Migration(16, 17) { | ||
override fun migrate(db: SupportSQLiteDatabase) { | ||
db.execSQL( | ||
""" | ||
CREATE TABLE IF NOT EXISTS Abbreviation ( | ||
abbreviation TEXT PRIMARY KEY NOT NULL, | ||
expansion TEXT NOT NULL | ||
Comment on lines
+618
to
+620
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks good. |
||
) | ||
""", | ||
) | ||
} | ||
} | ||
|
||
@Entity | ||
data class Abbreviation( | ||
@PrimaryKey(autoGenerate = true) val id: Int = 0, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This column does not exist on your table. I have no idea how this isn't crashing. |
||
@ColumnInfo(name = "abbreviation") val abbreviation: String, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put the columninfo on a separate line if you would, like the other columns in this codebase. |
||
@ColumnInfo(name = "expansion") val expansion: String, | ||
) | ||
|
||
val MIGRATION_17_18 = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a single PR, so you don't need 2 migrations. Just delete the debug application and start with the table you actually want. Really confused me because you put the entity in between 2 migration definitions. The entity should probably go near the top of the file. |
||
object : Migration(17, 18) { | ||
override fun migrate(db: SupportSQLiteDatabase) { | ||
// Create a temporary table with the new schema | ||
db.execSQL( | ||
""" | ||
CREATE TABLE IF NOT EXISTS Abbreviation_temp ( | ||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, | ||
abbreviation TEXT NOT NULL, | ||
expansion TEXT NOT NULL | ||
Comment on lines
+640
to
+643
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you want to keep an integer id column that's fine I spose, but add a unique index for the abbreviation field. |
||
) | ||
""", | ||
) | ||
|
||
// Copy data from the old table to the new table | ||
db.execSQL( | ||
""" | ||
INSERT INTO Abbreviation_temp (abbreviation, expansion) | ||
SELECT abbreviation, expansion FROM Abbreviation | ||
""", | ||
) | ||
|
||
// Drop the old table | ||
db.execSQL("DROP TABLE Abbreviation") | ||
|
||
// Rename the temporary table to the original name | ||
db.execSQL("ALTER TABLE Abbreviation_temp RENAME TO Abbreviation") | ||
} | ||
} | ||
|
||
@Database( | ||
version = 16, | ||
entities = [AppSettings::class], | ||
version = 18, | ||
entities = [AppSettings::class, Abbreviation::class], | ||
exportSchema = true, | ||
) | ||
abstract class AppDB : RoomDatabase() { | ||
abstract fun appSettingsDao(): AppSettingsDao | ||
|
||
abstract fun abbreviationDao(): AbbreviationDao | ||
|
||
companion object { | ||
@Volatile | ||
private var instance: AppDB? = null | ||
|
@@ -621,7 +702,9 @@ abstract class AppDB : RoomDatabase() { | |
MIGRATION_13_14, | ||
MIGRATION_14_15, | ||
MIGRATION_15_16, | ||
) | ||
MIGRATION_16_17, | ||
MIGRATION_17_18, | ||
).fallbackToDestructiveMigration() | ||
// Necessary because it can't insert data on creation | ||
.addCallback( | ||
object : Callback() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,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.AbbreviationManager | ||
import com.dessalines.thumbkey.utils.CircularDragAction | ||
import com.dessalines.thumbkey.utils.KeyAction | ||
import com.dessalines.thumbkey.utils.KeyboardLayout | ||
|
@@ -72,6 +73,7 @@ import com.dessalines.thumbkey.utils.KeyboardPosition | |
import com.dessalines.thumbkey.utils.TAG | ||
import com.dessalines.thumbkey.utils.getKeyboardMode | ||
import com.dessalines.thumbkey.utils.keyboardPositionToAlignment | ||
import com.dessalines.thumbkey.utils.performKeyAction | ||
import com.dessalines.thumbkey.utils.toBool | ||
import kotlin.time.TimeMark | ||
|
||
|
@@ -82,14 +84,15 @@ fun KeyboardScreen( | |
onChangePosition: ((old: KeyboardPosition) -> KeyboardPosition) -> Unit, | ||
) { | ||
val ctx = LocalContext.current as IMEService | ||
val abbreviationManager = remember { AbbreviationManager(ctx.applicationContext) } | ||
|
||
var mode by remember { | ||
val startMode = | ||
getKeyboardMode( | ||
ime = ctx, | ||
autoCapitalize = settings?.autoCapitalize?.toBool() ?: false, | ||
) | ||
|
||
Log.d(TAG, "KeyboardScreen: Initial keyboard mode: $startMode") | ||
mutableStateOf(startMode) | ||
} | ||
|
||
|
@@ -226,9 +229,44 @@ fun KeyboardScreen( | |
if (soundOnTap) { | ||
audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK, .1f) | ||
} | ||
ctx.currentInputConnection.commitText( | ||
it.emoji, | ||
1, | ||
performKeyAction( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is going on here, this has nothing to do with abbreviations. |
||
action = KeyAction.CommitText(it.emoji), | ||
ime = ctx, | ||
autoCapitalize = autoCapitalize, | ||
keyboardSettings = keyboardDefinition.settings, | ||
onToggleShiftMode = { enable -> | ||
mode = | ||
if (enable) { | ||
KeyboardMode.SHIFTED | ||
} else { | ||
capsLock = false | ||
KeyboardMode.MAIN | ||
} | ||
}, | ||
onToggleNumericMode = { enable -> | ||
mode = | ||
if (enable) { | ||
KeyboardMode.NUMERIC | ||
} else { | ||
capsLock = false | ||
KeyboardMode.MAIN | ||
} | ||
}, | ||
onToggleEmojiMode = { enable -> | ||
mode = if (enable) KeyboardMode.EMOJI else KeyboardMode.MAIN | ||
}, | ||
onToggleCapsLock = { capsLock = !capsLock }, | ||
onAutoCapitalize = { enable -> | ||
if (mode !== KeyboardMode.NUMERIC) { | ||
if (enable) { | ||
mode = KeyboardMode.SHIFTED | ||
} else if (!capsLock) { | ||
mode = KeyboardMode.MAIN | ||
} | ||
} | ||
}, | ||
onSwitchLanguage = onSwitchLanguage, | ||
onChangePosition = onChangePosition, | ||
) | ||
} | ||
emojiPicker | ||
|
@@ -273,30 +311,36 @@ fun KeyboardScreen( | |
slideSpacebarDeadzoneEnabled = slideSpacebarDeadzoneEnabled, | ||
slideBackspaceDeadzoneEnabled = slideBackspaceDeadzoneEnabled, | ||
onToggleShiftMode = { enable -> | ||
Log.d(TAG, "KeyboardScreen: Toggling shift mode, enable: $enable") | ||
mode = | ||
if (enable) { | ||
KeyboardMode.SHIFTED | ||
} else { | ||
capsLock = false | ||
KeyboardMode.MAIN | ||
} | ||
Log.d(TAG, "KeyboardScreen: New keyboard mode: $mode") | ||
}, | ||
onToggleNumericMode = { enable -> | ||
Log.d(TAG, "KeyboardScreen: Toggling numeric mode, enable: $enable") | ||
mode = | ||
if (enable) { | ||
KeyboardMode.NUMERIC | ||
} else { | ||
capsLock = false | ||
KeyboardMode.MAIN | ||
} | ||
Log.d(TAG, "KeyboardScreen: New keyboard mode: $mode") | ||
}, | ||
onToggleEmojiMode = { enable -> | ||
Log.d(TAG, "KeyboardScreen: Toggling emoji mode, enable: $enable") | ||
mode = | ||
if (enable) { | ||
KeyboardMode.EMOJI | ||
} else { | ||
KeyboardMode.MAIN | ||
} | ||
Log.d(TAG, "KeyboardScreen: New keyboard mode: $mode") | ||
}, | ||
onToggleCapsLock = { | ||
capsLock = !capsLock | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a search, and room now has an
@Upsert
: https://developer.android.com/reference/kotlin/androidx/room/Upsert?hl=en