Kaynağa Gözat

added room database

sirekanian 3 yıl önce
ebeveyn
işleme
5822c06ae7

+ 15 - 0
app/build.gradle

@@ -1,6 +1,7 @@
 plugins {
     id 'com.android.application'
     id 'org.jetbrains.kotlin.android'
+    id 'org.jetbrains.kotlin.kapt'
 }
 
 apply(from: 'version.gradle')
@@ -17,6 +18,11 @@ android {
         vectorDrawables {
             useSupportLibrary true
         }
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments += ["room.schemaLocation": "$projectDir/schemas".toString()]
+            }
+        }
     }
     buildTypes {
         debug {
@@ -49,8 +55,17 @@ android {
 }
 
 dependencies {
+
+    // compose
     implementation "androidx.compose.material:material:$composeLibraryVersion"
     implementation "androidx.activity:activity-compose:1.6.0"
     implementation "com.google.accompanist:accompanist-pager:0.25.1"
+
+    // room
+    implementation "androidx.room:room-ktx:2.4.3"
+    kapt "androidx.room:room-compiler:2.4.3"
+
+    // coil
     implementation "io.coil-kt:coil-compose:2.2.1"
+
 }

+ 40 - 0
app/schemas/com.sirekanian.spacetime.data.local.Database/1.json

@@ -0,0 +1,40 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 1,
+    "identityHash": "e84ca83f2417831753493017919aa6f7",
+    "entities": [
+      {
+        "tableName": "PageEntity",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "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, 'e84ca83f2417831753493017919aa6f7')"
+    ]
+  }
+}

+ 10 - 1
app/src/main/java/com/sirekanian/spacetime/App.kt

@@ -1,11 +1,20 @@
 package com.sirekanian.spacetime
 
 import android.app.Application
+import androidx.room.Room
+import com.sirekanian.spacetime.data.DefaultDataCallback
 import com.sirekanian.spacetime.data.Repository
 import com.sirekanian.spacetime.data.RepositoryImpl
+import com.sirekanian.spacetime.data.local.Database
 
 class App : Application() {
 
-    val repository: Repository by lazy { RepositoryImpl() }
+    val repository: Repository by lazy {
+        RepositoryImpl(
+            Room.databaseBuilder(this, Database::class.java, "database")
+                .addCallback(DefaultDataCallback())
+                .build().getPageDao()
+        )
+    }
 
 }

+ 32 - 5
app/src/main/java/com/sirekanian/spacetime/MainPresenter.kt

@@ -2,24 +2,51 @@ package com.sirekanian.spacetime
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.platform.LocalContext
 import com.sirekanian.spacetime.data.Repository
 import com.sirekanian.spacetime.ext.app
+import com.sirekanian.spacetime.model.GalleryPage
+import com.sirekanian.spacetime.model.ImagePage
+import com.sirekanian.spacetime.model.Page
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
 
 @Composable
 fun rememberMainPresenter(): MainPresenter {
     val context = LocalContext.current
-    return remember { MainPresenterImpl(context.app().repository) }
+    val scope = rememberCoroutineScope()
+    return remember { MainPresenterImpl(context.app().repository, scope) }
 }
 
 interface MainPresenter {
 
-    val state: MainState
+    fun observePages(): Flow<List<Page>>
+    fun addPage(page: ImagePage)
+    fun removePage(page: ImagePage)
 
 }
 
-class MainPresenterImpl(private val repository: Repository) : MainPresenter {
-
-    override val state = MainState()
+class MainPresenterImpl(
+    private val repository: Repository,
+    private val scope: CoroutineScope,
+) : MainPresenter {
+
+    override fun observePages(): Flow<List<Page>> =
+        repository.observePages().map { it.plus(GalleryPage) }
+
+    override fun addPage(page: ImagePage) {
+        scope.launch {
+            repository.addPage(page)
+        }
+    }
+
+    override fun removePage(page: ImagePage) {
+        scope.launch {
+            repository.removePage(page)
+        }
+    }
 
 }

+ 13 - 5
app/src/main/java/com/sirekanian/spacetime/MainScreen.kt

@@ -4,24 +4,32 @@ import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.asPaddingValues
 import androidx.compose.foundation.layout.systemBars
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
 import com.google.accompanist.pager.ExperimentalPagerApi
 import com.google.accompanist.pager.HorizontalPager
+import com.sirekanian.spacetime.model.GalleryPage
+import com.sirekanian.spacetime.model.ImagePage
+import com.sirekanian.spacetime.model.Page
 import com.sirekanian.spacetime.ui.GalleryPageContent
 import com.sirekanian.spacetime.ui.ImagePageContent
 
 @Composable
 @ExperimentalPagerApi
 fun MainScreen(presenter: MainPresenter) {
-    val state = presenter.state
-    val pages = state.getPages()
+    val pages by presenter.observePages().collectAsState(listOf<Page>())
     val insets = WindowInsets.systemBars.asPaddingValues()
     HorizontalPager(
         count = pages.size,
-        key = { pages[it].uuid },
+        key = { pages[it].id },
     ) { index ->
         when (val page = pages[index]) {
-            is ImagePage -> ImagePageContent(insets, state, page, index)
-            GalleryPage -> GalleryPageContent(insets, state)
+            is ImagePage -> {
+                ImagePageContent(insets, page, onDelete = { presenter.removePage(page) })
+            }
+            GalleryPage -> {
+                GalleryPageContent(insets, onSelect = { presenter.addPage(ImagePage(0, it)) })
+            }
         }
     }
 }

+ 0 - 25
app/src/main/java/com/sirekanian/spacetime/MainState.kt

@@ -1,25 +0,0 @@
-package com.sirekanian.spacetime
-
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import com.sirekanian.spacetime.data.URLS
-
-class MainState {
-
-    private var list by mutableStateOf(
-        URLS.take(2).map(::ImagePage)
-    )
-
-    fun getPages(): List<Page> =
-        list.plus(GalleryPage)
-
-    fun addPage(name: String) {
-        list = list.plus(ImagePage(name))
-    }
-
-    fun removePage(index: Int) {
-        list = list.filterIndexed { i, _ -> i != index }
-    }
-
-}

+ 0 - 11
app/src/main/java/com/sirekanian/spacetime/Page.kt

@@ -1,11 +0,0 @@
-package com.sirekanian.spacetime
-
-import java.util.*
-
-sealed class Page {
-    val uuid: String = UUID.randomUUID().toString()
-}
-
-class ImagePage(val name: String) : Page()
-
-object GalleryPage : Page()

+ 12 - 0
app/src/main/java/com/sirekanian/spacetime/data/DefaultDataCallback.kt

@@ -0,0 +1,12 @@
+package com.sirekanian.spacetime.data
+
+import androidx.room.RoomDatabase
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class DefaultDataCallback : RoomDatabase.Callback() {
+    override fun onCreate(db: SupportSQLiteDatabase) {
+        URLS.take(2).forEach {
+            db.execSQL("INSERT INTO PageEntity (name) VALUES (?)", arrayOf(it))
+        }
+    }
+}

+ 24 - 1
app/src/main/java/com/sirekanian/spacetime/data/Repository.kt

@@ -1,7 +1,30 @@
 package com.sirekanian.spacetime.data
 
+import com.sirekanian.spacetime.data.local.PageDao
+import com.sirekanian.spacetime.data.local.PageEntity
+import com.sirekanian.spacetime.model.ImagePage
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
 interface Repository {
+
+    fun observePages(): Flow<List<ImagePage>>
+    suspend fun addPage(page: ImagePage)
+    suspend fun removePage(page: ImagePage)
+
 }
 
-class RepositoryImpl : Repository {
+class RepositoryImpl(private val dao: PageDao) : Repository {
+
+    override fun observePages(): Flow<List<ImagePage>> =
+        dao.observe().map { it.map(PageEntity::toModel) }
+
+    override suspend fun addPage(page: ImagePage) {
+        dao.insert(page.toEntity())
+    }
+
+    override suspend fun removePage(page: ImagePage) {
+        dao.delete(page.toEntity())
+    }
+
 }

+ 11 - 0
app/src/main/java/com/sirekanian/spacetime/data/local/Database.kt

@@ -0,0 +1,11 @@
+package com.sirekanian.spacetime.data.local
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+
+@Database(entities = [PageEntity::class], version = 1)
+abstract class Database : RoomDatabase() {
+
+    abstract fun getPageDao(): PageDao
+
+}

+ 21 - 0
app/src/main/java/com/sirekanian/spacetime/data/local/PageDao.kt

@@ -0,0 +1,21 @@
+package com.sirekanian.spacetime.data.local
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.Query
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface PageDao {
+
+    @Query("SELECT * FROM PageEntity")
+    fun observe(): Flow<List<PageEntity>>
+
+    @Insert
+    suspend fun insert(page: PageEntity)
+
+    @Delete
+    suspend fun delete(page: PageEntity)
+
+}

+ 14 - 0
app/src/main/java/com/sirekanian/spacetime/data/local/PageEntity.kt

@@ -0,0 +1,14 @@
+package com.sirekanian.spacetime.data.local
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.sirekanian.spacetime.model.ImagePage
+
+@Entity
+class PageEntity(
+    @PrimaryKey(autoGenerate = true)
+    val id: Int,
+    val name: String,
+) {
+    fun toModel() = ImagePage(id, name)
+}

+ 11 - 0
app/src/main/java/com/sirekanian/spacetime/model/Page.kt

@@ -0,0 +1,11 @@
+package com.sirekanian.spacetime.model
+
+import com.sirekanian.spacetime.data.local.PageEntity
+
+sealed class Page(val id: Int)
+
+class ImagePage(id: Int, val name: String) : Page(id) {
+    fun toEntity() = PageEntity(id, name)
+}
+
+object GalleryPage : Page(-1)

+ 2 - 3
app/src/main/java/com/sirekanian/spacetime/ui/GalleryPageContent.kt

@@ -13,11 +13,10 @@ import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.unit.dp
 import coil.compose.AsyncImage
 import coil.request.ImageRequest
-import com.sirekanian.spacetime.MainState
 import com.sirekanian.spacetime.data.URLS
 
 @Composable
-fun GalleryPageContent(insets: PaddingValues, state: MainState) {
+fun GalleryPageContent(insets: PaddingValues, onSelect: (String) -> Unit) {
     LazyVerticalGrid(
         columns = GridCells.Adaptive(100.dp),
         modifier = Modifier.fillMaxSize(),
@@ -33,7 +32,7 @@ fun GalleryPageContent(insets: PaddingValues, state: MainState) {
                 contentDescription = null,
                 modifier = Modifier
                     .aspectRatio(1f)
-                    .clickable { state.addPage(url) },
+                    .clickable { onSelect(url) },
                 contentScale = ContentScale.Crop,
             )
         }

+ 3 - 4
app/src/main/java/com/sirekanian/spacetime/ui/ImagePageContent.kt

@@ -11,13 +11,12 @@ import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.platform.LocalContext
 import coil.compose.AsyncImage
 import coil.request.ImageRequest
-import com.sirekanian.spacetime.ImagePage
-import com.sirekanian.spacetime.MainState
 import com.sirekanian.spacetime.ext.DefaultAnimatedVisibility
 import com.sirekanian.spacetime.ext.VectorIconButton
+import com.sirekanian.spacetime.model.ImagePage
 
 @Composable
-fun ImagePageContent(insets: PaddingValues, state: MainState, page: ImagePage, index: Int) {
+fun ImagePageContent(insets: PaddingValues, page: ImagePage, onDelete: () -> Unit) {
     AsyncImage(
         model = ImageRequest.Builder(LocalContext.current)
             .data(page.name)
@@ -38,7 +37,7 @@ fun ImagePageContent(insets: PaddingValues, state: MainState, page: ImagePage, i
         Row(Modifier.padding(insets)) {
             VectorIconButton(Icons.Default.ArrowBack, onClick = { isEditMode = false })
             Spacer(Modifier.weight(1f))
-            VectorIconButton(Icons.Default.Delete, onClick = { state.removePage(index) })
+            VectorIconButton(Icons.Default.Delete, onClick = { onDelete() })
         }
     }
 }