Fixed: Multiple issues with JSON LanguageHistory(fixed with moving it in Room Database) + Room version migration 2 → 3.
This commit is contained in:
parent
ce7fef8dc8
commit
562c06b0c4
19 changed files with 373 additions and 89 deletions
|
|
@ -0,0 +1,187 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 3,
|
||||||
|
"identityHash": "8b6e8d2712148ad1c165aa984ffcf35b",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "BookEntity",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `author` TEXT, `description` TEXT, `textPath` TEXT NOT NULL, `filePath` TEXT NOT NULL, `scrollIndex` INTEGER NOT NULL, `scrollOffset` INTEGER NOT NULL, `progress` REAL NOT NULL, `image` TEXT, `category` TEXT NOT NULL, `enableTranslator` INTEGER NOT NULL DEFAULT false, `translateFrom` TEXT NOT NULL DEFAULT 'auto', `translateTo` TEXT NOT NULL DEFAULT 'en', `doubleClickTranslation` INTEGER NOT NULL DEFAULT true, `translateWhenOpen` INTEGER NOT NULL DEFAULT false)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "author",
|
||||||
|
"columnName": "author",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "description",
|
||||||
|
"columnName": "description",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "textPath",
|
||||||
|
"columnName": "textPath",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "filePath",
|
||||||
|
"columnName": "filePath",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "scrollIndex",
|
||||||
|
"columnName": "scrollIndex",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "scrollOffset",
|
||||||
|
"columnName": "scrollOffset",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "progress",
|
||||||
|
"columnName": "progress",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "image",
|
||||||
|
"columnName": "image",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "category",
|
||||||
|
"columnName": "category",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "enableTranslator",
|
||||||
|
"columnName": "enableTranslator",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "translateFrom",
|
||||||
|
"columnName": "translateFrom",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "'auto'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "translateTo",
|
||||||
|
"columnName": "translateTo",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "'en'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "doubleClickTranslation",
|
||||||
|
"columnName": "doubleClickTranslation",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "true"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "translateWhenOpen",
|
||||||
|
"columnName": "translateWhenOpen",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "HistoryEntity",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `bookId` INTEGER NOT NULL, `time` INTEGER NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bookId",
|
||||||
|
"columnName": "bookId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "time",
|
||||||
|
"columnName": "time",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "LanguageHistoryEntity",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`languageCode` TEXT NOT NULL, `order` INTEGER NOT NULL, PRIMARY KEY(`languageCode`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "languageCode",
|
||||||
|
"columnName": "languageCode",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "order",
|
||||||
|
"columnName": "order",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"languageCode"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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, '8b6e8d2712148ad1c165aa984ffcf35b')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ import retrofit2.Retrofit
|
||||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||||
import ua.acclorite.book_story.data.local.room.BookDao
|
import ua.acclorite.book_story.data.local.room.BookDao
|
||||||
import ua.acclorite.book_story.data.local.room.BookDatabase
|
import ua.acclorite.book_story.data.local.room.BookDatabase
|
||||||
|
import ua.acclorite.book_story.data.local.room.DatabaseHelper
|
||||||
import ua.acclorite.book_story.data.remote.GithubAPI
|
import ua.acclorite.book_story.data.remote.GithubAPI
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
@ -43,7 +44,13 @@ object AppModule {
|
||||||
app,
|
app,
|
||||||
BookDatabase::class.java,
|
BookDatabase::class.java,
|
||||||
"book_db"
|
"book_db"
|
||||||
).allowMainThreadQueries().build().dao
|
)
|
||||||
|
.addMigrations(
|
||||||
|
DatabaseHelper.MIGRATION_2_3, // creates LanguageHistoryEntity table(if does not exist)
|
||||||
|
)
|
||||||
|
.allowMainThreadQueries()
|
||||||
|
.build()
|
||||||
|
.dao
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ import ua.acclorite.book_story.data.mapper.book.BookMapper
|
||||||
import ua.acclorite.book_story.data.mapper.book.BookMapperImpl
|
import ua.acclorite.book_story.data.mapper.book.BookMapperImpl
|
||||||
import ua.acclorite.book_story.data.mapper.history.HistoryMapper
|
import ua.acclorite.book_story.data.mapper.history.HistoryMapper
|
||||||
import ua.acclorite.book_story.data.mapper.history.HistoryMapperImpl
|
import ua.acclorite.book_story.data.mapper.history.HistoryMapperImpl
|
||||||
|
import ua.acclorite.book_story.data.mapper.language_history.LanguageHistoryMapper
|
||||||
|
import ua.acclorite.book_story.data.mapper.language_history.LanguageHistoryMapperImpl
|
||||||
import ua.acclorite.book_story.data.parser.FileParser
|
import ua.acclorite.book_story.data.parser.FileParser
|
||||||
import ua.acclorite.book_story.data.parser.FileParserImpl
|
import ua.acclorite.book_story.data.parser.FileParserImpl
|
||||||
import ua.acclorite.book_story.data.parser.TextParser
|
import ua.acclorite.book_story.data.parser.TextParser
|
||||||
|
|
@ -47,6 +49,12 @@ abstract class RepositoryModule {
|
||||||
historyMapperImpl: HistoryMapperImpl
|
historyMapperImpl: HistoryMapperImpl
|
||||||
): HistoryMapper
|
): HistoryMapper
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
abstract fun bindLanguageHistoryMapper(
|
||||||
|
languageHistoryMapperImpl: LanguageHistoryMapperImpl
|
||||||
|
): LanguageHistoryMapper
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@Singleton
|
@Singleton
|
||||||
abstract fun bindFileParser(
|
abstract fun bindFileParser(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package ua.acclorite.book_story.data.local.dto
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class LanguageHistoryEntity(
|
||||||
|
@PrimaryKey(false) val languageCode: String,
|
||||||
|
val order: Int
|
||||||
|
)
|
||||||
|
|
@ -6,8 +6,10 @@ import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
|
import androidx.room.Upsert
|
||||||
import ua.acclorite.book_story.data.local.dto.BookEntity
|
import ua.acclorite.book_story.data.local.dto.BookEntity
|
||||||
import ua.acclorite.book_story.data.local.dto.HistoryEntity
|
import ua.acclorite.book_story.data.local.dto.HistoryEntity
|
||||||
|
import ua.acclorite.book_story.data.local.dto.LanguageHistoryEntity
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface BookDao {
|
interface BookDao {
|
||||||
|
|
@ -25,9 +27,7 @@ interface BookDao {
|
||||||
)
|
)
|
||||||
suspend fun searchBooks(query: String): List<BookEntity>
|
suspend fun searchBooks(query: String): List<BookEntity>
|
||||||
|
|
||||||
@Query(
|
@Query("SELECT * FROM bookentity WHERE id=:id")
|
||||||
"SELECT * FROM bookentity WHERE id=:id"
|
|
||||||
)
|
|
||||||
suspend fun findBookById(id: Int): BookEntity
|
suspend fun findBookById(id: Int): BookEntity
|
||||||
|
|
||||||
@Query("SELECT * FROM bookentity WHERE id IN (:ids)")
|
@Query("SELECT * FROM bookentity WHERE id IN (:ids)")
|
||||||
|
|
@ -59,4 +59,11 @@ interface BookDao {
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun deleteHistory(history: List<HistoryEntity>)
|
suspend fun deleteHistory(history: List<HistoryEntity>)
|
||||||
|
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
suspend fun updateLanguageHistory(history: List<LanguageHistoryEntity>)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM languagehistoryentity")
|
||||||
|
suspend fun getLanguageHistory(): List<LanguageHistoryEntity>
|
||||||
}
|
}
|
||||||
|
|
@ -3,20 +3,40 @@ package ua.acclorite.book_story.data.local.room
|
||||||
import androidx.room.AutoMigration
|
import androidx.room.AutoMigration
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
import ua.acclorite.book_story.data.local.dto.BookEntity
|
import ua.acclorite.book_story.data.local.dto.BookEntity
|
||||||
import ua.acclorite.book_story.data.local.dto.HistoryEntity
|
import ua.acclorite.book_story.data.local.dto.HistoryEntity
|
||||||
|
import ua.acclorite.book_story.data.local.dto.LanguageHistoryEntity
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [
|
entities = [
|
||||||
BookEntity::class,
|
BookEntity::class,
|
||||||
HistoryEntity::class
|
HistoryEntity::class,
|
||||||
|
LanguageHistoryEntity::class
|
||||||
],
|
],
|
||||||
version = 2,
|
version = 3,
|
||||||
autoMigrations = [
|
autoMigrations = [
|
||||||
AutoMigration(1, 2)
|
AutoMigration(1, 2),
|
||||||
|
AutoMigration(2, 3),
|
||||||
],
|
],
|
||||||
exportSchema = true
|
exportSchema = true
|
||||||
)
|
)
|
||||||
abstract class BookDatabase : RoomDatabase() {
|
abstract class BookDatabase : RoomDatabase() {
|
||||||
abstract val dao: BookDao
|
abstract val dao: BookDao
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object DatabaseHelper {
|
||||||
|
|
||||||
|
val MIGRATION_2_3 = object : Migration(2, 3) {
|
||||||
|
override fun migrate(db: SupportSQLiteDatabase) {
|
||||||
|
db.execSQL(
|
||||||
|
"CREATE TABLE IF NOT EXISTS `LanguageHistoryEntity` (" +
|
||||||
|
"`languageCode` TEXT NOT NULL," +
|
||||||
|
" `order` INTEGER NOT NULL," +
|
||||||
|
" PRIMARY KEY(`languageCode`)" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package ua.acclorite.book_story.data.mapper.language_history
|
||||||
|
|
||||||
|
import ua.acclorite.book_story.data.local.dto.LanguageHistoryEntity
|
||||||
|
import ua.acclorite.book_story.domain.model.LanguageHistory
|
||||||
|
|
||||||
|
interface LanguageHistoryMapper {
|
||||||
|
suspend fun toLanguageHistoryEntity(languageHistory: LanguageHistory): LanguageHistoryEntity
|
||||||
|
|
||||||
|
suspend fun toLanguageHistory(languageHistoryEntity: LanguageHistoryEntity): LanguageHistory
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package ua.acclorite.book_story.data.mapper.language_history
|
||||||
|
|
||||||
|
import ua.acclorite.book_story.data.local.dto.LanguageHistoryEntity
|
||||||
|
import ua.acclorite.book_story.domain.model.LanguageHistory
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LanguageHistoryMapperImpl @Inject constructor() : LanguageHistoryMapper {
|
||||||
|
|
||||||
|
override suspend fun toLanguageHistoryEntity(languageHistory: LanguageHistory): LanguageHistoryEntity {
|
||||||
|
return LanguageHistoryEntity(
|
||||||
|
languageCode = languageHistory.languageCode,
|
||||||
|
order = languageHistory.order
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun toLanguageHistory(languageHistoryEntity: LanguageHistoryEntity): LanguageHistory {
|
||||||
|
return LanguageHistory(
|
||||||
|
languageCode = languageHistoryEntity.languageCode,
|
||||||
|
order = languageHistoryEntity.order
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,12 +29,14 @@ import ua.acclorite.book_story.data.local.notification.UpdatesNotificationServic
|
||||||
import ua.acclorite.book_story.data.local.room.BookDao
|
import ua.acclorite.book_story.data.local.room.BookDao
|
||||||
import ua.acclorite.book_story.data.mapper.book.BookMapper
|
import ua.acclorite.book_story.data.mapper.book.BookMapper
|
||||||
import ua.acclorite.book_story.data.mapper.history.HistoryMapper
|
import ua.acclorite.book_story.data.mapper.history.HistoryMapper
|
||||||
|
import ua.acclorite.book_story.data.mapper.language_history.LanguageHistoryMapper
|
||||||
import ua.acclorite.book_story.data.parser.FileParser
|
import ua.acclorite.book_story.data.parser.FileParser
|
||||||
import ua.acclorite.book_story.data.parser.TextParser
|
import ua.acclorite.book_story.data.parser.TextParser
|
||||||
import ua.acclorite.book_story.data.remote.GithubAPI
|
import ua.acclorite.book_story.data.remote.GithubAPI
|
||||||
import ua.acclorite.book_story.data.remote.dto.LatestReleaseInfo
|
import ua.acclorite.book_story.data.remote.dto.LatestReleaseInfo
|
||||||
import ua.acclorite.book_story.domain.model.Book
|
import ua.acclorite.book_story.domain.model.Book
|
||||||
import ua.acclorite.book_story.domain.model.History
|
import ua.acclorite.book_story.domain.model.History
|
||||||
|
import ua.acclorite.book_story.domain.model.LanguageHistory
|
||||||
import ua.acclorite.book_story.domain.model.NullableBook
|
import ua.acclorite.book_story.domain.model.NullableBook
|
||||||
import ua.acclorite.book_story.domain.repository.BookRepository
|
import ua.acclorite.book_story.domain.repository.BookRepository
|
||||||
import ua.acclorite.book_story.domain.util.Constants
|
import ua.acclorite.book_story.domain.util.Constants
|
||||||
|
|
@ -67,6 +69,7 @@ class BookRepositoryImpl @Inject constructor(
|
||||||
|
|
||||||
private val bookMapper: BookMapper,
|
private val bookMapper: BookMapper,
|
||||||
private val historyMapper: HistoryMapper,
|
private val historyMapper: HistoryMapper,
|
||||||
|
private val languageHistoryMapper: LanguageHistoryMapper,
|
||||||
|
|
||||||
private val fileParser: FileParser,
|
private val fileParser: FileParser,
|
||||||
private val textParser: TextParser
|
private val textParser: TextParser
|
||||||
|
|
@ -816,6 +819,20 @@ class BookRepositoryImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getLanguageHistory(): List<LanguageHistory> {
|
||||||
|
return database.getLanguageHistory().map {
|
||||||
|
languageHistoryMapper.toLanguageHistory(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun updateLanguageHistory(history: List<LanguageHistory>) {
|
||||||
|
database.updateLanguageHistory(
|
||||||
|
history.map {
|
||||||
|
languageHistoryMapper.toLanguageHistoryEntity(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,9 @@
|
||||||
package ua.acclorite.book_story.domain.model
|
package ua.acclorite.book_story.domain.model
|
||||||
|
|
||||||
import android.os.Parcelable
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.google.errorprone.annotations.Keep
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@Keep
|
|
||||||
data class LanguageHistory(
|
data class LanguageHistory(
|
||||||
val id: Int,
|
val languageCode: String,
|
||||||
val languageCode: String
|
val order: Int
|
||||||
) : Parcelable
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.Flow
|
||||||
import ua.acclorite.book_story.data.remote.dto.LatestReleaseInfo
|
import ua.acclorite.book_story.data.remote.dto.LatestReleaseInfo
|
||||||
import ua.acclorite.book_story.domain.model.Book
|
import ua.acclorite.book_story.domain.model.Book
|
||||||
import ua.acclorite.book_story.domain.model.History
|
import ua.acclorite.book_story.domain.model.History
|
||||||
|
import ua.acclorite.book_story.domain.model.LanguageHistory
|
||||||
import ua.acclorite.book_story.domain.model.NullableBook
|
import ua.acclorite.book_story.domain.model.NullableBook
|
||||||
import ua.acclorite.book_story.domain.util.CoverImage
|
import ua.acclorite.book_story.domain.util.CoverImage
|
||||||
import ua.acclorite.book_story.domain.util.LanguageCode
|
import ua.acclorite.book_story.domain.util.LanguageCode
|
||||||
|
|
@ -105,4 +106,9 @@ interface BookRepository {
|
||||||
targetLanguage: LanguageCode,
|
targetLanguage: LanguageCode,
|
||||||
text: String
|
text: String
|
||||||
): Resource<String>
|
): Resource<String>
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun getLanguageHistory(): List<LanguageHistory>
|
||||||
|
|
||||||
|
suspend fun updateLanguageHistory(history: List<LanguageHistory>)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package ua.acclorite.book_story.domain.use_case
|
||||||
|
|
||||||
|
import ua.acclorite.book_story.domain.model.LanguageHistory
|
||||||
|
import ua.acclorite.book_story.domain.repository.BookRepository
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetLanguageHistory @Inject constructor(private val repository: BookRepository) {
|
||||||
|
|
||||||
|
suspend fun execute(): List<LanguageHistory> {
|
||||||
|
return repository.getLanguageHistory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package ua.acclorite.book_story.domain.use_case
|
||||||
|
|
||||||
|
import ua.acclorite.book_story.domain.model.LanguageHistory
|
||||||
|
import ua.acclorite.book_story.domain.repository.BookRepository
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class UpdateLanguageHistory @Inject constructor(private val repository: BookRepository) {
|
||||||
|
|
||||||
|
suspend fun execute(history: List<LanguageHistory>) {
|
||||||
|
repository.updateLanguageHistory(history)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,7 +32,6 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
import ua.acclorite.book_story.domain.model.LanguageHistory
|
|
||||||
import ua.acclorite.book_story.presentation.components.CategoryTitle
|
import ua.acclorite.book_story.presentation.components.CategoryTitle
|
||||||
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
|
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
|
||||||
import ua.acclorite.book_story.presentation.components.CustomBottomSheet
|
import ua.acclorite.book_story.presentation.components.CustomBottomSheet
|
||||||
|
|
@ -45,8 +44,6 @@ import ua.acclorite.book_story.presentation.components.translator_language.compo
|
||||||
import ua.acclorite.book_story.presentation.components.translator_language.data.TranslatorLanguageEvent
|
import ua.acclorite.book_story.presentation.components.translator_language.data.TranslatorLanguageEvent
|
||||||
import ua.acclorite.book_story.presentation.components.translator_language.data.TranslatorLanguageState
|
import ua.acclorite.book_story.presentation.components.translator_language.data.TranslatorLanguageState
|
||||||
import ua.acclorite.book_story.presentation.components.translator_language.data.TranslatorLanguageViewModel
|
import ua.acclorite.book_story.presentation.components.translator_language.data.TranslatorLanguageViewModel
|
||||||
import ua.acclorite.book_story.presentation.data.MainEvent
|
|
||||||
import ua.acclorite.book_story.presentation.data.MainViewModel
|
|
||||||
import ua.acclorite.book_story.presentation.ui.DefaultTransition
|
import ua.acclorite.book_story.presentation.ui.DefaultTransition
|
||||||
import ua.acclorite.book_story.presentation.ui.Transitions
|
import ua.acclorite.book_story.presentation.ui.Transitions
|
||||||
|
|
||||||
|
|
@ -69,11 +66,8 @@ fun TranslatorLanguageBottomSheet(
|
||||||
onSelect: (String, String) -> Unit
|
onSelect: (String, String) -> Unit
|
||||||
) {
|
) {
|
||||||
val translatorLanguageViewModel: TranslatorLanguageViewModel = hiltViewModel()
|
val translatorLanguageViewModel: TranslatorLanguageViewModel = hiltViewModel()
|
||||||
val mainViewModel: MainViewModel = hiltViewModel()
|
|
||||||
|
|
||||||
val state = translatorLanguageViewModel.state.collectAsState()
|
val state = translatorLanguageViewModel.state.collectAsState()
|
||||||
val mainState = mainViewModel.state.collectAsState()
|
|
||||||
|
|
||||||
val loading = remember { mutableStateOf(true) }
|
val loading = remember { mutableStateOf(true) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
|
|
@ -81,7 +75,6 @@ fun TranslatorLanguageBottomSheet(
|
||||||
selectedLanguage = selectedLanguage,
|
selectedLanguage = selectedLanguage,
|
||||||
unselectedLanguage = unselectedLanguage,
|
unselectedLanguage = unselectedLanguage,
|
||||||
translateFromSelecting = translateFromSelecting,
|
translateFromSelecting = translateFromSelecting,
|
||||||
history = mainState.value.translatorLanguageHistory!!,
|
|
||||||
loaded = {
|
loaded = {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
@ -96,24 +89,9 @@ fun TranslatorLanguageBottomSheet(
|
||||||
val languageToChoose = if (fromSelecting) from else to
|
val languageToChoose = if (fromSelecting) from else to
|
||||||
|
|
||||||
if (languageToChoose != "auto") {
|
if (languageToChoose != "auto") {
|
||||||
val currentHistory = mainState.value.translatorLanguageHistory!!.toMutableList()
|
translatorLanguageViewModel.onEvent(
|
||||||
val langToAdd = state.value.languageToSelect
|
TranslatorLanguageEvent.OnUpdateLanguageHistory(
|
||||||
val newHistory = mutableListOf<LanguageHistory>()
|
languageToChoose
|
||||||
val indexOfItem = currentHistory.indexOfFirst { it.languageCode == langToAdd }
|
|
||||||
|
|
||||||
if (indexOfItem != -1) {
|
|
||||||
currentHistory.removeAt(indexOfItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentHistory.add(0, LanguageHistory(0, langToAdd))
|
|
||||||
|
|
||||||
currentHistory.sortedBy { it.id }.forEachIndexed { index, languageHistory ->
|
|
||||||
newHistory.add(LanguageHistory(index, languageHistory.languageCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
mainViewModel.onEvent(
|
|
||||||
MainEvent.OnChangeTranslatorLanguageHistory(
|
|
||||||
newHistory
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +135,7 @@ private fun TranslatorLanguageBottomSheetContent(
|
||||||
.fillMaxHeight(),
|
.fillMaxHeight(),
|
||||||
dragHandle = {},
|
dragHandle = {},
|
||||||
shape = RoundedCornerShape(0),
|
shape = RoundedCornerShape(0),
|
||||||
scrimColor = Color.Transparent,
|
scrimColor = MaterialTheme.colorScheme.surface,
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
onDismiss()
|
onDismiss()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package ua.acclorite.book_story.presentation.components.translator_language.data
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import ua.acclorite.book_story.domain.model.Language
|
import ua.acclorite.book_story.domain.model.Language
|
||||||
|
import ua.acclorite.book_story.domain.util.LanguageCode
|
||||||
import ua.acclorite.book_story.domain.util.UIText
|
import ua.acclorite.book_story.domain.util.UIText
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|
@ -36,4 +37,8 @@ sealed class TranslatorLanguageEvent {
|
||||||
val languageCode: String,
|
val languageCode: String,
|
||||||
val block: (Language) -> Language
|
val block: (Language) -> Language
|
||||||
) : TranslatorLanguageEvent()
|
) : TranslatorLanguageEvent()
|
||||||
|
|
||||||
|
data class OnUpdateLanguageHistory(
|
||||||
|
val languageToChoose: LanguageCode
|
||||||
|
) : TranslatorLanguageEvent()
|
||||||
}
|
}
|
||||||
|
|
@ -17,7 +17,9 @@ import ua.acclorite.book_story.domain.model.Language
|
||||||
import ua.acclorite.book_story.domain.model.LanguageHistory
|
import ua.acclorite.book_story.domain.model.LanguageHistory
|
||||||
import ua.acclorite.book_story.domain.use_case.DeleteLanguageModel
|
import ua.acclorite.book_story.domain.use_case.DeleteLanguageModel
|
||||||
import ua.acclorite.book_story.domain.use_case.DownloadLanguageModel
|
import ua.acclorite.book_story.domain.use_case.DownloadLanguageModel
|
||||||
|
import ua.acclorite.book_story.domain.use_case.GetLanguageHistory
|
||||||
import ua.acclorite.book_story.domain.use_case.IsLanguageModelDownloaded
|
import ua.acclorite.book_story.domain.use_case.IsLanguageModelDownloaded
|
||||||
|
import ua.acclorite.book_story.domain.use_case.UpdateLanguageHistory
|
||||||
import ua.acclorite.book_story.domain.util.UIText
|
import ua.acclorite.book_story.domain.util.UIText
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -26,7 +28,9 @@ import javax.inject.Inject
|
||||||
class TranslatorLanguageViewModel @Inject constructor(
|
class TranslatorLanguageViewModel @Inject constructor(
|
||||||
private val isLanguageModelDownloaded: IsLanguageModelDownloaded,
|
private val isLanguageModelDownloaded: IsLanguageModelDownloaded,
|
||||||
private val downloadLanguageModel: DownloadLanguageModel,
|
private val downloadLanguageModel: DownloadLanguageModel,
|
||||||
private val deleteLanguageModel: DeleteLanguageModel
|
private val deleteLanguageModel: DeleteLanguageModel,
|
||||||
|
private val getLanguageHistory: GetLanguageHistory,
|
||||||
|
private val updateLanguageHistory: UpdateLanguageHistory,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _state = MutableStateFlow(TranslatorLanguageState())
|
private val _state = MutableStateFlow(TranslatorLanguageState())
|
||||||
|
|
@ -313,6 +317,33 @@ class TranslatorLanguageViewModel @Inject constructor(
|
||||||
filterLanguages()
|
filterLanguages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is TranslatorLanguageEvent.OnUpdateLanguageHistory -> {
|
||||||
|
viewModelScope.launch {
|
||||||
|
var history = getLanguageHistory
|
||||||
|
.execute()
|
||||||
|
.sortedBy { it.order }
|
||||||
|
.toMutableList()
|
||||||
|
|
||||||
|
val langToAdd = _state.value.languageToSelect
|
||||||
|
|
||||||
|
val firstItem = history.indexOfFirst { it.languageCode == langToAdd }
|
||||||
|
val indexOfItem = if (firstItem != -1) firstItem else null
|
||||||
|
|
||||||
|
if (indexOfItem != null) {
|
||||||
|
history.removeAt(indexOfItem)
|
||||||
|
}
|
||||||
|
history.add(0, LanguageHistory(langToAdd, 0))
|
||||||
|
|
||||||
|
history = history.mapIndexed { index, item ->
|
||||||
|
item.copy(
|
||||||
|
order = index
|
||||||
|
)
|
||||||
|
}.toMutableList()
|
||||||
|
|
||||||
|
updateLanguageHistory.execute(history)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -320,7 +351,6 @@ class TranslatorLanguageViewModel @Inject constructor(
|
||||||
selectedLanguage: String,
|
selectedLanguage: String,
|
||||||
unselectedLanguage: String,
|
unselectedLanguage: String,
|
||||||
translateFromSelecting: Boolean,
|
translateFromSelecting: Boolean,
|
||||||
history: List<LanguageHistory>,
|
|
||||||
loaded: () -> Unit
|
loaded: () -> Unit
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
|
@ -329,6 +359,7 @@ class TranslatorLanguageViewModel @Inject constructor(
|
||||||
TranslatorLanguageState()
|
TranslatorLanguageState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val history = getLanguageHistory.execute()
|
||||||
val unfilteredLanguages = TranslateLanguage.getAllLanguages()
|
val unfilteredLanguages = TranslateLanguage.getAllLanguages()
|
||||||
.filter {
|
.filter {
|
||||||
if (it == "ru") {
|
if (it == "ru") {
|
||||||
|
|
@ -353,7 +384,7 @@ class TranslatorLanguageViewModel @Inject constructor(
|
||||||
|
|
||||||
val historyOrder = history.find {
|
val historyOrder = history.find {
|
||||||
it.languageCode == languageCode
|
it.languageCode == languageCode
|
||||||
}?.id
|
}?.order
|
||||||
|
|
||||||
Language(
|
Language(
|
||||||
languageCode = languageCode,
|
languageCode = languageCode,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package ua.acclorite.book_story.presentation.data
|
package ua.acclorite.book_story.presentation.data
|
||||||
|
|
||||||
import ua.acclorite.book_story.domain.model.LanguageHistory
|
|
||||||
|
|
||||||
sealed class MainEvent {
|
sealed class MainEvent {
|
||||||
data class OnChangeLanguage(val lang: String) : MainEvent()
|
data class OnChangeLanguage(val lang: String) : MainEvent()
|
||||||
data class OnChangeTheme(val theme: String) : MainEvent()
|
data class OnChangeTheme(val theme: String) : MainEvent()
|
||||||
|
|
@ -23,5 +21,4 @@ sealed class MainEvent {
|
||||||
data class OnChangeTranslateFrom(val languageCode: String) : MainEvent()
|
data class OnChangeTranslateFrom(val languageCode: String) : MainEvent()
|
||||||
data class OnChangeTranslateTo(val languageCode: String) : MainEvent()
|
data class OnChangeTranslateTo(val languageCode: String) : MainEvent()
|
||||||
data class OnChangeDoubleClickTranslation(val bool: Boolean) : MainEvent()
|
data class OnChangeDoubleClickTranslation(val bool: Boolean) : MainEvent()
|
||||||
data class OnChangeTranslatorLanguageHistory(val history: List<LanguageHistory>) : MainEvent()
|
|
||||||
}
|
}
|
||||||
|
|
@ -6,12 +6,7 @@ import androidx.annotation.Keep
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import com.google.mlkit.nl.translate.TranslateLanguage
|
import com.google.mlkit.nl.translate.TranslateLanguage
|
||||||
import com.squareup.moshi.JsonAdapter
|
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import com.squareup.moshi.Types
|
|
||||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import ua.acclorite.book_story.domain.model.LanguageHistory
|
|
||||||
import ua.acclorite.book_story.domain.util.Constants
|
import ua.acclorite.book_story.domain.util.Constants
|
||||||
import ua.acclorite.book_story.domain.util.DataStoreConstants
|
import ua.acclorite.book_story.domain.util.DataStoreConstants
|
||||||
import ua.acclorite.book_story.presentation.ui.DarkTheme
|
import ua.acclorite.book_story.presentation.ui.DarkTheme
|
||||||
|
|
@ -52,21 +47,9 @@ data class MainState(
|
||||||
val enableTranslator: Boolean? = null,
|
val enableTranslator: Boolean? = null,
|
||||||
val translateFrom: String? = null,
|
val translateFrom: String? = null,
|
||||||
val translateTo: String? = null,
|
val translateTo: String? = null,
|
||||||
val doubleClickTranslation: Boolean? = null,
|
val doubleClickTranslation: Boolean? = null
|
||||||
val translatorLanguageHistory: List<LanguageHistory>? = null,
|
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
companion object {
|
companion object {
|
||||||
val translatorHistoryMoshi: JsonAdapter<List<LanguageHistory>> = Moshi
|
|
||||||
.Builder()
|
|
||||||
.add(KotlinJsonAdapterFactory())
|
|
||||||
.build()
|
|
||||||
.adapter(
|
|
||||||
Types.newParameterizedType(
|
|
||||||
List::class.java,
|
|
||||||
LanguageHistory::class.java
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes [MainState] by given [Map].
|
* Initializes [MainState] by given [Map].
|
||||||
*/
|
*/
|
||||||
|
|
@ -144,12 +127,6 @@ data class MainState(
|
||||||
data[DOUBLE_CLICK_TRANSLATION.name] as? Boolean
|
data[DOUBLE_CLICK_TRANSLATION.name] as? Boolean
|
||||||
?: true
|
?: true
|
||||||
|
|
||||||
val translatorLanguageHistory: List<LanguageHistory> =
|
|
||||||
(data[TRANSLATOR_LANGUAGE_HISTORY.name] as? String)?.let {
|
|
||||||
translatorHistoryMoshi.fromJson(it)
|
|
||||||
} ?: emptyList()
|
|
||||||
|
|
||||||
|
|
||||||
return MainState(
|
return MainState(
|
||||||
language = language,
|
language = language,
|
||||||
theme = theme.toTheme(),
|
theme = theme.toTheme(),
|
||||||
|
|
@ -170,8 +147,7 @@ data class MainState(
|
||||||
enableTranslator = enableTranslator,
|
enableTranslator = enableTranslator,
|
||||||
translateFrom = translateFrom,
|
translateFrom = translateFrom,
|
||||||
translateTo = translateTo,
|
translateTo = translateTo,
|
||||||
doubleClickTranslation = doubleClickTranslation,
|
doubleClickTranslation = doubleClickTranslation
|
||||||
translatorLanguageHistory = translatorLanguageHistory
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -271,22 +271,6 @@ class MainViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is MainEvent.OnChangeTranslatorLanguageHistory -> {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
val historyJson = MainState.translatorHistoryMoshi.toJson(event.history)
|
|
||||||
|
|
||||||
setDatastore.execute(
|
|
||||||
DataStoreConstants.TRANSLATOR_LANGUAGE_HISTORY,
|
|
||||||
historyJson
|
|
||||||
)
|
|
||||||
updateStateWithSavedHandle {
|
|
||||||
it.copy(
|
|
||||||
translatorLanguageHistory = event.history
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue