diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0d6b686a..67de277a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -79,5 +79,8 @@ dependencies { //testImplementation (libs.junit4) //androidTestImplementation (libs.bundles.android.test.bundle) //debugImplementation (libs.bundles.compose.debug.bundle) + implementation(libs.timber) + implementation(libs.play.update) + implementation(libs.play.update.kts) } \ No newline at end of file diff --git a/app/src/main/java/com/mshdabiola/playnotepad/MainActivity.kt b/app/src/main/java/com/mshdabiola/playnotepad/MainActivity.kt index 620fca56..c452868c 100644 --- a/app/src/main/java/com/mshdabiola/playnotepad/MainActivity.kt +++ b/app/src/main/java/com/mshdabiola/playnotepad/MainActivity.kt @@ -7,6 +7,9 @@ import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSiz import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat +import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.UpdateAvailability import com.mshdabiola.designsystem.theme.NotePadAppTheme import com.mshdabiola.playnotepad.ui.NotePadApp import dagger.hilt.android.AndroidEntryPoint @@ -25,4 +28,26 @@ class MainActivity : ComponentActivity() { } } } + + override fun onStart() { + super.onStart() + val appUpdateInfoManager = AppUpdateManagerFactory.create(this) + val appUpdateInfoTask = appUpdateInfoManager.appUpdateInfo + + appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> + if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && + appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) + ) { + appUpdateInfoManager.startUpdateFlowForResult( + appUpdateInfo, + AppUpdateType.IMMEDIATE, + this, + 343 + ) + } + // log("update ${appUpdateInfo.packageName()} ${appUpdateInfo.availableVersionCode()}",) + }.addOnFailureListener { + it.printStackTrace() + } + } } diff --git a/app/src/main/java/com/mshdabiola/playnotepad/NotePadApplication.kt b/app/src/main/java/com/mshdabiola/playnotepad/NotePadApplication.kt index 26d700ad..d8045c68 100644 --- a/app/src/main/java/com/mshdabiola/playnotepad/NotePadApplication.kt +++ b/app/src/main/java/com/mshdabiola/playnotepad/NotePadApplication.kt @@ -3,11 +3,18 @@ package com.mshdabiola.playnotepad import android.app.Application import com.mshdabiola.worker.Saver import dagger.hilt.android.HiltAndroidApp +import timber.log.Timber @HiltAndroidApp class NotePadApplication : Application(){ override fun onCreate() { super.onCreate() Saver.initialize(applicationContext) + + if (packageName.contains("debug")) { + Timber.plant(Timber.DebugTree()) + Timber.e("log on app create") + } } + } diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt index ab24b48b..9c5bd6af 100644 --- a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt @@ -34,8 +34,8 @@ class AndroidApplicationConventionPlugin : Plugin { defaultConfig.targetSdk = 33 // compileSdkPreview = "UpsideDownCake" defaultConfig.minSdk = 24 - defaultConfig.versionName = "1.1.4" - defaultConfig.versionCode = 12 + defaultConfig.versionName = "1.1.5" + defaultConfig.versionCode = 13 defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" defaultConfig.vectorDrawables { diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt index f7645193..e759fcf7 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -18,8 +18,10 @@ import com.android.build.gradle.LibraryExtension import com.mshdabiola.app.configureKotlinAndroid import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalogsExtension import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.kotlin class AndroidLibraryConventionPlugin : Plugin { @@ -49,9 +51,11 @@ class AndroidLibraryConventionPlugin : Plugin { // force("org.objenesis:objenesis:2.6") // } // } + val libs = extensions.getByType().named("libs") dependencies { add("androidTestImplementation", kotlin("test")) add("testImplementation", kotlin("test")) + add("implementation", libs.findLibrary("timber").get()) } } } diff --git a/core/common/src/main/java/com/mshdabiola/common/ContentManager.kt b/core/common/src/main/java/com/mshdabiola/common/ContentManager.kt index 9c2dbd6f..22a17348 100644 --- a/core/common/src/main/java/com/mshdabiola/common/ContentManager.kt +++ b/core/common/src/main/java/com/mshdabiola/common/ContentManager.kt @@ -75,12 +75,12 @@ class ContentManager } } - fun dataFile(): File { + fun dataFile(drawingId:Long): File { val dir = File(context.filesDir.absolutePath + "/drawingfile") if (dir.exists().not()) { dir.mkdir() } - return File(dir, "data.json") + return File(dir, "data_$drawingId.json") } } diff --git a/core/database/schemas/com.mshdabiola.database.NoteDatabase/3.json b/core/database/schemas/com.mshdabiola.database.NoteDatabase/3.json new file mode 100644 index 00000000..9492f633 --- /dev/null +++ b/core/database/schemas/com.mshdabiola.database.NoteDatabase/3.json @@ -0,0 +1,322 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "8a44b7a696de82b667d9efea96f5377f", + "entities": [ + { + "tableName": "note_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT NOT NULL, `detail` TEXT NOT NULL, `editDate` INTEGER NOT NULL, `isCheck` INTEGER NOT NULL, `color` INTEGER NOT NULL, `background` INTEGER NOT NULL, `isPin` INTEGER NOT NULL, `reminder` INTEGER NOT NULL, `interval` INTEGER NOT NULL, `noteType` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "detail", + "columnName": "detail", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "editDate", + "columnName": "editDate", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCheck", + "columnName": "isCheck", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "background", + "columnName": "background", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPin", + "columnName": "isPin", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reminder", + "columnName": "reminder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interval", + "columnName": "interval", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "noteType", + "columnName": "noteType", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "note_voice_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `noteId` INTEGER NOT NULL, `voiceName` TEXT NOT NULL, PRIMARY KEY(`id`, `noteId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "noteId", + "columnName": "noteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "voiceName", + "columnName": "voiceName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id", + "noteId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "note_image_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `noteId` INTEGER NOT NULL, `isDrawing` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`, `noteId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "noteId", + "columnName": "noteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDrawing", + "columnName": "isDrawing", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id", + "noteId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "note_check_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `noteId` INTEGER NOT NULL, `content` TEXT NOT NULL, `isCheck` INTEGER NOT NULL, PRIMARY KEY(`id`, `noteId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "noteId", + "columnName": "noteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isCheck", + "columnName": "isCheck", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id", + "noteId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "note_label_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`noteId` INTEGER NOT NULL, `labelId` INTEGER NOT NULL, PRIMARY KEY(`noteId`, `labelId`))", + "fields": [ + { + "fieldPath": "noteId", + "columnName": "noteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "labelId", + "columnName": "labelId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "noteId", + "labelId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "label_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "path_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`imageId` INTEGER NOT NULL, `pathId` INTEGER NOT NULL, `color` INTEGER NOT NULL, `width` INTEGER NOT NULL, `join` INTEGER NOT NULL, `alpha` REAL NOT NULL, `cap` INTEGER NOT NULL, `paths` TEXT NOT NULL, PRIMARY KEY(`imageId`, `pathId`))", + "fields": [ + { + "fieldPath": "imageId", + "columnName": "imageId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pathId", + "columnName": "pathId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "width", + "columnName": "width", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "join", + "columnName": "join", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "alpha", + "columnName": "alpha", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "cap", + "columnName": "cap", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "paths", + "columnName": "paths", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "imageId", + "pathId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8a44b7a696de82b667d9efea96f5377f')" + ] + } +} \ No newline at end of file diff --git a/core/database/src/main/java/com/mshdabiola/database/NoteDatabase.kt b/core/database/src/main/java/com/mshdabiola/database/NoteDatabase.kt index 3da23e25..04bd5370 100644 --- a/core/database/src/main/java/com/mshdabiola/database/NoteDatabase.kt +++ b/core/database/src/main/java/com/mshdabiola/database/NoteDatabase.kt @@ -2,7 +2,10 @@ package com.mshdabiola.database import androidx.room.AutoMigration import androidx.room.Database +import androidx.room.DeleteColumn +import androidx.room.DeleteTable import androidx.room.RoomDatabase +import androidx.room.migration.AutoMigrationSpec import com.mshdabiola.database.dao.LabelDao import com.mshdabiola.database.dao.NoteCheckDao import com.mshdabiola.database.dao.NoteDao @@ -29,10 +32,11 @@ import com.mshdabiola.database.model.NoteVoiceEntity LabelEntity::class, DrawPathEntity::class, ], - version = 2, - autoMigrations = [ AutoMigration(1,2)] - - + version = 3, + autoMigrations = [ + AutoMigration(1,2), + AutoMigration(2 ,3,NoteDatabase.Migrate2to3::class) + ] ) abstract class NoteDatabase : RoomDatabase() { @@ -51,4 +55,6 @@ abstract class NoteDatabase : RoomDatabase() { abstract fun getNotePadDao(): NotepadDao abstract fun getPath(): PathDao + @DeleteColumn(tableName = "note_image_table", columnName = "imageName") + class Migrate2to3 : AutoMigrationSpec } diff --git a/core/database/src/main/java/com/mshdabiola/database/model/NoteImageEntity.kt b/core/database/src/main/java/com/mshdabiola/database/model/NoteImageEntity.kt index af2a9306..cd9133a8 100644 --- a/core/database/src/main/java/com/mshdabiola/database/model/NoteImageEntity.kt +++ b/core/database/src/main/java/com/mshdabiola/database/model/NoteImageEntity.kt @@ -12,11 +12,10 @@ import java.sql.Timestamp data class NoteImageEntity( val id: Long, val noteId: Long, - val imageName: String, val isDrawing: Boolean, @ColumnInfo(defaultValue = "0") val timestamp: Long ) -fun NoteImage.toNoteImageEntity() = NoteImageEntity(id, noteId, imageName, isDrawing,timestamp) -fun NoteImageEntity.toNoteImage() = NoteImage(id, noteId, imageName, isDrawing,timestamp) +fun NoteImage.toNoteImageEntity() = NoteImageEntity(id, noteId, isDrawing,timestamp) +fun NoteImageEntity.toNoteImage() = NoteImage(id, noteId,isDrawing,timestamp) diff --git a/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/NoteCard.kt b/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/NoteCard.kt index ac0c3712..e4df7ff5 100644 --- a/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/NoteCard.kt +++ b/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/NoteCard.kt @@ -134,7 +134,7 @@ fun NoteCard( modifier = Modifier .weight(1f) .height(100.dp), - model = it.imageName, + model = it.path, contentDescription = "", contentScale = ContentScale.Crop, ) diff --git a/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/NotifySneackerUi.kt b/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/NotifySneackerUi.kt new file mode 100644 index 00000000..9cabd208 --- /dev/null +++ b/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/NotifySneackerUi.kt @@ -0,0 +1,33 @@ +package com.mshdabiola.designsystem.component + +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.SnackbarResult +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import com.mshdabiola.designsystem.component.state.Notify +import kotlinx.collections.immutable.ImmutableList + + +@Composable +fun NotifySnacker(snackHostState: SnackbarHostState, notifys: ImmutableList) { + LaunchedEffect(key1 = notifys, block = { + if (notifys.isNotEmpty()) { + val first = notifys.first() + val result = snackHostState.showSnackbar( + message = first.message, + withDismissAction = first.withDismissAction, + actionLabel = first.label, + duration = if (first.isShort) SnackbarDuration.Short else SnackbarDuration.Long, + ) + when (result) { + SnackbarResult.ActionPerformed -> { + } + + SnackbarResult.Dismissed -> { + first.callback() + } + } + } + }) +} \ No newline at end of file diff --git a/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/state/NoteImageUiState.kt b/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/state/NoteImageUiState.kt index 3038d03b..fdc4d711 100644 --- a/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/state/NoteImageUiState.kt +++ b/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/state/NoteImageUiState.kt @@ -5,11 +5,11 @@ import com.mshdabiola.model.NoteImage data class NoteImageUiState( val id: Long, val noteId: Long, - val imageName: String, + val path: String, val isDrawing: Boolean, val timestamp: Long=0 ) -fun NoteImage.toNoteImageUiState() = NoteImageUiState(id, noteId, imageName, isDrawing,timestamp) +fun NoteImage.toNoteImageUiState(toPath:(Long)->String) = NoteImageUiState(id, noteId, toPath(id), isDrawing,timestamp) -fun NoteImageUiState.toNoteImage() = NoteImage(id, noteId, imageName, isDrawing,timestamp) +fun NoteImageUiState.toNoteImage() = NoteImage(id, noteId, isDrawing,timestamp) diff --git a/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/state/NotePadUiState.kt b/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/state/NotePadUiState.kt index 5597a33e..218241f1 100644 --- a/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/state/NotePadUiState.kt +++ b/core/designsystem/src/main/java/com/mshdabiola/designsystem/component/state/NotePadUiState.kt @@ -51,9 +51,12 @@ data class NotePadUiState( } } -fun NotePad.toNotePadUiState(list: List