Move database module from :core to :data

This commit is contained in:
MM20
2023-10-28 14:20:36 +02:00
parent 957358c79a
commit e3005cf051
83 changed files with 24 additions and 24 deletions
@@ -0,0 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
/
</manifest>
@@ -0,0 +1,156 @@
@file:Suppress("ClassName")
package de.mm20.launcher2.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import androidx.sqlite.db.SupportSQLiteDatabase
import de.mm20.launcher2.database.daos.ThemeDao
import de.mm20.launcher2.database.entities.CurrencyEntity
import de.mm20.launcher2.database.entities.CustomAttributeEntity
import de.mm20.launcher2.database.entities.ForecastEntity
import de.mm20.launcher2.database.entities.IconEntity
import de.mm20.launcher2.database.entities.IconPackEntity
import de.mm20.launcher2.database.entities.SavedSearchableEntity
import de.mm20.launcher2.database.entities.SearchActionEntity
import de.mm20.launcher2.database.entities.ThemeEntity
import de.mm20.launcher2.database.entities.WidgetEntity
import de.mm20.launcher2.database.migrations.Migration_10_11
import de.mm20.launcher2.database.migrations.Migration_11_12
import de.mm20.launcher2.database.migrations.Migration_12_13
import de.mm20.launcher2.database.migrations.Migration_13_14
import de.mm20.launcher2.database.migrations.Migration_14_15
import de.mm20.launcher2.database.migrations.Migration_15_16
import de.mm20.launcher2.database.migrations.Migration_16_17
import de.mm20.launcher2.database.migrations.Migration_17_18
import de.mm20.launcher2.database.migrations.Migration_18_19
import de.mm20.launcher2.database.migrations.Migration_19_20
import de.mm20.launcher2.database.migrations.Migration_20_21
import de.mm20.launcher2.database.migrations.Migration_21_22
import de.mm20.launcher2.database.migrations.Migration_22_23
import de.mm20.launcher2.database.migrations.Migration_23_24
import de.mm20.launcher2.database.migrations.Migration_24_25
import de.mm20.launcher2.database.migrations.Migration_6_7
import de.mm20.launcher2.database.migrations.Migration_7_8
import de.mm20.launcher2.database.migrations.Migration_8_9
import de.mm20.launcher2.database.migrations.Migration_9_10
import de.mm20.launcher2.ktx.toBytes
import java.util.UUID
@Database(
entities = [
ForecastEntity::class,
SavedSearchableEntity::class,
CurrencyEntity::class,
IconEntity::class,
IconPackEntity::class,
WidgetEntity::class,
CustomAttributeEntity::class,
SearchActionEntity::class,
ThemeEntity::class,
], version = 25, exportSchema = true
)
@TypeConverters(ComponentNameConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun weatherDao(): WeatherDao
abstract fun iconDao(): IconDao
abstract fun searchableDao(): SearchableDao
abstract fun widgetDao(): WidgetDao
abstract fun currencyDao(): CurrencyDao
abstract fun backupDao(): BackupRestoreDao
abstract fun customAttrsDao(): CustomAttrsDao
abstract fun searchActionDao(): SearchActionDao
abstract fun themeDao(): ThemeDao
companion object {
private var _instance: AppDatabase? = null
fun getInstance(context: Context): AppDatabase {
val instance = _instance
?: Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "room")
//.fallbackToDestructiveMigration()
.addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
db.execSQL(
"INSERT INTO `SearchAction` (`position`, `type`) VALUES" +
"(0, 'call')," +
"(1, 'message')," +
"(2, 'email')," +
"(3, 'contact')," +
"(4, 'alarm')," +
"(5, 'timer')," +
"(6, 'calendar')," +
"(7, 'website')," +
"(8, 'websearch')"
)
db.execSQL(
"INSERT INTO `SearchAction` (`position`, `type`, `data`, `label`, `color`, `icon`, `customIcon`, `options`) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?)",
arrayOf(
9,
"url",
context.getString(R.string.default_websearch_2_url),
context.getString(R.string.default_websearch_2_name),
0,
0,
null,
null,
10,
"url",
context.getString(R.string.default_websearch_3_url),
context.getString(R.string.default_websearch_3_name),
0,
0,
null,
null,
)
)
db.execSQL(
"INSERT INTO Widget (`type`, `position`, `id`) VALUES " +
"('weather', 0, ?)," +
"('music', 1, ?)," +
"('calendar', 2, ?);",
arrayOf(
UUID.randomUUID().toBytes(),
UUID.randomUUID().toBytes(),
UUID.randomUUID().toBytes()
)
)
}
})
.addMigrations(
Migration_6_7(),
Migration_7_8(),
Migration_8_9(),
Migration_9_10(),
Migration_10_11(),
Migration_11_12(),
Migration_12_13(),
Migration_13_14(),
Migration_14_15(),
Migration_15_16(),
Migration_16_17(),
Migration_17_18(),
Migration_18_19(),
Migration_19_20(),
Migration_20_21(),
Migration_21_22(),
Migration_22_23(),
Migration_23_24(),
Migration_24_25(context),
).build()
if (_instance == null) _instance = instance
return instance
}
}
}
@@ -0,0 +1,54 @@
package de.mm20.launcher2.database
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import de.mm20.launcher2.database.entities.CustomAttributeEntity
import de.mm20.launcher2.database.entities.SavedSearchableEntity
import de.mm20.launcher2.database.entities.SearchActionEntity
import de.mm20.launcher2.database.entities.WebsearchEntity
import de.mm20.launcher2.database.entities.WidgetEntity
@Dao
interface BackupRestoreDao {
@Query("DELETE FROM Searchable")
suspend fun wipeFavorites()
@Query("SELECT * FROM Searchable LIMIT :limit OFFSET :offset")
suspend fun exportFavorites(limit: Int, offset: Int): List<SavedSearchableEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun importFavorites(items: List<SavedSearchableEntity>)
@Query("DELETE FROM Widget")
suspend fun wipeWidgets()
@Query("SELECT * FROM Widget LIMIT :limit OFFSET :offset")
suspend fun exportWidgets(limit: Int, offset: Int): List<WidgetEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun importWidgets(items: List<WidgetEntity>)
@Query("DELETE FROM SearchAction")
suspend fun wipeSearchActions()
@Query("SELECT * FROM SearchAction LIMIT :limit OFFSET :offset")
suspend fun exportSearchActions(limit: Int, offset: Int): List<SearchActionEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun importSearchActions(items: List<SearchActionEntity>)
@Query("DELETE FROM CustomAttributes")
suspend fun wipeCustomAttributes()
@Query("SELECT * FROM CustomAttributes LIMIT :limit OFFSET :offset")
suspend fun exportCustomAttributes(limit: Int, offset: Int): List<CustomAttributeEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun importCustomAttributes(items: List<CustomAttributeEntity>)
@Query("DELETE FROM CustomAttributes WHERE (type = 'tag' OR type = 'label') AND NOT EXISTS(SELECT 1 FROM Searchable WHERE CustomAttributes.key = Searchable.key)")
suspend fun cleanUp(): Int
}
@@ -0,0 +1,34 @@
package de.mm20.launcher2.database
import android.content.ComponentName
import androidx.room.TypeConverter
import org.json.JSONArray
class ComponentNameConverter {
@TypeConverter
fun toString(componentName: ComponentName?): String? {
return componentName?.flattenToString()
}
@TypeConverter
fun toComponentName(string: String?) : ComponentName? {
string ?: return null
return ComponentName.unflattenFromString(string)
}
}
class StringListConverter {
@TypeConverter
fun toString(list: List<String>): String {
val json = JSONArray()
list.forEach { json.put(it) }
return json.toString()
}
@TypeConverter
fun toStringList(string: String): List<String> {
val json = JSONArray(string)
return (0..json.length()).map { json.getString(it) }
}
}
@@ -0,0 +1,34 @@
package de.mm20.launcher2.database
import androidx.room.*
import de.mm20.launcher2.database.entities.CurrencyEntity
@Dao
interface CurrencyDao {
@Query("SELECT value FROM Currency WHERE symbol = :symbol")
fun getExchangeRate(symbol: String) : Double?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(currency: CurrencyEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(currencies: List<CurrencyEntity>)
@Query("SELECT * FROM Currency WHERE symbol = :symbol")
fun getCurrency(symbol: String) : CurrencyEntity?
@Query("SELECT * FROM Currency WHERE symbol IN (:symbols)")
fun getCurrencies(symbols: List<String>) : List<CurrencyEntity>
@Query("SELECT * FROM Currency")
fun getAllCurrencies() : List<CurrencyEntity>
@Transaction
fun exists(symbol: String): Boolean {
return getCurrency(symbol) != null
}
@Query("SELECT lastUpdate FROM Currency WHERE symbol = :symbol")
fun getLastUpdate(symbol: String) : Long
}
@@ -0,0 +1,69 @@
package de.mm20.launcher2.database
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import de.mm20.launcher2.database.entities.CustomAttributeEntity
import kotlinx.coroutines.flow.Flow
@Dao
interface CustomAttrsDao {
@Query("SELECT * FROM CustomAttributes WHERE type = :type AND `key` = :key LIMIT 1")
fun getCustomAttribute(key: String, type: String) : Flow<CustomAttributeEntity?>
@Query("DELETE FROM CustomAttributes WHERE type = :type AND `key` = :key")
fun clearCustomAttribute(key: String, type: String)
@Insert
fun setCustomAttribute(entity: CustomAttributeEntity)
@Insert
suspend fun insertCustomAttributes(entities: List<CustomAttributeEntity>)
@Query("SELECT * FROM CustomAttributes WHERE type = :type AND `key` IN (:keys)")
fun getCustomAttributes(keys: List<String>, type: String) : Flow<List<CustomAttributeEntity>>
@Query("SELECT DISTINCT `key` FROM CustomAttributes WHERE (type = 'label' OR type = 'tag') AND value LIKE :query")
fun search(query: String): Flow<List<String>>
@Transaction
suspend fun setTags(key: String, tags: List<CustomAttributeEntity>) {
clearCustomAttribute(key, "tag")
insertCustomAttributes(tags)
}
@Query("SELECT DISTINCT value FROM CustomAttributes WHERE type = 'tag' AND value LIKE :like ORDER BY value")
fun getAllTagsLike(like: String): Flow<List<String>>
@Query("SELECT DISTINCT value FROM CustomAttributes WHERE type = 'tag' ORDER BY value")
fun getAllTags(): Flow<List<String>>
@Query("SELECT `key` FROM CustomAttributes WHERE type = 'tag' AND value = :tag")
fun getItemsWithTag(tag: String): Flow<List<String>>
@Transaction
suspend fun setItemsWithTag(tag: String, items: List<String>) {
deleteTag(tag)
insertCustomAttributes(items.map { CustomAttributeEntity(it, "tag", tag) })
}
@Transaction
suspend fun addTag(key: String, tag: String) {
removeTag(key, tag)
insertTag(key, tag)
}
@Query("DELETE FROM CustomAttributes WHERE type = 'tag' AND `key` = :key AND value = :tag")
suspend fun removeTag(key: String, tag: String)
@Query("INSERT INTO CustomAttributes (`key`, value, type) VALUES (:key, :tag, 'tag')")
suspend fun insertTag(key: String, tag: String)
@Query("UPDATE CustomAttributes SET value = :newName WHERE value = :oldName AND type = 'tag'")
suspend fun renameTag(oldName: String, newName: String)
@Query("DELETE FROM CustomAttributes WHERE type = 'tag' AND value = :tag")
suspend fun deleteTag(tag: String)
}
@@ -0,0 +1,60 @@
package de.mm20.launcher2.database
import androidx.room.*
import de.mm20.launcher2.database.entities.IconEntity
import de.mm20.launcher2.database.entities.IconPackEntity
import kotlinx.coroutines.flow.Flow
@Dao
interface IconDao {
@Insert
suspend fun insertAll(icons: List<IconEntity>)
@Query("SELECT * FROM Icons WHERE packageName = :packageName AND (activityName = :activityName OR activityName IS NULL) AND iconPack = :iconPack AND type IN ('app', 'calendar', 'clock') ORDER BY type DESC LIMIT 1")
suspend fun getIcon(packageName: String, activityName: String?, iconPack: String): IconEntity?
@Query("SELECT * FROM Icons WHERE packageName = :packageName AND (activityName = :activityName OR activityName IS NULL) AND type IN ('app', 'calendar', 'clock')")
suspend fun getIconsFromAllPacks(packageName: String, activityName: String): List<IconEntity>
@Query("SELECT * FROM Icons WHERE type IN ('app', 'calendar', 'clock') AND (drawable LIKE :drawableQuery OR packageName LIKE :componentQuery OR activityName LIKE :componentQuery OR name LIKE :nameQuery) AND (:iconPack IS NULL OR iconPack = :iconPack) GROUP BY drawable ORDER BY iconPack, drawable LIMIT :limit")
suspend fun searchIconPackIcons(
componentQuery: String,
nameQuery: String,
drawableQuery: String,
iconPack: String?,
limit: Int = 100
): List<IconEntity>
@Query("DELETE FROM Icons WHERE iconPack = :iconPack")
fun deleteIcons(iconPack: String)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun installIconPack(iconPack: IconPackEntity)
@Query("SELECT * FROM IconPack ORDER BY name ASC")
fun getInstalledIconPacks(): Flow<List<IconPackEntity>>
@Query("SELECT * FROM IconPack WHERE packageName = :packageName LIMIT 1")
suspend fun getIconPack(packageName: String): IconPackEntity?
@Delete
fun deleteIconPack(iconPack: IconPackEntity)
@Query("SELECT drawable FROM Icons WHERE iconPack = :pack AND type = 'iconback'")
suspend fun getIconBacks(pack: String): List<String>
@Query("SELECT drawable FROM Icons WHERE iconPack = :pack AND type = 'iconupon'")
suspend fun getIconUpons(pack: String): List<String>
@Query("SELECT drawable FROM Icons WHERE iconPack = :pack AND type = 'iconmask'")
suspend fun getIconMasks(pack: String): List<String>
@Query("SELECT scale FROM IconPack WHERE packageName = :pack")
suspend fun getScale(pack: String): Float?
@Query("DELETE FROM Icons WHERE iconPack NOT IN (:keep)")
suspend fun deleteIconsNotIn(keep: List<String>)
@Query("DELETE FROM IconPack WHERE packageName NOT IN (:keep)")
suspend fun deleteIconPacksNotIn(keep: List<String>)
}
@@ -0,0 +1,6 @@
package de.mm20.launcher2.database
import org.koin.dsl.module
val databaseModule = module {
single { AppDatabase.getInstance(get()) }
}
@@ -0,0 +1,26 @@
package de.mm20.launcher2.database
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import de.mm20.launcher2.database.entities.SearchActionEntity
import kotlinx.coroutines.flow.Flow
@Dao
interface SearchActionDao {
@Query("SELECT * FROM SearchAction ORDER BY position ASC")
fun getSearchActions(): Flow<List<SearchActionEntity>>
@Transaction
suspend fun replaceAll(actions: List<SearchActionEntity>) {
deleteAll()
insertAll(actions)
}
@Query("DELETE FROM `SearchAction`")
suspend fun deleteAll()
@Insert
suspend fun insertAll(actions: List<SearchActionEntity>)
}
@@ -0,0 +1,178 @@
package de.mm20.launcher2.database
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import androidx.room.Upsert
import de.mm20.launcher2.database.entities.SavedSearchableEntity
import de.mm20.launcher2.database.entities.SavedSearchableUpdatePinEntity
import kotlinx.coroutines.flow.Flow
@Dao
interface SearchableDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(searchable: SavedSearchableEntity)
@Upsert(entity = SavedSearchableEntity::class)
suspend fun upsert(searchable: SavedSearchableEntity)
@Upsert(entity = SavedSearchableEntity::class)
suspend fun upsert(searchable: List<SavedSearchableUpdatePinEntity>)
@Update(entity = SavedSearchableEntity::class)
suspend fun update(searchable: SavedSearchableUpdatePinEntity)
@Query(
"SELECT * FROM Searchable " +
"WHERE (" +
"(:manuallySorted AND pinPosition > 1) OR " +
"(:automaticallySorted AND pinPosition = 1) OR" +
"(:frequentlyUsed AND pinPosition = 0 AND launchCount > 0) OR " +
"(:hidden AND hidden = 1)" +
") AND hidden = :hidden ORDER BY pinPosition DESC, weight DESC, launchCount DESC LIMIT :limit"
)
fun get(
manuallySorted: Boolean = false,
automaticallySorted: Boolean = false,
frequentlyUsed: Boolean = false,
hidden: Boolean = false,
limit: Int,
): Flow<List<SavedSearchableEntity>>
@Query(
"SELECT * FROM Searchable " +
"WHERE (`type` IN (:includeTypes)) AND " +
"(" +
"(:manuallySorted AND pinPosition > 1) OR " +
"(:automaticallySorted AND pinPosition = 1) OR" +
"(:frequentlyUsed AND pinPosition = 0 AND launchCount > 0) OR " +
"(:hidden AND hidden = 1)" +
") AND hidden = :hidden ORDER BY pinPosition DESC, weight DESC, launchCount DESC LIMIT :limit"
)
fun getIncludeTypes(
includeTypes: List<String>?,
manuallySorted: Boolean = false,
automaticallySorted: Boolean = false,
frequentlyUsed: Boolean = false,
hidden: Boolean = false,
limit: Int,
): Flow<List<SavedSearchableEntity>>
@Query(
"SELECT * FROM Searchable " +
"WHERE (`type` NOT IN (:excludeTypes)) AND " +
"(" +
"(:manuallySorted AND pinPosition > 1) OR " +
"(:automaticallySorted AND pinPosition = 1) OR" +
"(:frequentlyUsed AND pinPosition = 0 AND launchCount > 0) OR " +
"(:hidden AND hidden = 1)" +
") AND hidden = :hidden ORDER BY pinPosition DESC, weight DESC, launchCount DESC LIMIT :limit"
)
fun getExcludeTypes(
excludeTypes: List<String>?,
manuallySorted: Boolean = false,
automaticallySorted: Boolean = false,
frequentlyUsed: Boolean = false,
hidden: Boolean = false,
limit: Int,
): Flow<List<SavedSearchableEntity>>
@Query(
"SELECT `key` FROM Searchable " +
"WHERE (" +
"(:manuallySorted AND pinPosition > 1) OR " +
"(:automaticallySorted AND pinPosition = 1) OR" +
"(:frequentlyUsed AND pinPosition = 0 AND launchCount > 0) OR " +
"(:hidden AND hidden = 1)" +
") AND hidden = :hidden ORDER BY pinPosition DESC, weight DESC, launchCount DESC LIMIT :limit"
)
fun getKeys(
manuallySorted: Boolean = false,
automaticallySorted: Boolean = false,
frequentlyUsed: Boolean = false,
hidden: Boolean = false,
limit: Int,
): Flow<List<String>>
@Query(
"SELECT `key` FROM Searchable " +
"WHERE (`type` IN (:includeTypes)) AND " +
"(" +
"(:manuallySorted AND pinPosition > 1) OR " +
"(:automaticallySorted AND pinPosition = 1) OR" +
"(:frequentlyUsed AND pinPosition = 0 AND launchCount > 0) OR " +
"(:hidden AND hidden = 1)" +
") AND hidden = :hidden ORDER BY pinPosition DESC, weight DESC, launchCount DESC LIMIT :limit"
)
fun getKeysIncludeTypes(
includeTypes: List<String>?,
manuallySorted: Boolean = false,
automaticallySorted: Boolean = false,
frequentlyUsed: Boolean = false,
hidden: Boolean = false,
limit: Int,
): Flow<List<String>>
@Query(
"SELECT `key` FROM Searchable " +
"WHERE (`type` NOT IN (:excludeTypes)) AND " +
"(" +
"(:manuallySorted AND pinPosition > 1) OR " +
"(:automaticallySorted AND pinPosition = 1) OR" +
"(:frequentlyUsed AND pinPosition = 0 AND launchCount > 0) OR " +
"(:hidden AND hidden = 1)" +
") AND hidden = :hidden ORDER BY pinPosition DESC, weight DESC, launchCount DESC LIMIT :limit"
)
fun getKeysExcludeTypes(
excludeTypes: List<String>?,
manuallySorted: Boolean = false,
automaticallySorted: Boolean = false,
frequentlyUsed: Boolean = false,
hidden: Boolean = false,
limit: Int,
): Flow<List<String>>
@Query("SELECT * FROM Searchable WHERE `key` IN (:keys)")
suspend fun getByKeys(keys: List<String>): List<SavedSearchableEntity>
@Query("SELECT * FROM Searchable WHERE `key` = :key")
fun getByKey(key: String): Flow<SavedSearchableEntity?>
@Transaction
suspend fun touch(item: SavedSearchableEntity, alpha: Double) {
incrementLaunchCount(item.key)
increaseWeightWhere(item.key, alpha)
reduceWeightExcept(item.key, alpha)
insert(item)
}
@Query("UPDATE Searchable SET launchCount = launchCount + 1 WHERE `key` = :key")
fun incrementLaunchCount(key: String)
@Query("UPDATE Searchable SET `weight` = `weight` * (1.0 - :alpha) WHERE `key` != :key")
fun reduceWeightExcept(key: String, alpha: Double)
@Query("UPDATE Searchable SET `weight` = `weight` + :alpha * (1.0 - `weight`) WHERE `key` == :key")
fun increaseWeightWhere(key: String, alpha: Double)
@Query("DELETE FROM Searchable WHERE `key` = :key")
suspend fun delete(key: String)
@Query("UPDATE Searchable SET `pinPosition` = 0")
suspend fun unpinAll()
@Query("SELECT `key` FROM Searchable WHERE `key` IN (:keys) AND launchCount > 0 ORDER BY launchCount DESC, pinPosition DESC")
fun sortByRelevance(keys: List<String>): Flow<List<String>>
@Query("SELECT `key` FROM Searchable WHERE `key` IN (:keys) ORDER BY `weight` DESC, pinPosition DESC")
fun sortByWeight(keys: List<String>): Flow<List<String>>
@Query("SELECT hidden FROM Searchable WHERE `key` = :key UNION SELECT 0 as hidden ORDER BY hidden DESC LIMIT 1")
fun isHidden(key: String): Flow<Boolean>
@Query("SELECT pinPosition FROM Searchable WHERE `key` = :key UNION SELECT 0 as pinPosition ORDER BY pinPosition DESC LIMIT 1")
fun isPinned(key: String): Flow<Boolean>
}
@@ -0,0 +1,23 @@
package de.mm20.launcher2.database
import androidx.room.*
import de.mm20.launcher2.database.entities.ForecastEntity
import kotlinx.coroutines.flow.Flow
@Dao
interface WeatherDao {
@Query("SELECT * FROM ${ForecastEntity.TABLE_NAME} ORDER BY timestamp ASC")
fun getForecasts(): Flow<List<ForecastEntity>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(forecasts: List<ForecastEntity>)
@Query("DELETE FROM ${ForecastEntity.TABLE_NAME}")
fun deleteAll()
@Transaction
fun replaceAll(forecasts: List<ForecastEntity>) {
deleteAll()
insertAll(forecasts)
}
}
@@ -0,0 +1,57 @@
package de.mm20.launcher2.database
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import de.mm20.launcher2.database.entities.WidgetEntity
import de.mm20.launcher2.database.entities.PartialWidgetEntity
import kotlinx.coroutines.flow.Flow
import java.util.UUID
@Dao
interface WidgetDao {
@Query("SELECT * FROM Widget WHERE parentId IS NULL ORDER BY position ASC LIMIT :limit OFFSET :offset")
fun queryRoot(limit: Int, offset: Int): Flow<List<WidgetEntity>>
@Query("SELECT * FROM Widget WHERE parentId = :parentId ORDER BY position ASC LIMIT :limit OFFSET :offset")
fun queryByParent(parentId: UUID,limit: Int, offset: Int): Flow<List<WidgetEntity>>
@Insert
suspend fun insert(widget: WidgetEntity)
@Insert
suspend fun insert(widgets: List<WidgetEntity>)
@Update(entity = WidgetEntity::class)
suspend fun patch(widget: PartialWidgetEntity)
@Update(entity = WidgetEntity::class)
suspend fun patch(widgets: List<PartialWidgetEntity>)
@Update
suspend fun update(widget: WidgetEntity)
@Update
suspend fun update(widgets: List<WidgetEntity>)
@Query("DELETE FROM Widget WHERE id = :id")
suspend fun delete(id: UUID)
@Query("DELETE FROM WIDGET WHERE id IN (:ids)")
suspend fun delete(ids: List<UUID>)
@Query("DELETE FROM Widget WHERE parentId = :parentId")
suspend fun deleteByParent(parentId: UUID)
@Query("DELETE FROM Widget WHERE parentId IS NULL")
suspend fun deleteRoot()
@Query("SELECT EXISTS(SELECT 1 FROM Widget WHERE type = :type)")
fun exists(type: String): Flow<Boolean>
@Query("SELECT COUNT(*) FROM Widget WHERE type = :type")
fun count(type: String): Flow<Int>
}
@@ -0,0 +1,33 @@
package de.mm20.launcher2.database.daos
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import de.mm20.launcher2.database.entities.ThemeEntity
import kotlinx.coroutines.flow.Flow
import java.util.UUID
@Dao
interface ThemeDao {
@Query("SELECT * FROM Theme")
fun getAll(): Flow<List<ThemeEntity>>
@Query("SELECT * FROM Theme WHERE id = :id LIMIT 1")
fun get(id: UUID): Flow<ThemeEntity?>
@Insert
suspend fun insert(theme: ThemeEntity)
@Update
suspend fun update(theme: ThemeEntity)
@Query("DELETE FROM Theme WHERE id = :id")
suspend fun delete(id: UUID)
@Query("DELETE FROM Theme")
suspend fun deleteAll()
@Insert
fun insertAll(themes: List<ThemeEntity>)
}
@@ -0,0 +1,11 @@
package de.mm20.launcher2.database.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "Currency")
data class CurrencyEntity(
@PrimaryKey val symbol: String,
val value: Double,
val lastUpdate: Long
)
@@ -0,0 +1,12 @@
package de.mm20.launcher2.database.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "CustomAttributes")
data class CustomAttributeEntity(
val key: String,
val type: String,
val value: String,
@PrimaryKey(autoGenerate = true) val id: Int? = null,
)
@@ -0,0 +1,33 @@
package de.mm20.launcher2.database.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = ForecastEntity.TABLE_NAME)
data class ForecastEntity(
@PrimaryKey val timestamp: Long,
val temperature: Double,
val minTemp: Double = -1.0,
val maxTemp: Double = -1.0,
val pressure: Double = -1.0,
val humidity: Double = -1.0,
val icon: Int,
val condition: String,
val clouds: Int = -1,
val windSpeed: Double = -1.0,
val windDirection: Double = -1.0,
@ColumnInfo(name = "rain") val precipitation: Double = -1.0,
val snow: Double = -1.0,
val night: Boolean = false,
val location: String,
val provider: String,
val providerUrl: String = "",
@ColumnInfo(name = "rainProbability") val precipProbability: Int = -1,
val snowProbability: Int = -1,
val updateTime: Long
) {
companion object {
const val TABLE_NAME = "forecasts"
}
}
@@ -0,0 +1,17 @@
package de.mm20.launcher2.database.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "Icons")
data class IconEntity(
val type: String,
val packageName: String? = null,
val activityName: String? = null,
val drawable: String?,
val extras: String? = null,
val iconPack: String,
val name: String? = null,
val themed: Boolean = false,
@PrimaryKey(autoGenerate = true) val id : Long? = null
)
@@ -0,0 +1,13 @@
package de.mm20.launcher2.database.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "IconPack")
data class IconPackEntity(
val name: String,
@PrimaryKey val packageName: String,
val version: String,
var scale: Float = 1f,
val themed: Boolean = false,
)
@@ -0,0 +1,23 @@
package de.mm20.launcher2.database.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "Searchable")
data class SavedSearchableEntity(
@PrimaryKey val key: String,
val type: String,
@ColumnInfo(name = "searchable") val serializedSearchable: String,
@ColumnInfo(defaultValue = "0") val launchCount: Int,
@ColumnInfo(defaultValue = "0") val pinPosition: Int,
@ColumnInfo(defaultValue = "0") val hidden: Boolean,
@ColumnInfo(defaultValue = "0.0") val weight: Double
)
data class SavedSearchableUpdatePinEntity(
val key: String,
val type: String,
@ColumnInfo(name = "searchable") val serializedSearchable: String,
val pinPosition: Int? = null,
)
@@ -0,0 +1,16 @@
package de.mm20.launcher2.database.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "SearchAction")
data class SearchActionEntity(
@PrimaryKey val position: Int,
val type: String,
val data: String? = null,
val label: String? = null,
val icon: Int? = null,
val color: Int? = null,
val customIcon: String? = null,
val options: String? = null,
)
@@ -0,0 +1,92 @@
package de.mm20.launcher2.database.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.util.UUID
@Entity(tableName = "Theme")
data class ThemeEntity(
@PrimaryKey val id: UUID,
val name: String,
val corePaletteA1: Int?,
val corePaletteA2: Int?,
val corePaletteA3: Int?,
val corePaletteN1: Int?,
val corePaletteN2: Int?,
val corePaletteE: Int?,
val lightPrimary: String?,
val lightOnPrimary: String?,
val lightPrimaryContainer: String?,
val lightOnPrimaryContainer: String?,
val lightSecondary: String?,
val lightOnSecondary: String?,
val lightSecondaryContainer: String?,
val lightOnSecondaryContainer: String?,
val lightTertiary: String?,
val lightOnTertiary: String?,
val lightTertiaryContainer: String?,
val lightOnTertiaryContainer: String?,
val lightError: String?,
val lightOnError: String?,
val lightErrorContainer: String?,
val lightOnErrorContainer: String?,
val lightSurface: String?,
val lightOnSurface: String?,
val lightOnSurfaceVariant: String?,
val lightOutline: String?,
val lightOutlineVariant: String?,
val lightInverseSurface: String?,
val lightInverseOnSurface: String?,
val lightInversePrimary: String?,
val lightSurfaceDim: String?,
val lightSurfaceBright: String?,
val lightSurfaceContainerLowest: String?,
val lightSurfaceContainerLow: String?,
val lightSurfaceContainer: String?,
val lightSurfaceContainerHigh: String?,
val lightSurfaceContainerHighest: String?,
val lightBackground: String?,
val lightOnBackground: String?,
val lightSurfaceTint: String?,
val lightScrim: String?,
val lightSurfaceVariant: String?,
val darkPrimary: String?,
val darkOnPrimary: String?,
val darkPrimaryContainer: String?,
val darkOnPrimaryContainer: String?,
val darkSecondary: String?,
val darkOnSecondary: String?,
val darkSecondaryContainer: String?,
val darkOnSecondaryContainer: String?,
val darkTertiary: String?,
val darkOnTertiary: String?,
val darkTertiaryContainer: String?,
val darkOnTertiaryContainer: String?,
val darkError: String?,
val darkOnError: String?,
val darkErrorContainer: String?,
val darkOnErrorContainer: String?,
val darkSurface: String?,
val darkOnSurface: String?,
val darkOnSurfaceVariant: String?,
val darkOutline: String?,
val darkOutlineVariant: String?,
val darkInverseSurface: String?,
val darkInverseOnSurface: String?,
val darkInversePrimary: String?,
val darkSurfaceDim: String?,
val darkSurfaceBright: String?,
val darkSurfaceContainerLowest: String?,
val darkSurfaceContainerLow: String?,
val darkSurfaceContainer: String?,
val darkSurfaceContainerHigh: String?,
val darkSurfaceContainerHighest: String?,
val darkBackground: String?,
val darkOnBackground: String?,
val darkSurfaceTint: String?,
val darkScrim: String?,
val darkSurfaceVariant: String?,
)
@@ -0,0 +1,14 @@
package de.mm20.launcher2.database.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "Websearch")
data class WebsearchEntity(
var urlTemplate: String,
var label: String,
var color: Int,
var icon: String?,
var encoding: Int?,
@PrimaryKey(autoGenerate = true) val id: Long?
)
@@ -0,0 +1,24 @@
package de.mm20.launcher2.database.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.util.UUID
@Entity(tableName = "Widget")
data class WidgetEntity(
val type: String,
var config: String?,
var position: Int,
@PrimaryKey val id: UUID,
val parentId: UUID? = null,
)
/**
* Partial entity for updating and deleting
*/
data class PartialWidgetEntity(
val type: String,
var config: String?,
@PrimaryKey val id: UUID,
)
@@ -0,0 +1,13 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_10_11 : Migration(10, 11) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `temp` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, PRIMARY KEY(`key`))")
database.execSQL("INSERT INTO `temp` SELECT `key`, `searchable`, `launchCount`, `pinned`, `hidden` FROM `Searchable`")
database.execSQL("DROP TABLE `Searchable`")
database.execSQL("ALTER TABLE `temp` RENAME TO `Searchable`")
}
}
@@ -0,0 +1,10 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_11_12 : Migration(11, 12) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `Currency` (`symbol` TEXT NOT NULL, `value` REAL NOT NULL, `lastUpdate` INTEGER NOT NULL, PRIMARY KEY(`symbol`))")
}
}
@@ -0,0 +1,10 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_12_13 : Migration(12, 13) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `Plugin` (`packageName` TEXT NOT NULL, `data` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`packageName`, `data`))")
}
}
@@ -0,0 +1,10 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_13_14 : Migration(13, 14) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS `Plugins`;")
}
}
@@ -0,0 +1,9 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_14_15 : Migration(14, 15) {
override fun migrate(database: SupportSQLiteDatabase) {
}
}
@@ -0,0 +1,19 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_15_16 : Migration(15, 16) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS `CustomAttributes` (
`key` TEXT NOT NULL,
`type` TEXT NOT NULL,
`value` TEXT NOT NULL,
`id` INTEGER PRIMARY KEY AUTOINCREMENT
)
""".trimIndent()
)
}
}
@@ -0,0 +1,10 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_16_17 : Migration(16, 17) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Websearch ADD COLUMN encoding INTEGER")
}
}
@@ -0,0 +1,17 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_17_18 : Migration(17, 18) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Searchable ADD COLUMN type TEXT NOT NULL DEFAULT ''")
database.execSQL(
"""
UPDATE Searchable
SET type = SUBSTR(`key`, 0, INSTR(`key`, '://')),
searchable = SUBSTR(`searchable`, INSTR(`searchable`, '#') + 1)
""".trimIndent()
)
}
}
@@ -0,0 +1,54 @@
package de.mm20.launcher2.database.migrations
import androidx.core.database.getStringOrNull
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import de.mm20.launcher2.ktx.jsonObjectOf
class Migration_18_19 : Migration(18, 19) {
override fun migrate(database: SupportSQLiteDatabase) {
val websearches =
database.query("SELECT label, urlTemplate, color, icon, encoding FROM `Websearch` ORDER BY label ASC")
database.execSQL("CREATE TABLE IF NOT EXISTS `SearchAction` (`position` INTEGER NOT NULL, `type` TEXT NOT NULL, `data` TEXT, `label` TEXT, `icon` INTEGER, `color` INTEGER, `customIcon` TEXT, `options` TEXT, PRIMARY KEY(`position`))"
)
database.execSQL("INSERT INTO `SearchAction` (`position`, `type`) VALUES" +
"(0, 'call')," +
"(1, 'message')," +
"(2, 'email')," +
"(3, 'contact')," +
"(4, 'alarm')," +
"(5, 'timer')," +
"(6, 'calendar')," +
"(7, 'website')"
)
var position = 8
while (websearches.moveToNext()) {
val label = websearches.getString(0)
val data = websearches.getString(1)
val color = 0
val icon = websearches.getStringOrNull(3)
val encoding = websearches.getStringOrNull(4)
val options = encoding?.let{
jsonObjectOf("encoding" to encoding).toString()
}
database.execSQL(
"INSERT INTO `SearchAction` (`position`, `type`, `data`, `label`, `color`, `icon`, `customIcon`, `options`)" +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
arrayOf(
position,
"url",
data,
label,
color,
if (icon == null) 0 else 1,
icon,
options
)
)
position++
}
database.execSQL("DROP TABLE `Websearch`")
}
}
@@ -0,0 +1,23 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_19_20: Migration(19, 20) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE `Icons` RENAME TO `Icons_old`")
database.execSQL("""
CREATE TABLE IF NOT EXISTS `Icons` (
`type` TEXT NOT NULL,
`componentName` TEXT,
`drawable` TEXT,
`iconPack` TEXT NOT NULL,
`name` TEXT,
`themed` INTEGER NOT NULL DEFAULT 0,
`id` INTEGER PRIMARY KEY AUTOINCREMENT)
""")
database.execSQL("INSERT INTO `Icons` (`type`, `componentName`, `drawable`, `iconPack`, `themed`, `name`) SELECT `type`, `componentName`, `drawable`, `iconPack`, 0, null FROM `Icons_old`")
database.execSQL("DROP TABLE `Icons_old`")
database.execSQL("ALTER TABLE `IconPack` ADD COLUMN `themed` INTEGER NOT NULL DEFAULT 0")
}
}
@@ -0,0 +1,23 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_20_21: Migration(20, 21) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE `Icons`")
database.execSQL("DELETE FROM `IconPack`")
database.execSQL("""
CREATE TABLE IF NOT EXISTS `Icons` (
`type` TEXT NOT NULL,
`packageName` TEXT,
`activityName` TEXT,
`drawable` TEXT,
`extras` TEXT,
`iconPack` TEXT NOT NULL,
`name` TEXT,
`themed` INTEGER NOT NULL DEFAULT 0,
`id` INTEGER PRIMARY KEY AUTOINCREMENT)
""")
}
}
@@ -0,0 +1,37 @@
package de.mm20.launcher2.database.migrations
import android.util.Log
import androidx.core.database.getIntOrNull
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_21_22: Migration(21, 22) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
ALTER TABLE `Searchable`
ADD `weight` DOUBLE NOT NULL DEFAULT 0.0
""")
database.query("""
SELECT MAX(`launchCount`)
FROM `Searchable`
""")
.runCatching {
if (!this.moveToFirst()) {
return
}
this.getIntOrNull(0)
?.run {
database.execSQL("""
UPDATE `Searchable`
SET `weight` = `launchCount` / $this
""")
}
}.onFailure {
Log.e("Migration_21_22", "Setting default values for weight failed", it)
}
}
}
@@ -0,0 +1,44 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import de.mm20.launcher2.ktx.toBytes
import org.koin.core.component.KoinComponent
import java.util.UUID
class Migration_22_23 : Migration(22, 23), KoinComponent {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Widget RENAME TO Widget_old")
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Widget` (
`type` TEXT NOT NULL,
`config` TEXT,
`position` INTEGER NOT NULL,
`id` BLOB NOT NULL,
`parentId` BLOB,
PRIMARY KEY(`id`)
)
"""
)
val oldWidgets =
database.query("SELECT `type`, `data`, `height`, `position` FROM `Widget_old`")
while (oldWidgets.moveToNext()) {
val oldType = oldWidgets.getString(0)
val data = oldWidgets.getString(1)
val newType = if (oldType == "3rdparty") "app" else data
val height = oldWidgets.getInt(2)
val position = oldWidgets.getInt(3)
val id = UUID.randomUUID()
val config = if (oldType == "3rdparty") {
"{\"widgetId\": $data, \"height\": $height}"
} else null
database.execSQL(
"INSERT INTO `Widget` (`type`, `config`, `position`, `id`) VALUES (?, ?, ?, ?)",
arrayOf(newType, config, position, id.toBytes())
)
}
oldWidgets.close()
database.execSQL("DROP TABLE Widget_old")
}
}
@@ -0,0 +1,31 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_23_24 : Migration(23, 24) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Searchable RENAME TO Searchable_old")
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Searchable` (
`key` TEXT NOT NULL,
`type` TEXT NOT NULL,
`searchable` TEXT NOT NULL,
`launchCount` INTEGER NOT NULL DEFAULT 0,
`pinPosition` INTEGER NOT NULL DEFAULT 0,
`hidden` INTEGER NOT NULL DEFAULT 0,
`weight` DOUBLE NOT NULL DEFAULT 0.0,
PRIMARY KEY(`key`)
)
"""
)
database.execSQL(
"""
INSERT INTO `Searchable` (`key`, `type`, `searchable`, `launchCount`, `pinPosition`, `hidden`, `weight`)
SELECT `key`, `type`, `searchable`, `launchCount`, `pinned`, `hidden`, `weight` FROM `Searchable_old`
"""
)
database.execSQL("DROP TABLE Searchable_old")
}
}
@@ -0,0 +1,217 @@
package de.mm20.launcher2.database.migrations
import android.content.Context
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import de.mm20.launcher2.database.R
import de.mm20.launcher2.ktx.toBytes
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.util.UUID
class Migration_24_25(
private val context: Context,
) : Migration(24, 25), KoinComponent {
private val dataStore: LauncherDataStore by inject()
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Theme` (
`id` BLOB NOT NULL,
`name` TEXT NOT NULL,
`corePaletteA1` INTEGER,
`corePaletteA2` INTEGER,
`corePaletteA3` INTEGER,
`corePaletteN1` INTEGER,
`corePaletteN2` INTEGER,
`corePaletteE` INTEGER,
`lightPrimary` TEXT,
`lightOnPrimary` TEXT,
`lightPrimaryContainer` TEXT,
`lightOnPrimaryContainer` TEXT,
`lightSecondary` TEXT,
`lightOnSecondary` TEXT,
`lightSecondaryContainer` TEXT,
`lightOnSecondaryContainer` TEXT,
`lightTertiary` TEXT,
`lightOnTertiary` TEXT,
`lightTertiaryContainer` TEXT,
`lightOnTertiaryContainer` TEXT,
`lightError` TEXT,
`lightOnError` TEXT,
`lightErrorContainer` TEXT,
`lightOnErrorContainer` TEXT,
`lightSurface` TEXT,
`lightOnSurface` TEXT,
`lightOnSurfaceVariant` TEXT,
`lightOutline` TEXT,
`lightOutlineVariant` TEXT,
`lightInverseSurface` TEXT,
`lightInverseOnSurface` TEXT,
`lightInversePrimary` TEXT,
`lightSurfaceDim` TEXT,
`lightSurfaceBright` TEXT,
`lightSurfaceContainerLowest` TEXT,
`lightSurfaceContainerLow` TEXT,
`lightSurfaceContainer` TEXT,
`lightSurfaceContainerHigh` TEXT,
`lightSurfaceContainerHighest` TEXT,
`lightBackground` TEXT,
`lightOnBackground` TEXT,
`lightSurfaceTint` TEXT,
`lightScrim` TEXT,
`lightSurfaceVariant` TEXT,
`darkPrimary` TEXT,
`darkOnPrimary` TEXT,
`darkPrimaryContainer` TEXT,
`darkOnPrimaryContainer` TEXT,
`darkSecondary` TEXT,
`darkOnSecondary` TEXT,
`darkSecondaryContainer` TEXT,
`darkOnSecondaryContainer` TEXT,
`darkTertiary` TEXT,
`darkOnTertiary` TEXT,
`darkTertiaryContainer` TEXT,
`darkOnTertiaryContainer` TEXT,
`darkError` TEXT,
`darkOnError` TEXT,
`darkErrorContainer` TEXT,
`darkOnErrorContainer` TEXT,
`darkSurface` TEXT,
`darkOnSurface` TEXT,
`darkOnSurfaceVariant` TEXT,
`darkOutline` TEXT,
`darkOutlineVariant` TEXT,
`darkInverseSurface` TEXT,
`darkInverseOnSurface` TEXT,
`darkInversePrimary` TEXT,
`darkSurfaceDim` TEXT,
`darkSurfaceBright` TEXT,
`darkSurfaceContainerLowest` TEXT,
`darkSurfaceContainerLow` TEXT,
`darkSurfaceContainer` TEXT,
`darkSurfaceContainerHigh` TEXT,
`darkSurfaceContainerHighest` TEXT,
`darkBackground` TEXT,
`darkOnBackground` TEXT,
`darkSurfaceTint` TEXT,
`darkScrim` TEXT,
`darkSurfaceVariant` TEXT,
PRIMARY KEY(`id`)
)
""".trimIndent()
)
// Special UUID for migrated custom color scheme. Same UUID is used in data store migration 16..17
val uuid = UUID(1L, 1L)
val customColors = runBlocking {
dataStore.data.map { it.appearance.customColors }.first()
}
database.execSQL("""INSERT INTO `Theme` VALUES (
?,?,?,?,?,?,?,?,?,?,
?,?,?,?,?,?,?,?,?,?,
?,?,?,?,?,?,?,?,?,?,
?,?,?,?,?,?,?,?,?,?,
?,?,?,?,?,?,?,?,?,?,
?,?,?,?,?,?,?,?,?,?,
?,?,?,?,?,?,?,?,?,?,
?,?,?,?,?,?,?,?,?,?
)
""".trimIndent(),
arrayOf(
uuid.toBytes(),
context.getString(R.string.preference_colors_custom),
customColors.baseColors.accent1.toHexColor(),
customColors.baseColors.accent2.toHexColor(),
customColors.baseColors.accent3.toHexColor(),
customColors.baseColors.neutral1.toHexColor(),
customColors.baseColors.neutral2.toHexColor(),
customColors.baseColors.error.toHexColor(),
customColors.lightScheme.primary.toHexColor(),
customColors.lightScheme.onPrimary.toHexColor(),
customColors.lightScheme.primaryContainer.toHexColor(),
customColors.lightScheme.onPrimaryContainer.toHexColor(),
customColors.lightScheme.secondary.toHexColor(),
customColors.lightScheme.onSecondary.toHexColor(),
customColors.lightScheme.secondaryContainer.toHexColor(),
customColors.lightScheme.onSecondaryContainer.toHexColor(),
customColors.lightScheme.tertiary.toHexColor(),
customColors.lightScheme.onTertiary.toHexColor(),
customColors.lightScheme.tertiaryContainer.toHexColor(),
customColors.lightScheme.onTertiaryContainer.toHexColor(),
customColors.lightScheme.error.toHexColor(),
customColors.lightScheme.onError.toHexColor(),
customColors.lightScheme.errorContainer.toHexColor(),
customColors.lightScheme.onErrorContainer.toHexColor(),
customColors.lightScheme.surface.toHexColor(),
customColors.lightScheme.onSurface.toHexColor(),
customColors.lightScheme.onSurfaceVariant.toHexColor(),
customColors.lightScheme.outline.toHexColor(),
customColors.lightScheme.outlineVariant.toHexColor(),
customColors.lightScheme.inverseSurface.toHexColor(),
customColors.lightScheme.inverseOnSurface.toHexColor(),
customColors.lightScheme.inversePrimary.toHexColor(),
customColors.lightScheme.surfaceDim.toHexColor(),
customColors.lightScheme.surfaceBright.toHexColor(),
customColors.lightScheme.surfaceContainerLowest.toHexColor(),
customColors.lightScheme.surfaceContainerLow.toHexColor(),
customColors.lightScheme.surfaceContainer.toHexColor(),
customColors.lightScheme.surfaceContainerHigh.toHexColor(),
customColors.lightScheme.surfaceContainerHighest.toHexColor(),
customColors.lightScheme.background.toHexColor(),
customColors.lightScheme.onBackground.toHexColor(),
customColors.lightScheme.surfaceTint.toHexColor(),
customColors.lightScheme.scrim.toHexColor(),
customColors.lightScheme.surfaceVariant.toHexColor(),
customColors.darkScheme.primary.toHexColor(),
customColors.darkScheme.onPrimary.toHexColor(),
customColors.darkScheme.primaryContainer.toHexColor(),
customColors.darkScheme.onPrimaryContainer.toHexColor(),
customColors.darkScheme.secondary.toHexColor(),
customColors.darkScheme.onSecondary.toHexColor(),
customColors.darkScheme.secondaryContainer.toHexColor(),
customColors.darkScheme.onSecondaryContainer.toHexColor(),
customColors.darkScheme.tertiary.toHexColor(),
customColors.darkScheme.onTertiary.toHexColor(),
customColors.darkScheme.tertiaryContainer.toHexColor(),
customColors.darkScheme.onTertiaryContainer.toHexColor(),
customColors.darkScheme.error.toHexColor(),
customColors.darkScheme.onError.toHexColor(),
customColors.darkScheme.errorContainer.toHexColor(),
customColors.darkScheme.onErrorContainer.toHexColor(),
customColors.darkScheme.surface.toHexColor(),
customColors.darkScheme.onSurface.toHexColor(),
customColors.darkScheme.onSurfaceVariant.toHexColor(),
customColors.darkScheme.outline.toHexColor(),
customColors.darkScheme.outlineVariant.toHexColor(),
customColors.darkScheme.inverseSurface.toHexColor(),
customColors.darkScheme.inverseOnSurface.toHexColor(),
customColors.darkScheme.inversePrimary.toHexColor(),
customColors.darkScheme.surfaceDim.toHexColor(),
customColors.darkScheme.surfaceBright.toHexColor(),
customColors.darkScheme.surfaceContainerLowest.toHexColor(),
customColors.darkScheme.surfaceContainerLow.toHexColor(),
customColors.darkScheme.surfaceContainer.toHexColor(),
customColors.darkScheme.surfaceContainerHigh.toHexColor(),
customColors.darkScheme.surfaceContainerHighest.toHexColor(),
customColors.darkScheme.background.toHexColor(),
customColors.darkScheme.onBackground.toHexColor(),
customColors.darkScheme.surfaceTint.toHexColor(),
customColors.darkScheme.scrim.toHexColor(),
customColors.darkScheme.surfaceVariant.toHexColor(),
)
)
}
fun Int.toHexColor(): String {
return "#${toUInt().toString(16).padStart(6, '0')}"
}
}
@@ -0,0 +1,14 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_6_7 : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE Searchable2 (`key` TEXT NOT NULL, `searchable` TEXT, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, `inAllApps` INTEGER NOT NULL, PRIMARY KEY(`key`))")
database.execSQL("INSERT INTO Searchable2 SELECT * FROM Searchable")
database.execSQL("DROP TABLE Searchable")
database.execSQL("ALTER TABLE Searchable2 RENAME TO Searchable")
}
}
@@ -0,0 +1,14 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import de.mm20.launcher2.database.entities.ForecastEntity
class Migration_7_8 : Migration(7, 8) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `${ForecastEntity.TABLE_NAME}2` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, `rainPropability` INTEGER NOT NULL, `snowProbability` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))")
database.execSQL("INSERT INTO ${ForecastEntity.TABLE_NAME}2 SELECT *, -1 as rainPropability, -1 as snowPropability FROM ${ForecastEntity.TABLE_NAME}")
database.execSQL("DROP TABLE ${ForecastEntity.TABLE_NAME}")
database.execSQL("ALTER TABLE ${ForecastEntity.TABLE_NAME}2 RENAME TO ${ForecastEntity.TABLE_NAME}")
}
}
@@ -0,0 +1,38 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import de.mm20.launcher2.database.entities.ForecastEntity
class Migration_8_9 : Migration(8, 9) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"CREATE TABLE IF NOT EXISTS `${ForecastEntity.TABLE_NAME}2` (" +
"`timestamp` INTEGER NOT NULL, " +
"`temperature` REAL NOT NULL, " +
"`minTemp` REAL NOT NULL, " +
"`maxTemp` REAL NOT NULL, " +
"`pressure` REAL NOT NULL, " +
"`humidity` REAL NOT NULL, " +
"`icon` INTEGER NOT NULL, " +
"`condition` TEXT NOT NULL, " +
"`clouds` INTEGER NOT NULL, " +
"`windSpeed` REAL NOT NULL, " +
"`windDirection` REAL NOT NULL, " +
"`rain` REAL NOT NULL, " +
"`snow` REAL NOT NULL, " +
"`night` INTEGER NOT NULL, " +
"`location` TEXT NOT NULL, " +
"`provider` TEXT NOT NULL, " +
"`providerUrl` TEXT NOT NULL, " +
"`rainProbability` INTEGER NOT NULL, " +
"`snowProbability` INTEGER NOT NULL, " +
"`updateTime` INTEGER NOT NULL, " +
"PRIMARY KEY(`timestamp`))"
)
database.execSQL("INSERT INTO ${ForecastEntity.TABLE_NAME}2 SELECT timestamp, temperature, minTemp, maxTemp, pressure, humidity, icon, condition, clouds, windSpeed, windDirection, rain, snow, night, location, provider, providerUrl, rainPropability as rainProbability, snowProbability, 0 as updateTime FROM ${ForecastEntity.TABLE_NAME}")
database.execSQL("DROP TABLE ${ForecastEntity.TABLE_NAME}")
database.execSQL("ALTER TABLE ${ForecastEntity.TABLE_NAME}2 RENAME TO ${ForecastEntity.TABLE_NAME}")
}
}
@@ -0,0 +1,11 @@
package de.mm20.launcher2.database.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration_9_10 : Migration(9, 10) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `Plugins` (`packageName` TEXT NOT NULL, `label` TEXT NOT NULL, `description` TEXT NOT NULL, `pluginClassName` TEXT NOT NULL, `enabled` INTEGER NOT NULL, PRIMARY KEY(`packageName`) );")
}
}