Skip to content

Commit

Permalink
Merge pull request #120 from dmzz-yyhyy/data_export
Browse files Browse the repository at this point in the history
数据导出功能
  • Loading branch information
yukonisen authored Oct 7, 2024
2 parents 0ae4fd6 + 95aeb73 commit 38e8af4
Show file tree
Hide file tree
Showing 92 changed files with 3,676 additions and 1,080 deletions.
4 changes: 2 additions & 2 deletions .idea/dataSources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/dictionaries/NightFish.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ android {
minSdk = 24
targetSdk = 34
// 版本号为x.y.z则versionCode为x*1000000+y*10000+z*100+debug版本号(开发需要时迭代, 两位数)
versionCode = 4_04_024
versionName = "0.4.4"
versionCode = 1_00_00_001
versionName = "1.0.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import indi.dmzz_yyhyy.lightnovelreader.data.book.BookVolumes
import indi.dmzz_yyhyy.lightnovelreader.data.book.ChapterContent
import indi.dmzz_yyhyy.lightnovelreader.data.book.UserReadingData
import indi.dmzz_yyhyy.lightnovelreader.data.bookshelf.BookshelfRepository
import indi.dmzz_yyhyy.lightnovelreader.data.json.AppUserDataContent
import indi.dmzz_yyhyy.lightnovelreader.data.json.BookUserData
import indi.dmzz_yyhyy.lightnovelreader.data.local.LocalBookDataSource
import indi.dmzz_yyhyy.lightnovelreader.data.web.WebBookDataSource
import indi.dmzz_yyhyy.lightnovelreader.data.work.CacheBookWork
Expand Down Expand Up @@ -92,6 +94,9 @@ class BookRepository @Inject constructor(
fun getUserReadingData(bookId: Int): Flow<UserReadingData> =
localBookDataSource.getUserReadingData(bookId).map { it }

fun getAllUserReadingData(): List<UserReadingData> =
localBookDataSource.getAllUserReadingData()

fun updateUserReadingData(id: Int, update: (UserReadingData) -> UserReadingData) {
localBookDataSource.updateUserReadingData(id, update)
}
Expand All @@ -111,4 +116,23 @@ class BookRepository @Inject constructor(
}

fun isCacheBookWorkFlow(workId: UUID) = workManager.getWorkInfoByIdFlow(workId)

fun importUserReadingData(data: AppUserDataContent): Boolean {
val userReadingDataList: List<BookUserData> = data.bookUserData ?: return false
userReadingDataList.forEach { bookUserData ->
localBookDataSource.updateUserReadingData(bookUserData.id) {
UserReadingData(
id = bookUserData.id,
lastReadTime = if (bookUserData.lastReadTime.isAfter(it.lastReadTime)) bookUserData.lastReadTime else it.lastReadTime,
totalReadTime = if (bookUserData.totalReadTime > it.totalReadTime) bookUserData.totalReadTime else it.totalReadTime,
readingProgress = if (bookUserData.readingProgress > it.readingProgress) bookUserData.readingProgress else it.readingProgress,
lastReadChapterId = bookUserData.lastReadChapterId,
lastReadChapterTitle = bookUserData.lastReadChapterTitle,
lastReadChapterProgress = bookUserData.lastReadChapterProgress,
readCompletedChapterIds = (bookUserData.readCompletedChapterIds + it.readCompletedChapterIds).distinct()
)
}
}
return true
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package indi.dmzz_yyhyy.lightnovelreader.data

import indi.dmzz_yyhyy.lightnovelreader.data.json.AppUserDataContent
import indi.dmzz_yyhyy.lightnovelreader.data.local.room.dao.UserDataDao
import indi.dmzz_yyhyy.lightnovelreader.data.userdata.BooleanUserData
import indi.dmzz_yyhyy.lightnovelreader.data.userdata.FloatUserData
Expand All @@ -11,12 +12,24 @@ import javax.inject.Singleton

@Singleton
class UserDataRepository @Inject constructor(
private val userDataDao: UserDataDao)
{

private val userDataDao: UserDataDao
) {
fun stringUserData(path: String): StringUserData = StringUserData(path, userDataDao)
fun floatUserData(path: String): FloatUserData = FloatUserData(path, userDataDao)
fun booleanUserData(path: String): BooleanUserData = BooleanUserData(path, userDataDao)
fun intListUserData(path: String): IntListUserData = IntListUserData(path, userDataDao)
fun stringListUserData(path: String): StringListUserData = StringListUserData(path, userDataDao)

fun importUserData(data: AppUserDataContent): Boolean {
val userDataList = data.userData ?: return false
userDataList.forEach {
userDataDao.update(
path = it.path,
group = it.group,
type = it.type,
value = it.value
)
}
return true
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package indi.dmzz_yyhyy.lightnovelreader.data.bookshelf

import android.net.Uri
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.workDataOf
import indi.dmzz_yyhyy.lightnovelreader.data.book.BookInformation
import indi.dmzz_yyhyy.lightnovelreader.data.json.AppUserDataContent
import indi.dmzz_yyhyy.lightnovelreader.data.json.AppUserDataJsonBuilder
import indi.dmzz_yyhyy.lightnovelreader.data.json.toJsonData
import indi.dmzz_yyhyy.lightnovelreader.data.loacltion.room.converter.LocalDataTimeConverter.dateToString
import indi.dmzz_yyhyy.lightnovelreader.data.local.room.converter.ListConverter.intListToString
import indi.dmzz_yyhyy.lightnovelreader.data.local.room.dao.BookshelfDao
import indi.dmzz_yyhyy.lightnovelreader.data.local.room.entity.BookshelfEntity
import indi.dmzz_yyhyy.lightnovelreader.data.web.WebBookDataSource
import indi.dmzz_yyhyy.lightnovelreader.data.work.CacheBookWork
import indi.dmzz_yyhyy.lightnovelreader.data.work.SaveBookshelfWork
import java.time.Instant
import java.time.LocalDateTime
import javax.inject.Inject
Expand All @@ -20,7 +27,8 @@ import kotlinx.coroutines.flow.map
@Singleton
class BookshelfRepository @Inject constructor(
private val bookshelfDao: BookshelfDao,
private val workManager: WorkManager
private val workManager: WorkManager,
private val webBookDataSource: WebBookDataSource
) {
fun getAllBookshelfIds(): List<Int> = bookshelfDao.getAllBookshelfIds()

Expand Down Expand Up @@ -86,7 +94,7 @@ class BookshelfRepository @Inject constructor(
val bookshelf = bookshelfDao.getBookShelf(bookshelfId) ?: return
bookshelfDao.addBookshelfMetadata(
id = bookInformation.id,
lastUpdate = dateToString(bookInformation.lastUpdated) ?: "",
lastUpdate = bookInformation.lastUpdated,
bookshelfIds = listOf(bookshelfId)
)
if (bookshelf.autoCache && bookshelf.allBookIds.contains(bookInformation.id)) {
Expand Down Expand Up @@ -212,4 +220,71 @@ class BookshelfRepository @Inject constructor(
intListToString(bookshelfDao.getBookshelfBookMetadata(bookId)?.bookShelfIds!!)
)
}

fun exportAllBookshelvesJson(): String = AppUserDataJsonBuilder()
.data {
webDataSourceId(webBookDataSource.id)
getAllBookshelfIds()
.mapNotNull { (getBookshelf(it)) }
.map { (it as Bookshelf).toJsonData() }
.forEach (::bookshelf)
getAllBookshelfBooksMetadata()
.map(BookshelfBookMetadata::toJsonData)
.forEach(::bookshelfBookMetaData)
}
.build()
.toJson()

fun exportBookshelvesJson(id: Int): String = AppUserDataJsonBuilder()
.data {
webDataSourceId(webBookDataSource.id)
getBookshelf(id)?.toJsonData()?.let { bookshelf(it) }
getBookshelf(id)?.allBookIds
?.mapNotNull(::getBookshelfBookMetadata)
?.map { BookshelfBookMetadata(id = it.id, lastUpdate = it.lastUpdate, bookShelfIds = listOf(id)) }
?.map(BookshelfBookMetadata::toJsonData)
?.forEach(::bookshelfBookMetaData)
}
.build()
.toJson()

fun saveBookshelfJsonData(bookshelfId: Int, uri: Uri): OneTimeWorkRequest {
val workRequest = OneTimeWorkRequestBuilder<SaveBookshelfWork>()
.setInputData(workDataOf(
"bookshelfId" to bookshelfId,
"uri" to uri.toString(),
))
.build()
workManager.enqueueUniqueWork(
uri.toString(),
ExistingWorkPolicy.KEEP,
workRequest
)
return workRequest
}

fun importBookshelf(data: AppUserDataContent): Boolean {
val bookshelfDataList = data.bookshelf ?: return false
val bookshelfBookMetadataList = data.bookShelfBookMetadata ?: return false
val allBookshelfIds = getAllBookshelfIds()
bookshelfDataList.forEach { bookshelf ->
if (allBookshelfIds.contains(bookshelf.id)) return@forEach
bookshelfDao.createBookshelf(
BookshelfEntity(
id = bookshelf.id,
name = bookshelf.name,
sortType = bookshelf.sortType.key,
autoCache = bookshelf.autoCache,
systemUpdateReminder = bookshelf.systemUpdateReminder,
allBookIds = bookshelf.allBookIds,
pinnedBookIds = bookshelf.pinnedBookIds,
updatedBookIds = bookshelf.updatedBookIds,
)
)
}
bookshelfBookMetadataList.forEach {
bookshelfDao.addBookshelfMetadata(it.id, it.lastUpdate, it.bookShelfIds)
}
return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ package indi.dmzz_yyhyy.lightnovelreader.data.bookshelf

enum class BookshelfSortType(val key: String) {
Default("default"),
Latest("latest"),
Latest("latest");
companion object {
fun map(key: String): BookshelfSortType = BookshelfSortType.entries.first { it.key == key }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package indi.dmzz_yyhyy.lightnovelreader.data.json

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.annotations.SerializedName
import indi.dmzz_yyhyy.lightnovelreader.data.bookshelf.BookshelfSortType
import java.time.LocalDateTime

data class AppUserDataJson(
val type: String,
val id: Int? = null,
val data: List<AppUserDataContent>
) {
companion object {
val gson: Gson = GsonBuilder()
.serializeSpecialFloatingPointValues()
.registerTypeAdapter(LocalDateTime::class.java, LocalTimeDataTypeAdapter)
.registerTypeAdapter(BookshelfSortType::class.java, BookshelfSortTypeTypeAdapter)
.create()

fun fromJson(json: String): AppUserDataJson = gson.fromJson(json, AppUserDataJson::class.java)
}

fun toJson(): String = gson.toJson(this)
}

data class AppUserDataContent(
@SerializedName("web_data_source_id")
val webDataSourceId: Int,
@SerializedName("book_user_data")
val bookUserData: List<BookUserData>? = null,
@SerializedName("book_shelf")
val bookshelf: List<BookshelfData>? = null,
@SerializedName("book_shelf_book_metadata")
val bookShelfBookMetadata: List<BookShelfBookMetadataData>? = null,
@SerializedName("user_data")
val userData: List<UserDataData>? = null,
)

class AppUserDataJsonBuilder {
private var id: Int? = null
private var data: MutableList<AppUserDataContent> = mutableListOf()

fun build(): AppUserDataJson = AppUserDataJson(
type = "light novel reader data file",
id = id,
data = data
)

fun data(data: AppUserDataContentBuilder.() -> Unit): AppUserDataJsonBuilder {
this.data.add(
AppUserDataContentBuilder()
.let {
data.invoke(it)
it.build()
}
)
return this
}
}

class AppUserDataContentBuilder() {
private var webDataSourceId: Int? = null
private var bookUserData: MutableList<BookUserData> = mutableListOf()
private var bookshelf: MutableList<BookshelfData> = mutableListOf()
private var bookShelfBookMetadata: MutableList<BookShelfBookMetadataData> = mutableListOf()
private var userData: MutableList<UserDataData> = mutableListOf()

fun build(): AppUserDataContent {
if (webDataSourceId == null) {
throw NullPointerException("webDataSourceId can not be null")
}
return AppUserDataContent(
webDataSourceId = webDataSourceId!!,
bookUserData = bookUserData.ifEmpty { null },
bookshelf = bookshelf.ifEmpty { null },
bookShelfBookMetadata = bookShelfBookMetadata.ifEmpty { null },
userData = userData.ifEmpty { null },
)
}

fun webDataSourceId(webDataSourceId: Int): AppUserDataContentBuilder {
this.webDataSourceId = webDataSourceId
return this
}

fun bookUserData(bookUserData: BookUserData): AppUserDataContentBuilder {
this.bookUserData.add(bookUserData)
return this
}

fun bookshelf(bookshelf: BookshelfData): AppUserDataContentBuilder {
this.bookshelf.add(bookshelf)
return this
}

fun bookshelfBookMetaData(bookshelfBookMetadata: BookShelfBookMetadataData): AppUserDataContentBuilder {
this.bookShelfBookMetadata.add(bookshelfBookMetadata)
return this
}

fun userData(userData: UserDataData): AppUserDataContentBuilder {
this.userData.add(userData)
return this
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package indi.dmzz_yyhyy.lightnovelreader.data.json

import com.google.gson.annotations.SerializedName
import indi.dmzz_yyhyy.lightnovelreader.data.bookshelf.BookshelfBookMetadata
import java.time.LocalDateTime

data class BookShelfBookMetadataData(
val id: Int,
@SerializedName("last_update")
val lastUpdate: LocalDateTime,
@SerializedName("book_shelf_ids")
val bookShelfIds: List<Int>,
)

fun BookshelfBookMetadata.toJsonData() =
BookShelfBookMetadataData(
id = id,
lastUpdate = lastUpdate,
bookShelfIds = bookShelfIds,
)
Loading

0 comments on commit 38e8af4

Please sign in to comment.