Skip to content

Commit

Permalink
Merge pull request #82 from dmzz-yyhyy/optimizations
Browse files Browse the repository at this point in the history
优化更新检查
  • Loading branch information
dmzz-yyhyy authored Aug 29, 2024
2 parents 88ad97a + 7a74a22 commit fa5a639
Show file tree
Hide file tree
Showing 15 changed files with 172 additions and 147 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ android {
minSdk = 24
targetSdk = 34
// 版本号为x.y.z则versionCode为x*1000000+y*10000+z*100+debug版本号(开发需要时迭代, 两位数)
versionCode = 4_04_000
versionCode = 4_04_002
versionName = "0.4.4"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ import indi.dmzz_yyhyy.lightnovelreader.data.work.CheckUpdateWork
import indi.dmzz_yyhyy.lightnovelreader.theme.LightNovelReaderTheme
import indi.dmzz_yyhyy.lightnovelreader.ui.LightNovelReaderApp
import indi.dmzz_yyhyy.lightnovelreader.utils.update
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
Expand All @@ -48,10 +48,12 @@ class MainActivity : ComponentActivity() {
private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
private var isUsingVolumeKeyFlip = false


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var appLocale by mutableStateOf("${Locale.current.platformLocale.language}-${Locale.current.platformLocale.variant}")
var darkMode by mutableStateOf("FollowSystem")
installSplashScreen()
var statisticsEnabled by mutableStateOf(true)
workManager.enqueueUniquePeriodicWork(
"checkUpdate",
Expand Down Expand Up @@ -96,7 +98,6 @@ class MainActivity : ComponentActivity() {
it?.let { isUsingVolumeKeyFlip = it }
}
}
installSplashScreen()
if (Build.VERSION.SDK_INT >= 33) { /* Android 13 + */
if (ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package indi.dmzz_yyhyy.lightnovelreader.data.update

data class UpdateMetaData (
data class AppCenterMetadata (
val version: String,
val versionName: String,
val releaseNotes: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter

class UpdateMetaDataTypeAdapter : TypeAdapter<UpdateMetaData>() {
override fun write(writer: JsonWriter, value: UpdateMetaData?) {
class AppCenterMetadataAdapter : TypeAdapter<AppCenterMetadata>() {
override fun write(writer: JsonWriter, value: AppCenterMetadata?) {
if (value == null) {
writer.nullValue()
return
Expand All @@ -20,7 +20,7 @@ class UpdateMetaDataTypeAdapter : TypeAdapter<UpdateMetaData>() {
writer.endObject()
}

override fun read(reader: JsonReader): UpdateMetaData {
override fun read(reader: JsonReader): AppCenterMetadata {
var version: String? = null
var versionName: String? = null
var releaseNotes: String? = null
Expand All @@ -40,7 +40,7 @@ class UpdateMetaDataTypeAdapter : TypeAdapter<UpdateMetaData>() {
}
reader.endObject()

return UpdateMetaData(
return AppCenterMetadata(
version!!,
versionName!!,
releaseNotes!!,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package indi.dmzz_yyhyy.lightnovelreader.data.update

data class Release(
val status: ReleaseStatus,
val version: Int? = null,
val versionName: String? = null,
val releaseNotes: String? = null,
val downloadUrl: String? = null,
val downloadSize: String? = null
)

enum class ReleaseStatus {
LATEST,
AVAILABLE,
NULL
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Environment
import android.util.Log
import androidx.core.content.FileProvider
import com.google.gson.Gson
import com.google.gson.GsonBuilder
Expand All @@ -13,73 +14,67 @@ import indi.dmzz_yyhyy.lightnovelreader.BuildConfig
import indi.dmzz_yyhyy.lightnovelreader.R
import indi.dmzz_yyhyy.lightnovelreader.data.UserDataRepository
import indi.dmzz_yyhyy.lightnovelreader.data.userdata.UserDataPath
import indi.dmzz_yyhyy.lightnovelreader.utils.autoReconnectionGet
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.jsoup.Jsoup
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class UpdateCheckRepository @Inject constructor (
class UpdateCheckRepository @Inject constructor(
val userDataRepository: UserDataRepository
) {
private val releaseUrl = "https://api.appcenter.ms/v0.1/public/sdk/apps/f7743820-f7dc-498f-b31d-ec5032b0d66d/distribution_groups/bfcd55aa-302c-452a-b59e-90f065d437f5/releases/latest"
private val developmentUrl = "https://api.appcenter.ms/v0.1/public/sdk/apps/f7743820-f7dc-498f-b31d-ec5032b0d66d/distribution_groups/f21a594f-5a56-4b2b-9361-9a734c10f1c9/releases/latest"
private val coroutineScope = CoroutineScope(Dispatchers.IO)
private val gson: Gson = createGson()
private lateinit var ketch: Ketch

private val releaseUrl: String = "https://api.appcenter.ms/v0.1/public/sdk/apps/f7743820-f7dc-498f-b31d-ec5032b0d66d/distribution_groups/bfcd55aa-302c-452a-b59e-90f065d437f5/releases/latest"
private val developmentUrl: String = "https://api.appcenter.ms/v0.1/public/sdk/apps/f7743820-f7dc-498f-b31d-ec5032b0d66d/distribution_groups/f21a594f-5a56-4b2b-9361-9a734c10f1c9/releases/latest"
private val updateChannel = userDataRepository.stringUserData(UserDataPath.Settings.App.UpdateChannel.path)

val isNeedUpdateFlow = MutableStateFlow(false)
val versionNameFlow = MutableStateFlow("?")
val versionCodeFlow = MutableStateFlow(0)
val releaseNotesFlow = MutableStateFlow("**正在解析更新内容...**\n***Loading release notes***")
val downloadUrlFlow = MutableStateFlow("")
val downloadSizeFlow = MutableStateFlow("0")
fun checkAppCenter(): Release {
val url = when (updateChannel.getOrDefault("Development")) {
"Release" -> developmentUrl
"Development" -> releaseUrl
else -> developmentUrl
}

fun checkUpdate() {
coroutineScope.launch(Dispatchers.IO) {
try {
val url = when (userDataRepository.stringUserData(UserDataPath.Settings.App.UpdateChannel.path)
.getOrDefault("Release")) {
"Development" -> developmentUrl
"Release" -> releaseUrl
else -> releaseUrl
}
Log.i("UpdateChecker", "Checking for updates on channel \"${updateChannel.get()}\"")

val response = Jsoup
.connect(url)
.ignoreContentType(true)
.autoReconnectionGet()
?.body()
?.text()
try {
val response = Jsoup
.connect(url)
.ignoreContentType(true)
.get()
.body()
.text()

if (response != null) {
val gsonData = gson.fromJson(response, UpdateMetaData::class.java)
isNeedUpdateFlow.update { _ -> gsonData.version.toInt() > BuildConfig.VERSION_CODE }
versionCodeFlow.update { _ -> gsonData.version.toInt() }
versionNameFlow.update { _ -> gsonData.versionName }
releaseNotesFlow.update { _ -> gsonData.releaseNotes }
downloadUrlFlow.update { _ -> gsonData.downloadUrl }
downloadSizeFlow.update { _ -> gsonData.downloadSize }
} else {
isNeedUpdateFlow.update { false }
}
} catch (e: Exception) {
e.printStackTrace()
val gsonData = gson.fromJson(response, AppCenterMetadata::class.java)
val available = gsonData.version.toInt() > BuildConfig.VERSION_CODE
if (available) {
Log.i("UpdateChecker", "New version available: ${gsonData.versionName}")
return Release(
ReleaseStatus.AVAILABLE,
gsonData.version.toInt(),
gsonData.versionName,
gsonData.releaseNotes,
gsonData.downloadUrl,
gsonData.downloadSize
)
} else {
Log.i("UpdateChecker", "App is up to date")
return Release(ReleaseStatus.LATEST)
}
} catch (e: Exception) {
Log.e("UpdateChecker", "Failed to check updates:")
e.printStackTrace()
return Release(ReleaseStatus.NULL)
}
}


fun installUpdate(url: String, version: String, size: Long, context: Context) {
fun downloadUpdate(url: String, version: String, size: Long, context: Context) {
val fileName = "LightNovelReader-update-$version.apk"
val downloadPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
val downloadPath =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
val file = File(downloadPath, fileName)

if (url.isBlank()) return
Expand All @@ -90,13 +85,14 @@ class UpdateCheckRepository @Inject constructor (
return
} else file.delete()
}
ketch = Ketch.init(
val ketch: Ketch = Ketch.init(
context = context,
notificationConfig = NotificationConfig(
enabled = true,
smallIcon = R.drawable.icon_foreground,
)
)
val coroutineScope = CoroutineScope(Dispatchers.IO)
coroutineScope.launch(Dispatchers.IO) {
ketch.download(
url = url,
Expand All @@ -113,16 +109,16 @@ class UpdateCheckRepository @Inject constructor (
private fun installApk(file: File, context: Context) {
val intent = Intent(Intent.ACTION_VIEW).apply {
setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK)
val uri: Uri = FileProvider.getUriForFile(context, "${context.packageName}.provider", file)
val uri: Uri =
FileProvider.getUriForFile(context, "${context.packageName}.provider", file)
setDataAndType(uri, "application/vnd.android.package-archive")
}
context.startActivity(intent)
}

private fun createGson(): Gson {
return GsonBuilder()
.registerTypeAdapter(UpdateMetaData::class.java, UpdateMetaDataTypeAdapter())
.registerTypeAdapter(AppCenterMetadata::class.java, AppCenterMetadataAdapter())
.create()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,24 @@ abstract class UserData<T> (
open val path: String
) {
val group get() = path.split(".").dropLast(1).joinToString(".")
/**
* 此函数为阻塞函数,请务必不要在初始化阶段或主线程上调用
*/
abstract fun set(value: T)
/**
* 此函数为阻塞函数,请务必不要在初始化阶段或主线程上调用
*/
abstract fun get(): T?
abstract fun getFlow(): Flow<T?>
/**
* 此函数为阻塞函数,请务必不要在初始化阶段或主线程上调用
*/
fun getOrDefault(default: T): T {
return get() ?: default
}
/**
* 此函数为阻塞函数,请务必不要在初始化阶段或主线程上调用
*/
fun update(updater: (T) -> T, default: T) {
set(updater(getOrDefault(default)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ import indi.dmzz_yyhyy.lightnovelreader.ui.home.HomeScreen
fun LightNovelReaderApp(
viewModel: LightNovelReaderViewModel = hiltViewModel(),
context: Context = LocalContext.current,
onClickInstallUpdate: () -> Unit = {
viewModel.installUpdate(
url = viewModel.updateDialogUiState.downloadUrl,
version = viewModel.updateDialogUiState.versionName,
size = viewModel.updateDialogUiState.downloadSize.toLong(),
context = context
)
},
) {
LifecycleEventEffect(Lifecycle.Event.ON_CREATE) {
viewModel.autoCheckUpdate()
Expand All @@ -46,11 +38,17 @@ fun LightNovelReaderApp(
AnimatedVisibility(visible = viewModel.updateDialogUiState.visible) {
UpdatesAvailableDialog(
onDismissRequest = viewModel::onDismissUpdateRequest,
onConfirmation = onClickInstallUpdate,
newVersionCode = viewModel.updateDialogUiState.versionCode,
newVersionName = viewModel.updateDialogUiState.versionName,
contentMarkdown = viewModel.updateDialogUiState.releaseNotes,
downloadSize = viewModel.updateDialogUiState.downloadSize,
onConfirmation = { viewModel.downloadUpdate(
url = viewModel.updateDialogUiState.release.downloadUrl ?: "",
version = viewModel.updateDialogUiState.release.versionName ?: "",
size = viewModel.updateDialogUiState.release.downloadSize?.toLong() ?: -1,
context = context
) },
newVersionCode = viewModel.updateDialogUiState.release.version ?: -1,
newVersionName = viewModel.updateDialogUiState.release.versionName ?: "",
contentMarkdown = viewModel.updateDialogUiState.release.releaseNotes ?: "",
downloadSize = viewModel.updateDialogUiState.release.downloadSize ?: "",
downloadUrl = viewModel.updateDialogUiState.release.downloadUrl
)
}
AnimatedVisibility(visible = viewModel.addToBookshelfDialogUiState.visible) {
Expand Down
Loading

0 comments on commit fa5a639

Please sign in to comment.