mirror of
https://gitlab.futo.org/keyboard/latinime.git
synced 2024-09-28 14:54:30 +01:00
Auto-update improvements
1. Keep screen awake during download 2. Maintain download state 3. Cache download, don't re-download if cancelled and restarted
This commit is contained in:
parent
f7ca151e18
commit
f134517f11
@ -1,12 +1,15 @@
|
|||||||
package org.futo.inputmethod.latin.uix.settings
|
package org.futo.inputmethod.latin.uix.settings
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.app.PendingIntent.FLAG_MUTABLE
|
import android.app.PendingIntent.FLAG_MUTABLE
|
||||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
import android.app.PendingIntent.getBroadcast
|
import android.app.PendingIntent.getBroadcast
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.ContextWrapper
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageInstaller
|
import android.content.pm.PackageInstaller
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.WindowManager
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@ -18,6 +21,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@ -45,9 +49,30 @@ import org.futo.inputmethod.latin.uix.getSetting
|
|||||||
import org.futo.inputmethod.updates.InstallReceiver
|
import org.futo.inputmethod.updates.InstallReceiver
|
||||||
import org.futo.inputmethod.updates.LAST_UPDATE_CHECK_RESULT
|
import org.futo.inputmethod.updates.LAST_UPDATE_CHECK_RESULT
|
||||||
import org.futo.inputmethod.updates.UpdateResult
|
import org.futo.inputmethod.updates.UpdateResult
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun KeepScreenOn() {
|
||||||
|
val context = LocalContext.current
|
||||||
|
DisposableEffect(Unit) {
|
||||||
|
val window = context.findActivity()?.window
|
||||||
|
window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
onDispose {
|
||||||
|
window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.findActivity(): Activity? {
|
||||||
|
var context = this
|
||||||
|
while (context is ContextWrapper) {
|
||||||
|
if (context is Activity) return context
|
||||||
|
context = context.baseContext
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
private fun InputStream.copyToOutputStream(inputStreamLength: Long, outputStream: OutputStream, onProgress: (Float) -> Unit) {
|
private fun InputStream.copyToOutputStream(inputStreamLength: Long, outputStream: OutputStream, onProgress: (Float) -> Unit) {
|
||||||
val buffer = ByteArray(16384);
|
val buffer = ByteArray(16384);
|
||||||
@ -63,6 +88,13 @@ private fun InputStream.copyToOutputStream(inputStreamLength: Long, outputStream
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object UpdateStatus {
|
||||||
|
val isDownloading = mutableStateOf(false)
|
||||||
|
val downloadText = mutableStateOf("")
|
||||||
|
|
||||||
|
var downloadedUpdate: ByteArray? = null
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun install(scope: CoroutineScope, context: Context, inputStream: InputStream, dataLength: Long, updateStatusText: (String) -> Unit) {
|
private suspend fun install(scope: CoroutineScope, context: Context, inputStream: InputStream, dataLength: Long, updateStatusText: (String) -> Unit) {
|
||||||
var lastProgressText = "";
|
var lastProgressText = "";
|
||||||
var session: PackageInstaller.Session? = null;
|
var session: PackageInstaller.Session? = null;
|
||||||
@ -76,17 +108,25 @@ private suspend fun install(scope: CoroutineScope, context: Context, inputStream
|
|||||||
val sessionId = packageInstaller.createSession(params);
|
val sessionId = packageInstaller.createSession(params);
|
||||||
session = packageInstaller.openSession(sessionId)
|
session = packageInstaller.openSession(sessionId)
|
||||||
|
|
||||||
session.openWrite("package", 0, dataLength).use { sessionStream ->
|
if(UpdateStatus.downloadedUpdate == null) {
|
||||||
inputStream.copyToOutputStream(dataLength, sessionStream) { progress ->
|
ByteArrayOutputStream(dataLength.toInt()).use { outputStream ->
|
||||||
val progressText = "${(progress * 100.0f).toInt()}%";
|
inputStream.copyToOutputStream(dataLength, outputStream) { progress ->
|
||||||
if (lastProgressText != progressText) {
|
val progressText = "${(progress * 100.0f).toInt()}%";
|
||||||
lastProgressText = progressText;
|
if (lastProgressText != progressText) {
|
||||||
|
lastProgressText = progressText
|
||||||
|
|
||||||
//TODO: Use proper scope
|
updateStatusText("Downloading... $progressText")
|
||||||
//GlobalScope.launch(Dispatchers.Main) {
|
}
|
||||||
//_textProgress.text = progressText;
|
|
||||||
//};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateStatus.downloadedUpdate = outputStream.toByteArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
session.openWrite("package", 0, dataLength).use { sessionStream ->
|
||||||
|
UpdateStatus.downloadedUpdate!!.inputStream().use { byteStream ->
|
||||||
|
byteStream.copyToOutputStream(dataLength, sessionStream) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
session.fsync(sessionStream);
|
session.fsync(sessionStream);
|
||||||
@ -120,6 +160,8 @@ private suspend fun install(scope: CoroutineScope, context: Context, inputStream
|
|||||||
|
|
||||||
@DelicateCoroutinesApi
|
@DelicateCoroutinesApi
|
||||||
private suspend fun downloadAndInstall(scope: CoroutineScope, context: Context, updateResult: UpdateResult, updateStatusText: (String) -> Unit) = GlobalScope.launch(Dispatchers.IO) {
|
private suspend fun downloadAndInstall(scope: CoroutineScope, context: Context, updateResult: UpdateResult, updateStatusText: (String) -> Unit) = GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
UpdateStatus.isDownloading.value = true
|
||||||
|
|
||||||
var inputStream: InputStream? = null;
|
var inputStream: InputStream? = null;
|
||||||
try {
|
try {
|
||||||
val httpClient = OkHttpClient()
|
val httpClient = OkHttpClient()
|
||||||
@ -141,6 +183,7 @@ private suspend fun downloadAndInstall(scope: CoroutineScope, context: Context,
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
inputStream?.close();
|
inputStream?.close();
|
||||||
|
UpdateStatus.isDownloading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,9 +201,9 @@ fun UpdateDialog(navController: NavHostController) {
|
|||||||
UpdateResult(123, "abc", "1.2.3")
|
UpdateResult(123, "abc", "1.2.3")
|
||||||
}
|
}
|
||||||
|
|
||||||
val isDownloading = remember { mutableStateOf(false) }
|
val isDownloading = UpdateStatus.isDownloading
|
||||||
val showSpinner = remember { mutableStateOf(true) }
|
val showSpinner = remember { mutableStateOf(true) }
|
||||||
val statusText = remember { mutableStateOf("Downloading ${lastUpdateResult?.nextVersionString}") }
|
val statusText = UpdateStatus.downloadText
|
||||||
|
|
||||||
if(lastUpdateResult == null || !lastUpdateResult.isNewer()) {
|
if(lastUpdateResult == null || !lastUpdateResult.isNewer()) {
|
||||||
InfoDialog(title = "Up-to-date", body = "As of the last update check, the app is up to date.")
|
InfoDialog(title = "Up-to-date", body = "As of the last update check, the app is up to date.")
|
||||||
@ -174,6 +217,7 @@ fun UpdateDialog(navController: NavHostController) {
|
|||||||
},
|
},
|
||||||
text = {
|
text = {
|
||||||
if(isDownloading.value) {
|
if(isDownloading.value) {
|
||||||
|
KeepScreenOn()
|
||||||
Column(modifier = Modifier.fillMaxWidth()) {
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
if(showSpinner.value) {
|
if(showSpinner.value) {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
@ -194,10 +238,12 @@ fun UpdateDialog(navController: NavHostController) {
|
|||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = {
|
TextButton(onClick = {
|
||||||
isDownloading.value = true
|
|
||||||
GlobalScope.launch { downloadAndInstall(scope.lifecycleScope, context, lastUpdateResult) {
|
GlobalScope.launch { downloadAndInstall(scope.lifecycleScope, context, lastUpdateResult) {
|
||||||
statusText.value = it
|
statusText.value = it
|
||||||
showSpinner.value = false
|
|
||||||
|
if(!it.endsWith("%")) {
|
||||||
|
showSpinner.value = false
|
||||||
|
}
|
||||||
} }
|
} }
|
||||||
}, enabled = !isDownloading.value) {
|
}, enabled = !isDownloading.value) {
|
||||||
Text(stringResource(R.string.update))
|
Text(stringResource(R.string.update))
|
||||||
|
Loading…
Reference in New Issue
Block a user