diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d036848..5d2c1bf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,45 +15,45 @@ limitations under the License. --> - - - - - - - - - - - - - - - - - - - - - - - - - - - +xmlns:tools="http://schemas.android.com/tools"> + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/database/schemas/com.skydoves.pokedex.compose.core.database.PokedexDatabase/3.json b/core/database/schemas/com.skydoves.pokedex.compose.core.database.PokedexDatabase/3.json new file mode 100644 index 0000000..ca4d915 --- /dev/null +++ b/core/database/schemas/com.skydoves.pokedex.compose.core.database.PokedexDatabase/3.json @@ -0,0 +1,108 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "2ce2c0e046fdc408aab83eb7e475bf26", + "entities": [ + { + "tableName": "PokemonEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`page` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`name`))", + "fields": [ + { + "fieldPath": "page", + "columnName": "page", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "name" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "PokemonInfoEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `height` INTEGER NOT NULL, `weight` INTEGER NOT NULL, `experience` INTEGER NOT NULL, `types` TEXT NOT NULL, `exp` INTEGER NOT NULL, `stats` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "experience", + "columnName": "experience", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "types", + "columnName": "types", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "exp", + "columnName": "exp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stats", + "columnName": "stats", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "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, '2ce2c0e046fdc408aab83eb7e475bf26')" + ] + } +} \ No newline at end of file diff --git a/core/database/schemas/com.skydoves.pokedex.compose.core.database.PokedexDatabase/4.json b/core/database/schemas/com.skydoves.pokedex.compose.core.database.PokedexDatabase/4.json new file mode 100644 index 0000000..d24909b --- /dev/null +++ b/core/database/schemas/com.skydoves.pokedex.compose.core.database.PokedexDatabase/4.json @@ -0,0 +1,108 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "2ce2c0e046fdc408aab83eb7e475bf26", + "entities": [ + { + "tableName": "PokemonEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`page` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`name`))", + "fields": [ + { + "fieldPath": "page", + "columnName": "page", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "name" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "PokemonInfoEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `height` INTEGER NOT NULL, `weight` INTEGER NOT NULL, `experience` INTEGER NOT NULL, `types` TEXT NOT NULL, `exp` INTEGER NOT NULL, `stats` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "experience", + "columnName": "experience", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "types", + "columnName": "types", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "exp", + "columnName": "exp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stats", + "columnName": "stats", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "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, '2ce2c0e046fdc408aab83eb7e475bf26')" + ] + } +} \ No newline at end of file diff --git a/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/PokedexDatabase.kt b/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/PokedexDatabase.kt index 40143e5..1c5f3b3 100644 --- a/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/PokedexDatabase.kt +++ b/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/PokedexDatabase.kt @@ -24,10 +24,10 @@ import com.skydoves.pokedex.compose.core.database.entitiy.PokemonInfoEntity @Database( entities = [PokemonEntity::class, PokemonInfoEntity::class], - version = 2, + version = 4, exportSchema = true, ) -@TypeConverters(value = [TypeResponseConverter::class]) +@TypeConverters(value = [TypeResponseConverter::class, StatsResponseConverter::class]) abstract class PokedexDatabase : RoomDatabase() { abstract fun pokemonDao(): PokemonDao diff --git a/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/StatsResponseConverter.kt b/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/StatsResponseConverter.kt new file mode 100644 index 0000000..df23bbe --- /dev/null +++ b/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/StatsResponseConverter.kt @@ -0,0 +1,47 @@ +/* + * Designed and developed by 2024 skydoves (Jaewoong Eum) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.skydoves.pokedex.compose.core.database + +import androidx.room.ProvidedTypeConverter +import androidx.room.TypeConverter +import com.skydoves.pokedex.compose.core.model.PokemonInfo +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import javax.inject.Inject + +@ProvidedTypeConverter +class StatsResponseConverter @Inject constructor( + private val moshi: Moshi, +) { + + @TypeConverter + fun fromString(value: String): List? { + val listType = + Types.newParameterizedType(List::class.java, PokemonInfo.StatsResponse::class.java) + val adapter: JsonAdapter> = moshi.adapter(listType) + return adapter.fromJson(value) + } + + @TypeConverter + fun fromInfoType(type: List?): String { + val listType = + Types.newParameterizedType(List::class.java, PokemonInfo.StatsResponse::class.java) + val adapter: JsonAdapter> = moshi.adapter(listType) + return adapter.toJson(type) + } +} diff --git a/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/di/DatabaseModule.kt b/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/di/DatabaseModule.kt index 571cca4..4a3ae79 100644 --- a/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/di/DatabaseModule.kt +++ b/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/di/DatabaseModule.kt @@ -21,6 +21,7 @@ import androidx.room.Room import com.skydoves.pokedex.compose.core.database.PokedexDatabase import com.skydoves.pokedex.compose.core.database.PokemonDao import com.skydoves.pokedex.compose.core.database.PokemonInfoDao +import com.skydoves.pokedex.compose.core.database.StatsResponseConverter import com.skydoves.pokedex.compose.core.database.TypeResponseConverter import com.squareup.moshi.Moshi import dagger.Module @@ -45,11 +46,13 @@ internal object DatabaseModule { fun provideAppDatabase( application: Application, typeResponseConverter: TypeResponseConverter, + statsResponseConverter: StatsResponseConverter, ): PokedexDatabase { return Room .databaseBuilder(application, PokedexDatabase::class.java, "Pokedex.db") .fallbackToDestructiveMigration() .addTypeConverter(typeResponseConverter) + .addTypeConverter(statsResponseConverter) .build() } diff --git a/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/entitiy/PokemonInfoEntity.kt b/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/entitiy/PokemonInfoEntity.kt index e368f62..f242314 100644 --- a/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/entitiy/PokemonInfoEntity.kt +++ b/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/entitiy/PokemonInfoEntity.kt @@ -28,9 +28,6 @@ data class PokemonInfoEntity( val weight: Int, val experience: Int, val types: List, - val hp: Int, - val attack: Int, - val defense: Int, - val speed: Int, val exp: Int, + val stats: List, ) diff --git a/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/entitiy/mapper/PokemonInfoEntityMapper.kt b/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/entitiy/mapper/PokemonInfoEntityMapper.kt index a09a800..85ed405 100644 --- a/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/entitiy/mapper/PokemonInfoEntityMapper.kt +++ b/core/database/src/main/kotlin/com/skydoves/pokedex/compose/core/database/entitiy/mapper/PokemonInfoEntityMapper.kt @@ -29,11 +29,8 @@ object PokemonInfoEntityMapper : EntityMapper { weight = domain.weight, experience = domain.experience, types = domain.types, - hp = domain.hp, - attack = domain.attack, - defense = domain.defense, - speed = domain.speed, exp = domain.exp, + stats = domain.stats, ) } @@ -45,11 +42,8 @@ object PokemonInfoEntityMapper : EntityMapper { weight = entity.weight, experience = entity.experience, types = entity.types, - hp = entity.hp, - attack = entity.attack, - defense = entity.defense, - speed = entity.speed, exp = entity.exp, + stats = entity.stats, ) } } diff --git a/core/model/src/main/kotlin/com/skydoves/pokedex/compose/core/model/PokemonInfo.kt b/core/model/src/main/kotlin/com/skydoves/pokedex/compose/core/model/PokemonInfo.kt index ff9c6b7..f650120 100644 --- a/core/model/src/main/kotlin/com/skydoves/pokedex/compose/core/model/PokemonInfo.kt +++ b/core/model/src/main/kotlin/com/skydoves/pokedex/compose/core/model/PokemonInfo.kt @@ -31,12 +31,21 @@ data class PokemonInfo( @field:Json(name = "weight") val weight: Int, @field:Json(name = "base_experience") val experience: Int, @field:Json(name = "types") val types: List, - val hp: Int = Random.nextInt(MAX_HP), - val attack: Int = Random.nextInt(MAX_ATTACK), - val defense: Int = Random.nextInt(MAX_DEFENSE), - val speed: Int = Random.nextInt(MAX_SPEED), + @field:Json(name = "stats") val stats: List, val exp: Int = Random.nextInt(MAX_EXP), ) { + val hp: Int by lazy { + stats.firstOrNull { it.stat.name == "hp" }?.baseStat ?: Random.nextInt(MAX_HP) + } + val attack: Int by lazy { + stats.firstOrNull { it.stat.name == "attack" }?.baseStat ?: Random.nextInt(MAX_ATTACK) + } + val defense: Int by lazy { + stats.firstOrNull { it.stat.name == "defense" }?.baseStat ?: Random.nextInt(MAX_DEFENSE) + } + val speed: Int by lazy { + stats.firstOrNull { it.stat.name == "speed" }?.baseStat ?: Random.nextInt(MAX_SPEED) + } fun getIdString(): String = String.format("#%03d", id) fun getWeightString(): String = String.format("%.1f KG", weight.toFloat() / 10) @@ -53,6 +62,18 @@ data class PokemonInfo( @field:Json(name = "type") val type: Type, ) + @JsonClass(generateAdapter = true) + data class StatsResponse( + @field:Json(name = "base_stat") val baseStat: Int, + @field:Json(name = "effort") val effort: Int, + @field:Json(name = "stat") val stat: Stat, + ) + + @JsonClass(generateAdapter = true) + data class Stat( + @field:Json(name = "name") val name: String, + ) + @JsonClass(generateAdapter = true) data class Type( @field:Json(name = "name") val name: String, diff --git a/core/preview/src/main/kotlin/com/skydoves/pokedex/compose/core/preview/PreviewUtils.kt b/core/preview/src/main/kotlin/com/skydoves/pokedex/compose/core/preview/PreviewUtils.kt index b3d6038..51a0753 100644 --- a/core/preview/src/main/kotlin/com/skydoves/pokedex/compose/core/preview/PreviewUtils.kt +++ b/core/preview/src/main/kotlin/com/skydoves/pokedex/compose/core/preview/PreviewUtils.kt @@ -41,5 +41,11 @@ object PreviewUtils { PokemonInfo.TypeResponse(slot = 0, type = PokemonInfo.Type("grass")), PokemonInfo.TypeResponse(slot = 0, type = PokemonInfo.Type("poison")), ), + stats = listOf( + PokemonInfo.StatsResponse(baseStat = 20, effort = 0, stat = PokemonInfo.Stat("hp")), + PokemonInfo.StatsResponse(baseStat = 40, effort = 0, stat = PokemonInfo.Stat("attack")), + PokemonInfo.StatsResponse(baseStat = 60, effort = 0, stat = PokemonInfo.Stat("defense")), + PokemonInfo.StatsResponse(baseStat = 80, effort = 0, stat = PokemonInfo.Stat("attack")), + ), ) } diff --git a/core/test/src/main/kotlin/com/skydoves/pokedex/compose/core/test/MockUtil.kt b/core/test/src/main/kotlin/com/skydoves/pokedex/compose/core/test/MockUtil.kt index 0ec3fd6..d26cc5e 100644 --- a/core/test/src/main/kotlin/com/skydoves/pokedex/compose/core/test/MockUtil.kt +++ b/core/test/src/main/kotlin/com/skydoves/pokedex/compose/core/test/MockUtil.kt @@ -36,5 +36,6 @@ object MockUtil { weight = 69, experience = 60, types = emptyList(), + stats = emptyList(), ) }