diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivityScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivityScreen.kt
index 28553b3200e..75e6f70250f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivityScreen.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivityScreen.kt
@@ -61,9 +61,9 @@ object MainActivityScreen : Screen {
buildSha = activity.currentCommitHash(),
settings = settings,
updater = ApkUpdater
- )/*.apply {
- onAction(AutoSearchForUpdate) // Remove this for now
- }*/
+ ).apply {
+ onAction(AutoSearchForUpdate)
+ }
}
}
@@ -171,6 +171,42 @@ object MainActivityScreen : Screen {
}
}
+ is GithubUpdateDialogState.InstallProgress -> {
+ cancelable = false
+ title = stringResource(R.string.install_update)
+ body = {
+ val progress = if (state.total != null && state.total > 0L) {
+ (state.progress.toFloat() / state.total.toFloat()).coerceIn(0.0f, 1.0f)
+ } else null
+
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ if (progress != null) {
+ Text(
+ color = MaterialTheme.colorScheme.onBackground,
+ style = MaterialTheme.typography.bodyMedium,
+ textAlign = TextAlign.Center,
+ text = "${(progress * 100.0f).toInt()}%"
+ )
+ Spacer(modifier = Modifier.height(MaterialTheme.padding.medium))
+ LinearProgressIndicator(
+ modifier = Modifier.fillMaxWidth(),
+ color = MaterialTheme.colorScheme.onBackground,
+ trackColor = MaterialTheme.colorScheme.surfaceVariant,
+ progress = { progress }
+ )
+ } else {
+ LinearProgressIndicator(
+ modifier = Modifier.fillMaxWidth(),
+ color = MaterialTheme.colorScheme.onBackground,
+ trackColor = MaterialTheme.colorScheme.surfaceVariant,
+ )
+ }
+ }
+ }
+ confirmButton = {}
+ dismissButton = {}
+ }
+
is GithubUpdateDialogState.DownloadProgress -> {
cancelable = false
title = stringResource(R.string.update_notification_downloading)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/ApkUpdater.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/ApkUpdater.kt
index 08e279b49a0..d26fdcc6f57 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/ApkUpdater.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/ApkUpdater.kt
@@ -65,9 +65,101 @@ object ApkUpdater : AppUpdater {
}
}
- fun clearOldFiles(activity: Activity) {
+ fun getCachedUpdateFile(context: Context, versionTag: String): File {
+ return File(context.cacheDir, "${APP_UPDATE_NAME}_$versionTag.$APP_UPDATE_SUFFIX")
+ }
+
+ suspend fun downloadSilently(
+ context: Context,
+ url: String,
+ versionTag: String,
+ digest: DigestPair?
+ ): File? = withContext(Dispatchers.IO) {
+ try {
+ val targetFile = getCachedUpdateFile(context, versionTag)
+ if (targetFile.exists() && targetFile.length() > 0) {
+ return@withContext targetFile
+ }
+
+ clearOldFiles(context)
+ val request = app.get(url)
+ val length = request.size
+ val body = request.body
+ val tempFile = File.createTempFile(APP_UPDATE_NAME, ".$APP_UPDATE_SUFFIX", context.cacheDir)
+ body.use { body ->
+ val readStream = body.byteStream()
+ tempFile.outputStream().use { writeStream ->
+ transfer(writeStream, readStream, length ?: body.contentLength(), { _, _ -> }, digest)
+ }
+ }
+
+ if (tempFile.renameTo(targetFile)) {
+ targetFile
+ } else {
+ tempFile
+ }
+ } catch (_: Throwable) {
+ null
+ }
+ }
+
+ @Throws
+ suspend fun installFromFile(
+ activity: Activity,
+ file: File,
+ settings: AppSettings,
+ installProgress: (Long, Long?) -> Unit
+ ) = withContext(Dispatchers.IO) {
+ when (settings.updates.apkInstaller.get()) {
+ 0 -> {
+ val length = file.length()
+ file.inputStream().use { inputStream ->
+ var sessionId: Int? = null
+ val packageInstaller = activity.packageManager.packageInstaller
+ try {
+ val installParams =
+ PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ installParams.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED)
+ }
+ installParams.setSize(length)
+
+ sessionId = packageInstaller.createSession(installParams)
+ val session = packageInstaller.openSession(sessionId)
+
+ session.openWrite(activity.packageName, 0, length)
+ .use { writeStream ->
+ transfer(writeStream, inputStream, length, installProgress, null)
+ session.fsync(writeStream)
+ }
+
+ val receiverIntent = Intent(activity, PackageInstallerStatusReceiver::class.java)
+ val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ } else {
+ PendingIntent.FLAG_UPDATE_CURRENT
+ }
+ val receiverPendingIntent = PendingIntent.getBroadcast(activity, 0, receiverIntent, flags)
+ session.commit(receiverPendingIntent.intentSender)
+ session.close()
+ } catch (t: Throwable) {
+ sessionId?.let { id ->
+ packageInstaller.abandonSession(id)
+ }
+ throw t
+ }
+ }
+ }
+ else -> {
+ openApk(activity, file)
+ }
+ }
+ }
+
+ fun clearOldFiles(context: Context) {
// Delete old files
- activity.cacheDir.listFiles()?.filter {
+ context.cacheDir.listFiles()?.filter {
it.name.startsWith(APP_UPDATE_NAME) && it.extension == APP_UPDATE_SUFFIX
}?.forEach {
deleteFileOnExit(it)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/GithubViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/GithubViewModel.kt
index 99236ab7173..1aead52df46 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/GithubViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/GithubViewModel.kt
@@ -24,6 +24,7 @@ data class GithubState(
sealed class GithubUpdateDialogState {
data class Error(val error: Throwable) : GithubUpdateDialogState()
data class DownloadProgress(val progress: Long, val total: Long?) : GithubUpdateDialogState()
+ data class InstallProgress(val progress: Long, val total: Long?) : GithubUpdateDialogState()
object Loading : GithubUpdateDialogState()
object NoUpdateFound : GithubUpdateDialogState()
data class UpdateFound(
@@ -153,6 +154,7 @@ class GithubViewModel(
is GithubAction.SkipThisUpdate -> {
settings.updates.skipUpdate.set(action.file.nodeId)
+ deleteCachedApk(action.file.tagName)
}
GithubAction.AutoSearchForUpdate -> {
@@ -165,11 +167,12 @@ class GithubViewModel(
is GithubAction.SkipUpdate -> {
settings.updates.skipUpdate.set(action.file.nodeId)
+ deleteCachedApk(action.file.tagName)
}
is GithubAction.Update -> {
ioSafe {
- installUpdate(action.file.downloadUrl, action.file.digest)
+ installUpdate(action.file)
}
}
}
@@ -193,22 +196,52 @@ class GithubViewModel(
}
}
- private suspend fun installUpdate(url: String, digestPair: String?) = dispatchUpdate {
- updater.update(
- settings = settings,
- url = url,
- digest = DigestPair.parse(digestPair),
- ) { progress, total ->
+ private suspend fun installUpdate(file: GithubReleases.GithubFile) = dispatchUpdate {
+ val activity = com.lagradost.cloudstream3.CommonActivity.activity
+ val cachedFile = activity?.let {
+ ApkUpdater.getCachedUpdateFile(it, file.tagName)
+ }
+
+ if (activity != null && cachedFile != null && cachedFile.exists() && cachedFile.length() > 0) {
updateState {
copy(
dialog = dialog?.copy(
- state = GithubUpdateDialogState.DownloadProgress(
- progress = progress,
- total = total
+ state = GithubUpdateDialogState.InstallProgress(
+ progress = 0,
+ total = cachedFile.length()
)
)
)
}
+ ApkUpdater.installFromFile(activity, cachedFile, settings) { progress, total ->
+ updateState {
+ copy(
+ dialog = dialog?.copy(
+ state = GithubUpdateDialogState.InstallProgress(
+ progress = progress,
+ total = total
+ )
+ )
+ )
+ }
+ }
+ } else {
+ updater.update(
+ settings = settings,
+ url = file.downloadUrl,
+ digest = DigestPair.parse(file.digest),
+ ) { progress, total ->
+ updateState {
+ copy(
+ dialog = dialog?.copy(
+ state = GithubUpdateDialogState.DownloadProgress(
+ progress = progress,
+ total = total
+ )
+ )
+ )
+ }
+ }
}
updateState {
copy(
@@ -266,6 +299,19 @@ class GithubViewModel(
return@dispatchUpdate
}
+ // If automated background search, download the update APK silently in advance
+ if (!fromUser) {
+ val activity = com.lagradost.cloudstream3.CommonActivity.activity
+ if (activity != null) {
+ ApkUpdater.downloadSilently(
+ activity,
+ release.downloadUrl,
+ release.tagName,
+ DigestPair.parse(release.digest)
+ )
+ }
+ }
+
updateState {
copy(
dialog = baseDialog.copy(
@@ -296,4 +342,14 @@ class GithubViewModel(
userName = remoteUserName,
repository = remoteRepository,
)
+
+ private fun deleteCachedApk(tagName: String) {
+ val activity = com.lagradost.cloudstream3.CommonActivity.activity
+ if (activity != null) {
+ val file = ApkUpdater.getCachedUpdateFile(activity, tagName)
+ if (file.exists()) {
+ file.delete()
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 66eb3533c4c..f7197f3871f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -593,6 +593,7 @@
Downloading app update…
Installing app update…
Could not install the new version of the app
+ Installing update…
Legacy
PackageInstaller
App will be updated upon exit