Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

数据导出功能 #120

Merged
merged 8 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading