Skip to content

Commit

Permalink
Merge pull request #95 from dmzz-yyhyy/checksum
Browse files Browse the repository at this point in the history
更新支持检查文件校验和
  • Loading branch information
yukonisen authored Sep 3, 2024
2 parents 0813f90 + 9686fae commit 054a858
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 35 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_017
versionCode = 4_04_020
versionName = "0.4.4"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ data class AppCenterMetadata (
val versionName: String,
val releaseNotes: String,
val downloadUrl: String,
val downloadSize: String
val downloadSize: String,
val checksum: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class AppCenterMetadataAdapter : TypeAdapter<AppCenterMetadata>() {
var releaseNotes: String? = null
var downloadUrl: String? = null
var downloadSize: String? = null
var checksum: String? = null

reader.beginObject()
while (reader.hasNext()) {
Expand All @@ -35,6 +36,7 @@ class AppCenterMetadataAdapter : TypeAdapter<AppCenterMetadata>() {
"release_notes" -> releaseNotes = reader.nextString()
"download_url" -> downloadUrl = reader.nextString()
"size" -> downloadSize = reader.nextString()
"fingerprint" -> checksum = reader.nextString()
else -> reader.skipValue()
}
}
Expand All @@ -45,7 +47,8 @@ class AppCenterMetadataAdapter : TypeAdapter<AppCenterMetadata>() {
versionName!!,
releaseNotes!!,
downloadUrl!!,
downloadSize!!
downloadSize!!,
checksum!!
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ data class Release(
val versionName: String? = null,
val releaseNotes: String? = null,
val downloadUrl: String? = null,
val downloadSize: String? = null
val downloadSize: String? = null,
val checksum: String? = null
)

enum class ReleaseStatus {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ package indi.dmzz_yyhyy.lightnovelreader.data.update
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Environment
import android.util.Log
import android.widget.Toast
import androidx.core.content.ContextCompat.startActivity
import androidx.core.content.FileProvider
import com.google.gson.Gson
import com.google.gson.GsonBuilder
Expand All @@ -21,6 +19,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.jsoup.Jsoup
import java.io.File
import java.io.FileInputStream
import java.security.MessageDigest
import javax.inject.Inject
import javax.inject.Singleton

Expand Down Expand Up @@ -61,7 +61,8 @@ class UpdateCheckRepository @Inject constructor(
gsonData.versionName,
gsonData.releaseNotes,
gsonData.downloadUrl,
gsonData.downloadSize
gsonData.downloadSize,
gsonData.checksum
)
} else {
Log.i("UpdateChecker", "App is up to date")
Expand All @@ -74,20 +75,24 @@ class UpdateCheckRepository @Inject constructor(
}
}

fun downloadUpdate(url: String, version: String, size: Long, context: Context) {
fun downloadUpdate(url: String, version: String, checksum: String, context: Context) {
val fileName = "LightNovelReader-update-$version.apk"
val downloadPath =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
val file = File(downloadPath, fileName)
val cacheDir = File(context.cacheDir, "updates")
if (!cacheDir.exists()) cacheDir.mkdirs()
val file = File(cacheDir, fileName)

if (url.isBlank()) return

if (file.exists()) {
if (file.length() == size) {
if (checkMD5sum(file, checksum)) {
installApk(file, context)
return
} else file.delete()
} else {
file.delete()
Toast.makeText(context, "本地文件校验和计算失败,正在重新下载...", Toast.LENGTH_SHORT).show()
}
}

val ketch: Ketch = Ketch.init(
context = context,
notificationConfig = NotificationConfig(
Expand All @@ -100,33 +105,60 @@ class UpdateCheckRepository @Inject constructor(
ketch.download(
url = url,
fileName = fileName,
path = downloadPath,
path = cacheDir.path,
tag = "Updates",
onSuccess = {
installApk(file, context)
if (checkMD5sum(file, checksum)) {
installApk(file, context)
} else {
file.delete()
Toast.makeText(context, "校验和计算失败,请重试", Toast.LENGTH_SHORT).show()
}
},
onFailure = {
Toast.makeText(context, "下载失败,请尝试手动下载", Toast.LENGTH_SHORT).show()
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(context, intent, null)
context.startActivity(intent)
}
)
}
}

private fun installApk(file: File, context: Context) {
val uri = FileProvider.getUriForFile(context, "${context.packageName}.provider", file)
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)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK)
setDataAndType(uri, "application/vnd.android.package-archive")
}
context.startActivity(intent)
}

private fun checkMD5sum(file: File, checksum: String): Boolean {
val digest = MessageDigest.getInstance("MD5")
val buffer = ByteArray(1024)

return try {
FileInputStream(file).use { stream ->
var bytesRead: Int
while (stream.read(buffer).also { bytesRead = it } != -1) {
digest.update(buffer, 0, bytesRead)
}
}

val result = digest.digest().joinToString("") { "%02x".format(it) }
Log.i("UpdateChecker", "CheckMD5sum result: ${file.path}\n[file] $result -> $checksum [expected checksum]")

result.equals(checksum, ignoreCase = true)
} catch (e: Exception) {
Log.e("UpdateChecker", "Error checking MD5 sum", e)
false
}
}

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,25 @@ fun LightNovelReaderApp(
val navController = rememberNavController()

AnimatedVisibility(visible = viewModel.updateDialogUiState.visible) {
val releaseNotes = viewModel.updateDialogUiState.release.releaseNotes ?: ""
val downloadUrl = viewModel.updateDialogUiState.release.downloadUrl ?: ""
val version = viewModel.updateDialogUiState.release.version ?: -1
val versionName = viewModel.updateDialogUiState.release.versionName ?: ""
val checksum = viewModel.updateDialogUiState.release.checksum ?: ""
val downloadSize = viewModel.updateDialogUiState.release.downloadSize?.toLong() ?: -1
UpdatesAvailableDialog(
onDismissRequest = viewModel::onDismissUpdateRequest,
onConfirmation = { viewModel.downloadUpdate(
url = viewModel.updateDialogUiState.release.downloadUrl ?: "",
version = viewModel.updateDialogUiState.release.versionName ?: "",
size = viewModel.updateDialogUiState.release.downloadSize?.toLong() ?: -1,
url = downloadUrl,
version = versionName,
checksum = checksum,
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
newVersionCode = version,
newVersionName = versionName,
contentMarkdown = releaseNotes,
downloadSize = downloadSize.toDouble(),
downloadUrl = downloadUrl
)
}
AnimatedVisibility(visible = viewModel.addToBookshelfDialogUiState.visible) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ class LightNovelReaderViewModel @Inject constructor(
}
}

fun downloadUpdate(url: String, version: String, size: Long, context: Context) =
updateCheckRepository.downloadUpdate(url, version, size, context)
fun downloadUpdate(url: String, version: String, checksum: String, context: Context) =
updateCheckRepository.downloadUpdate(url, version, checksum, context)

fun clearToast() {
_updateDialogUiState.toast = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ fun UpdatesAvailableDialog(
contentMarkdown: String? = null,
newVersionName: String? = null,
newVersionCode: Int = 0,
downloadSize: String? = null,
downloadSize: Double? = null,
downloadUrl: String? = null
) {
val context = LocalContext.current
Expand All @@ -160,7 +160,7 @@ fun UpdatesAvailableDialog(
text = {
Column {
newVersionName?.let {
val sizeInMB = ((downloadSize?.toDoubleOrNull() ?: 0.0) / 1024) / 1024
val sizeInMB = ((downloadSize ?: 0.0) / 1024) / 1024
val formatted = "%.2f".format(sizeInMB)
Text(
text = "${BuildConfig.VERSION_NAME}(${BuildConfig.VERSION_CODE}) → $newVersionName($newVersionCode), ${formatted}MB"
Expand Down
6 changes: 2 additions & 4 deletions app/src/main/res/xml/provider_paths.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files"
path="." />
</paths>
<cache-path name="cache" path="/"/>
</paths>

0 comments on commit 054a858

Please sign in to comment.