From e2eacba91ce790fc98d6ad41fcbe423e6f0e0b3d Mon Sep 17 00:00:00 2001 From: Igor Escodro Date: Thu, 5 Oct 2023 10:28:19 -0400 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Introduce=20category=20pre-populati?= =?UTF-8?q?on=20on=20iOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the SQLDelight does have a callback when the database schema is created, the solution was checking if the database is created before inserting the categories. The library creates the database on a specific folder, which is now checked on iOS variant. This change also required a few structure changes, such as the place of the strings and implementing the resource access on each platform. --- data/local/build.gradle.kts | 3 + .../local/provider/AndroidDriverFactory.kt | 64 ++++++------------- .../local/provider/DatabaseProvider.kt | 21 +++++- .../escodro/local/provider/DriverFactory.kt | 32 ++++++++-- .../local/provider/IosDriverFactory.kt | 64 +++++++++++++++++-- .../src/main/res/values-pt-rBR/strings.xml | 6 -- data/local/src/main/res/values/strings.xml | 6 -- .../commonMain/resources/MR/base/strings.xml | 5 ++ .../resources/MR/colors}/colors.xml | 0 .../commonMain/resources/MR/pt-br/strings.xml | 5 ++ 10 files changed, 140 insertions(+), 66 deletions(-) delete mode 100644 data/local/src/main/res/values-pt-rBR/strings.xml delete mode 100644 data/local/src/main/res/values/strings.xml rename {data/local/src/main/res/values => resources/src/commonMain/resources/MR/colors}/colors.xml (100%) diff --git a/data/local/build.gradle.kts b/data/local/build.gradle.kts index 7ad7259b6..bc6d001fd 100644 --- a/data/local/build.gradle.kts +++ b/data/local/build.gradle.kts @@ -16,9 +16,12 @@ kotlin { commonDependencies { implementation(projects.libraries.coroutines) implementation(projects.data.repository) + implementation(projects.resources) + implementation(libs.koin.core) implementation(libs.kotlinx.datetime) implementation(libs.sqldelight.coroutines) + implementation(libs.moko.resources.core) } androidDependencies { implementation(libs.sqldelight.driver) diff --git a/data/local/src/androidMain/kotlin/com/escodro/local/provider/AndroidDriverFactory.kt b/data/local/src/androidMain/kotlin/com/escodro/local/provider/AndroidDriverFactory.kt index fe8243368..15df4e006 100644 --- a/data/local/src/androidMain/kotlin/com/escodro/local/provider/AndroidDriverFactory.kt +++ b/data/local/src/androidMain/kotlin/com/escodro/local/provider/AndroidDriverFactory.kt @@ -1,63 +1,41 @@ package com.escodro.local.provider -import android.annotation.SuppressLint import android.content.Context import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.android.AndroidSqliteDriver -import com.escodro.coroutines.AppCoroutineScope import com.escodro.local.AlkaaDatabase import com.escodro.local.Category -import com.escodro.local.R +import com.escodro.resources.MR +import dev.icerock.moko.resources.desc.desc /** * Provides the platform-specific [SqlDriver] to be used in the database. */ internal class AndroidDriverFactory( private val context: Context, - private val appCoroutineScope: AppCoroutineScope, ) : DriverFactory { - /** - * Creates the platform-specific [SqlDriver] to be used in the database. - * - * @param databaseName the database name - * - * @return the [SqlDriver] to be used in the database - */ override fun createDriver(databaseName: String): SqlDriver = AndroidSqliteDriver(AlkaaDatabase.Schema, context, databaseName) - override fun prepopulateDatabase(database: AlkaaDatabase, databaseName: String) { - val databaseFile = context.getDatabasePath(databaseName) - if (!databaseFile.exists()) { - appCoroutineScope.launch { - for (category in getDefaultCategoryList()) { - database.categoryQueries.insert( - category_name = category.category_name, - category_color = category.category_color, - ) - } - } - } - } + override fun shouldPrepopulateDatabase(databaseName: String): Boolean = + !context.getDatabasePath(databaseName).exists() - @SuppressLint("ResourceType") - private fun getDefaultCategoryList() = - listOf( - Category( - category_id = 0, - category_name = context.getString(R.string.category_default_personal), - category_color = context.resources.getString(R.color.blue), - ), - Category( - category_id = 0, - category_name = context.getString(R.string.category_default_work), - category_color = context.getString(R.color.green), - ), - Category( - category_id = 0, - category_name = context.getString(R.string.category_default_shopping), - category_color = context.getString(R.color.orange), - ), - ) + override fun getPrepopulateData(): List = listOf( + Category( + category_id = 0, + category_name = MR.strings.category_default_personal.desc().toString(context), + category_color = context.getString(MR.colors.blue.resourceId), + ), + Category( + category_id = 0, + category_name = MR.strings.category_default_work.desc().toString(context), + category_color = context.getString(MR.colors.green.resourceId), + ), + Category( + category_id = 0, + category_name = MR.strings.category_default_shopping.desc().toString(context), + category_color = context.getString(MR.colors.orange.resourceId), + ), + ) } diff --git a/data/local/src/commonMain/kotlin/com/escodro/local/provider/DatabaseProvider.kt b/data/local/src/commonMain/kotlin/com/escodro/local/provider/DatabaseProvider.kt index 714da1d42..745d93284 100644 --- a/data/local/src/commonMain/kotlin/com/escodro/local/provider/DatabaseProvider.kt +++ b/data/local/src/commonMain/kotlin/com/escodro/local/provider/DatabaseProvider.kt @@ -1,5 +1,6 @@ package com.escodro.local.provider +import com.escodro.coroutines.AppCoroutineScope import com.escodro.local.AlkaaDatabase import com.escodro.local.Task import com.escodro.local.converter.alarmIntervalAdapter @@ -8,7 +9,10 @@ import com.escodro.local.converter.dateTimeAdapter /** * Repository with the local database. */ -class DatabaseProvider(private val driverFactory: DriverFactory) { +internal class DatabaseProvider( + private val driverFactory: DriverFactory, + private val appCoroutineScope: AppCoroutineScope, +) { private var database: AlkaaDatabase? = null @@ -32,10 +36,23 @@ class DatabaseProvider(private val driverFactory: DriverFactory) { ), ) - driverFactory.prepopulateDatabase(database = database, databaseName = DATABASE_NAME) + prepopulateDatabase(database) return database } + private fun prepopulateDatabase(database: AlkaaDatabase) { + if (driverFactory.shouldPrepopulateDatabase(DATABASE_NAME)) { + appCoroutineScope.launch { + for (category in driverFactory.getPrepopulateData()) { + database.categoryQueries.insert( + category_name = category.category_name, + category_color = category.category_color, + ) + } + } + } + } + private companion object { private const val DATABASE_NAME = "todo-db" } diff --git a/data/local/src/commonMain/kotlin/com/escodro/local/provider/DriverFactory.kt b/data/local/src/commonMain/kotlin/com/escodro/local/provider/DriverFactory.kt index b218b9924..22a8f9323 100644 --- a/data/local/src/commonMain/kotlin/com/escodro/local/provider/DriverFactory.kt +++ b/data/local/src/commonMain/kotlin/com/escodro/local/provider/DriverFactory.kt @@ -1,11 +1,35 @@ package com.escodro.local.provider import app.cash.sqldelight.db.SqlDriver -import com.escodro.local.AlkaaDatabase +import com.escodro.local.Category -interface DriverFactory { +/** + * Provides the platform-specific [SqlDriver] to be used in the database. + */ +internal interface DriverFactory { + + /** + * Creates the platform-specific [SqlDriver] to be used in the database. + * + * @param databaseName the database name + * + * @return the [SqlDriver] to be used in the database + */ fun createDriver(databaseName: String): SqlDriver - // TODO better document here - fun prepopulateDatabase(database: AlkaaDatabase, databaseName: String) + /** + * Checks if the database is opening for the first time and should be prepopulated. + * + * @param databaseName the database name + * + * @return true if the database should be prepopulated, false otherwise + */ + fun shouldPrepopulateDatabase(databaseName: String): Boolean + + /** + * Gets the prepopulate data to be inserted in the database. + * + * @return the prepopulate data + */ + fun getPrepopulateData(): List } diff --git a/data/local/src/iosMain/kotlin/com/escodro/local/provider/IosDriverFactory.kt b/data/local/src/iosMain/kotlin/com/escodro/local/provider/IosDriverFactory.kt index 5914ae392..f95ceac3a 100644 --- a/data/local/src/iosMain/kotlin/com/escodro/local/provider/IosDriverFactory.kt +++ b/data/local/src/iosMain/kotlin/com/escodro/local/provider/IosDriverFactory.kt @@ -3,15 +3,69 @@ package com.escodro.local.provider import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.native.NativeSqliteDriver import com.escodro.local.AlkaaDatabase +import com.escodro.local.Category +import com.escodro.resources.MR +import dev.icerock.moko.resources.desc.desc +import dev.icerock.moko.resources.getUIColor +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.DoubleVarOf +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.get +import platform.CoreGraphics.CGColorGetComponents +import platform.CoreGraphics.CGFloat +import platform.Foundation.NSFileManager +import platform.Foundation.NSLibraryDirectory +import platform.Foundation.NSURL +import platform.Foundation.NSUserDomainMask +import platform.Foundation.URLByAppendingPathComponent +import platform.UIKit.UIColor internal class IosDriverFactory : DriverFactory { override fun createDriver(databaseName: String): SqlDriver = NativeSqliteDriver(AlkaaDatabase.Schema, databaseName) - override fun prepopulateDatabase( - database: AlkaaDatabase, - databaseName: String, - ) { - // TODO: Implement prepopulateDatabase + override fun shouldPrepopulateDatabase(databaseName: String): Boolean = + !databaseExists(databaseName) + + override fun getPrepopulateData(): List = listOf( + Category( + category_id = 0, + category_name = MR.strings.category_default_personal.desc().localized(), + category_color = MR.colors.blue.getUIColor().toHex(), + ), + Category( + category_id = 0, + category_name = MR.strings.category_default_work.desc().localized(), + category_color = MR.colors.green.getUIColor().toHex(), + ), + Category( + category_id = 0, + category_name = MR.strings.category_default_shopping.desc().localized(), + category_color = MR.colors.orange.getUIColor().toHex(), + ), + ) + + private fun databaseExists(databaseName: String): Boolean { + val fileManager = NSFileManager.defaultManager + val documentDirectory = NSFileManager.defaultManager.URLsForDirectory( + NSLibraryDirectory, + NSUserDomainMask, + ).last() as NSURL + val file = documentDirectory + .URLByAppendingPathComponent("$DATABASE_PATH$databaseName")?.path + return fileManager.fileExistsAtPath(file ?: "") + } + + @OptIn(ExperimentalForeignApi::class) + private fun UIColor.toHex(): String { + val components: CPointer>? = CGColorGetComponents(CGColor) + val r = components?.get(0)?.times(255)?.toInt()?.toString(16)?.padStart(2, '0') ?: "00" + val g = components?.get(1)?.times(255)?.toInt()?.toString(16)?.padStart(2, '0') ?: "00" + val b = components?.get(2)?.times(255)?.toInt()?.toString(16)?.padStart(2, '0') ?: "00" + return "#$r$g$b" + } + + private companion object { + private const val DATABASE_PATH = "Application Support/databases/" } } diff --git a/data/local/src/main/res/values-pt-rBR/strings.xml b/data/local/src/main/res/values-pt-rBR/strings.xml deleted file mode 100644 index dadae7240..000000000 --- a/data/local/src/main/res/values-pt-rBR/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - Pessoal - Trabalho - Lista de Compras - diff --git a/data/local/src/main/res/values/strings.xml b/data/local/src/main/res/values/strings.xml deleted file mode 100644 index 946ec024e..000000000 --- a/data/local/src/main/res/values/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - Personal - Work - Shopping List - diff --git a/resources/src/commonMain/resources/MR/base/strings.xml b/resources/src/commonMain/resources/MR/base/strings.xml index f2e1ee43c..1c101d0b2 100644 --- a/resources/src/commonMain/resources/MR/base/strings.xml +++ b/resources/src/commonMain/resources/MR/base/strings.xml @@ -90,4 +90,9 @@ Settings Open source licenses + + Personal + Work + Shopping List + diff --git a/data/local/src/main/res/values/colors.xml b/resources/src/commonMain/resources/MR/colors/colors.xml similarity index 100% rename from data/local/src/main/res/values/colors.xml rename to resources/src/commonMain/resources/MR/colors/colors.xml diff --git a/resources/src/commonMain/resources/MR/pt-br/strings.xml b/resources/src/commonMain/resources/MR/pt-br/strings.xml index 1f90f2c23..b727cf7f7 100644 --- a/resources/src/commonMain/resources/MR/pt-br/strings.xml +++ b/resources/src/commonMain/resources/MR/pt-br/strings.xml @@ -86,4 +86,9 @@ Funcionalidades Preferências Licenças de código aberto + + + Pessoal + Trabalho + Lista de Compras