Skip to content

Commit c40210c

Browse files
committed
Check update in SettingsPage
1 parent 185adbd commit c40210c

File tree

5 files changed

+133
-2
lines changed

5 files changed

+133
-2
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.paulcoding.hviewer.model
2+
3+
data class Release(
4+
val url: String,
5+
val id: Int,
6+
val tag_name: String,
7+
val assets: List<Asset>,
8+
9+
)
10+
11+
data class Asset(
12+
val browser_download_url: String,
13+
)

app/src/main/java/com/paulcoding/hviewer/network/Github.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package com.paulcoding.hviewer.network
22

3+
import android.content.Context
34
import com.google.gson.Gson
5+
import com.paulcoding.hviewer.BuildConfig
46
import com.paulcoding.hviewer.MainApp.Companion.appContext
57
import com.paulcoding.hviewer.R
68
import com.paulcoding.hviewer.helper.extractTarGzFromResponseBody
79
import com.paulcoding.hviewer.helper.log
810
import com.paulcoding.hviewer.helper.readConfigFile
11+
import com.paulcoding.hviewer.model.Release
912
import com.paulcoding.hviewer.model.SiteConfigs
1013
import com.paulcoding.hviewer.preference.Preferences
1114
import io.ktor.client.call.body
@@ -14,6 +17,7 @@ import io.ktor.client.statement.readRawBytes
1417
import io.ktor.http.HttpStatusCode
1518
import kotlinx.coroutines.Dispatchers
1619
import kotlinx.coroutines.withContext
20+
import java.io.File
1721

1822
object Github {
1923
@Throws(Exception::class)
@@ -111,6 +115,37 @@ object Github {
111115
return null
112116
}
113117
}
118+
119+
suspend fun checkForUpdate(
120+
currentVersion: String,
121+
onUpdateAvailable: (String, String) -> Unit
122+
) {
123+
val (owner, repo) = parseRepo(BuildConfig.REPO_URL)
124+
val url = "https://api.github.com/repos/${owner}/${repo}/releases/latest"
125+
ktorClient.use { client ->
126+
val jsonObject: Release = client.get(url).body()
127+
val latestVersion = jsonObject.tag_name.substring(1)
128+
val downloadUrl = jsonObject.assets[0].browser_download_url
129+
if (latestVersion != currentVersion) {
130+
onUpdateAvailable(latestVersion, downloadUrl)
131+
}
132+
}
133+
}
134+
135+
suspend fun downloadApk(
136+
context: Context,
137+
downloadUrl: String,
138+
onDownloadComplete: (File) -> Unit
139+
) {
140+
val file = File(context.cacheDir, "latest.apk")
141+
ktorClient.use { client ->
142+
val input = client.get(downloadUrl).readRawBytes().inputStream()
143+
file.outputStream().use { output ->
144+
input.copyTo(output)
145+
}
146+
}
147+
onDownloadComplete(file)
148+
}
114149
}
115150

116151

app/src/main/java/com/paulcoding/hviewer/ui/page/AppViewModel.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.paulcoding.hviewer.ui.page
22

3+
import android.content.Context
4+
import android.content.Intent
5+
import androidx.core.content.FileProvider
36
import androidx.lifecycle.ViewModel
47
import androidx.lifecycle.viewModelScope
58
import com.paulcoding.hviewer.BuildConfig
@@ -66,6 +69,7 @@ class AppViewModel : ViewModel() {
6669
val siteConfigs: SiteConfigs? = appContext.readConfigFile<SiteConfigs>().getOrNull(),
6770
val error: Throwable? = null,
6871
val checkingForUpdateScripts: Boolean = false,
72+
val updatingApk: Boolean = false,
6973
)
7074

7175
private fun setError(throwable: Throwable) {
@@ -185,4 +189,31 @@ class AppViewModel : ViewModel() {
185189
}
186190
}
187191
}
192+
193+
fun checkForUpdate(currentVersion: String, onUpdateAvailable: (String, String) -> Unit) {
194+
viewModelScope.launch {
195+
_stateFlow.update { it.copy(updatingApk = true) }
196+
Github.checkForUpdate(currentVersion, onUpdateAvailable)
197+
_stateFlow.update { it.copy(updatingApk = false) }
198+
}
199+
}
200+
201+
fun downloadAndInstallApk(context: Context, downloadUrl: String) {
202+
viewModelScope.launch {
203+
_stateFlow.update { it.copy(updatingApk = true) }
204+
Github.downloadApk(context, downloadUrl) { file ->
205+
val uri = FileProvider.getUriForFile(
206+
context,
207+
"${context.packageName}.fileprovider",
208+
file
209+
)
210+
val intent = Intent(Intent.ACTION_VIEW).apply {
211+
setDataAndType(uri, "application/vnd.android.package-archive")
212+
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
213+
}
214+
context.startActivity(intent)
215+
}
216+
_stateFlow.update { it.copy(updatingApk = false) }
217+
}
218+
}
188219
}

app/src/main/java/com/paulcoding/hviewer/ui/page/settings/SettingsPage.kt

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.paulcoding.hviewer.ui.page.settings
22

33
import androidx.activity.ComponentActivity
4+
import androidx.compose.foundation.background
45
import androidx.compose.foundation.clickable
56
import androidx.compose.foundation.interaction.MutableInteractionSource
67
import androidx.compose.foundation.layout.Arrangement
8+
import androidx.compose.foundation.layout.Box
79
import androidx.compose.foundation.layout.Column
810
import androidx.compose.foundation.layout.Row
911
import androidx.compose.foundation.layout.RowScope
@@ -16,10 +18,12 @@ import androidx.compose.material.icons.Icons
1618
import androidx.compose.material.icons.outlined.BugReport
1719
import androidx.compose.material.icons.outlined.Description
1820
import androidx.compose.material.icons.outlined.Edit
21+
import androidx.compose.material.icons.outlined.Update
1922
import androidx.compose.material3.ExperimentalMaterial3Api
2023
import androidx.compose.material3.HorizontalDivider
2124
import androidx.compose.material3.Icon
2225
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
26+
import androidx.compose.material3.MaterialTheme
2327
import androidx.compose.material3.Scaffold
2428
import androidx.compose.material3.Switch
2529
import androidx.compose.material3.SwitchColors
@@ -35,17 +39,22 @@ import androidx.compose.runtime.remember
3539
import androidx.compose.runtime.setValue
3640
import androidx.compose.ui.Alignment
3741
import androidx.compose.ui.Modifier
42+
import androidx.compose.ui.graphics.Color
3843
import androidx.compose.ui.platform.LocalContext
3944
import androidx.compose.ui.res.stringResource
4045
import androidx.compose.ui.text.font.FontWeight
4146
import androidx.compose.ui.unit.dp
4247
import androidx.compose.ui.unit.sp
48+
import com.paulcoding.hviewer.BuildConfig
4349
import com.paulcoding.hviewer.R
4450
import com.paulcoding.hviewer.extensions.setSecureScreen
4551
import com.paulcoding.hviewer.helper.makeToast
4652
import com.paulcoding.hviewer.preference.Preferences
53+
import com.paulcoding.hviewer.ui.component.ConfirmDialog
4754
import com.paulcoding.hviewer.ui.component.H7Tap
4855
import com.paulcoding.hviewer.ui.component.HBackIcon
56+
import com.paulcoding.hviewer.ui.component.HIcon
57+
import com.paulcoding.hviewer.ui.component.HLoading
4958
import com.paulcoding.hviewer.ui.page.AppViewModel
5059

5160
@OptIn(ExperimentalMaterial3Api::class)
@@ -63,6 +72,8 @@ fun SettingsPage(
6372
val window = (context as ComponentActivity).window
6473
var lockModalVisible by remember { mutableStateOf(false) }
6574
var appLockEnabled by remember { mutableStateOf(Preferences.pin.isNotEmpty()) }
75+
var newVersion by remember { mutableStateOf("") }
76+
var downloadUrl by remember { mutableStateOf("") }
6677
val scrollState = rememberScrollState()
6778

6879
fun onAppLockEnabled(pin: String) {
@@ -147,8 +158,19 @@ fun SettingsPage(
147158
}
148159
}
149160

150-
H7Tap(modifier = Modifier.align(Alignment.CenterHorizontally)) {
151-
appViewModel.setDevMode(it)
161+
Row(
162+
modifier = Modifier.align(Alignment.CenterHorizontally),
163+
verticalAlignment = Alignment.CenterVertically
164+
) {
165+
H7Tap() {
166+
appViewModel.setDevMode(it)
167+
}
168+
HIcon(Icons.Outlined.Update, tint = MaterialTheme.colorScheme.primary) {
169+
appViewModel.checkForUpdate(BuildConfig.VERSION_NAME) { version, url ->
170+
newVersion = version
171+
downloadUrl = url
172+
}
173+
}
152174
}
153175
}
154176
}
@@ -169,6 +191,33 @@ fun SettingsPage(
169191
if (lockModalVisible) LockModal(onDismiss = { lockModalVisible = false }) {
170192
onAppLockEnabled(it)
171193
}
194+
195+
ConfirmDialog(
196+
showDialog = newVersion.isNotEmpty(),
197+
title = "Update Available",
198+
text = newVersion,
199+
confirmColor = MaterialTheme.colorScheme.primary,
200+
dismissColor = MaterialTheme.colorScheme.onBackground,
201+
onDismiss = {
202+
newVersion = ""
203+
},
204+
onConfirm = {
205+
appViewModel.downloadAndInstallApk(context, downloadUrl)
206+
newVersion = ""
207+
}
208+
)
209+
210+
if (appState.updatingApk) {
211+
Box(
212+
modifier = Modifier
213+
.fillMaxSize()
214+
.background(Color.Black.copy(alpha = 0.5f))
215+
) {
216+
Box(modifier = Modifier.align(Alignment.Center)) {
217+
HLoading()
218+
}
219+
}
220+
}
172221
}
173222

174223
@Composable

app/src/main/res/xml/provider_paths.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33
<external-path
44
name="download_path"
55
path="." />
6+
<cache-path
7+
name="cache"
8+
path="." />
69
</paths>

0 commit comments

Comments
 (0)