Skip to content

Commit 72ef107

Browse files
committed
Download Progress
* Disentangle MainViewModel. * Add Download Progress for Play Source and GitHub source. * Improve play token refresh. * TV UI is now default. * ApkMirror is disabled by default. * Random delay on Alarm is reduced to -5 +5 if ApkMirror is disabled.
1 parent b10fb75 commit 72ef107

26 files changed

+343
-189
lines changed

app/src/main/kotlin/com/apkupdater/data/snack/ISnack.kt

-3
This file was deleted.

app/src/main/kotlin/com/apkupdater/data/snack/InstallSnack.kt

-9
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.apkupdater.data.snack
2+
3+
import androidx.compose.material3.SnackbarDuration
4+
import androidx.compose.material3.SnackbarVisuals
5+
6+
7+
class TextSnack(
8+
override val message: String,
9+
override val actionLabel: String? = null,
10+
override val duration: SnackbarDuration = SnackbarDuration.Short,
11+
override val withDismissAction: Boolean = true
12+
): SnackbarVisuals
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.apkupdater.data.ui
2+
3+
data class AppInstallProgress(val id: Int, val progress: Long = 0L, val total: Long = 0L)

app/src/main/kotlin/com/apkupdater/data/ui/AppUpdate.kt

+11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ data class AppUpdate(
1414
val link: Link = Link.Empty,
1515
val whatsNew: String = "",
1616
val isInstalling: Boolean = false,
17+
val total: Long = 0L,
18+
val progress: Long = 0L,
1719
val id: Int = "${source.name}.$packageName.$versionCode.$version".hashCode()
1820
)
1921

@@ -32,3 +34,12 @@ fun MutableList<AppUpdate>.removeId(id: Int): List<AppUpdate> {
3234
if (index != -1) this.removeAt(index)
3335
return this
3436
}
37+
38+
fun MutableList<AppUpdate>.setProgress(progress: AppInstallProgress): MutableList<AppUpdate> {
39+
val index = this.indexOf(progress.id)
40+
if (index != -1) {
41+
if (progress.progress != 0L) this[index] = this[index].copy(progress = progress.progress)
42+
if (progress.total != 0L) this[index] = this[index].copy(total = progress.total)
43+
}
44+
return this
45+
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.apkupdater.data.ui
22

3+
import com.aurora.gplayapi.data.models.File
4+
35

46
sealed class Link {
57
data object Empty: Link()
6-
data class Url(val link: String): Link()
8+
data class Url(val link: String, val size: Long = 0L): Link()
79
data class Xapk(val link: String): Link()
8-
data class Play(val getInstallFiles: () -> List<String>): Link()
10+
data class Play(val getInstallFiles: () -> List<File>): Link()
911
}

app/src/main/kotlin/com/apkupdater/di/MainModule.kt

+21-6
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,14 @@ import com.apkupdater.service.AptoideService
2222
import com.apkupdater.service.FdroidService
2323
import com.apkupdater.service.GitHubService
2424
import com.apkupdater.service.GitLabService
25+
import com.apkupdater.util.Badger
2526
import com.apkupdater.util.Clipboard
2627
import com.apkupdater.util.Downloader
28+
import com.apkupdater.util.InstallLog
2729
import com.apkupdater.util.SessionInstaller
30+
import com.apkupdater.util.SnackBar
31+
import com.apkupdater.util.Stringer
32+
import com.apkupdater.util.Themer
2833
import com.apkupdater.util.UpdatesNotification
2934
import com.apkupdater.util.addUserAgentInterceptor
3035
import com.apkupdater.util.isAndroidTv
@@ -164,16 +169,26 @@ val mainModule = module {
164169

165170
single { Clipboard(androidContext()) }
166171

167-
single { SessionInstaller(get()) }
172+
single { SessionInstaller(get(), get()) }
168173

169-
viewModel { MainViewModel(get()) }
174+
single { SnackBar() }
170175

171-
viewModel { parameters -> AppsViewModel(parameters.get(), get(), get()) }
176+
single { Badger() }
172177

173-
viewModel { parameters -> UpdatesViewModel(parameters.get(), get(), get(), get(), get()) }
178+
single { Themer(get()) }
174179

175-
viewModel { parameters -> SettingsViewModel(parameters.get(), get(), get(), WorkManager.getInstance(get()), get(), get()) }
180+
single { Stringer(androidContext()) }
176181

177-
viewModel { parameters -> SearchViewModel(parameters.get(), get(), get(), get(), get()) }
182+
single { InstallLog() }
183+
184+
viewModel { MainViewModel(get(), get()) }
185+
186+
viewModel { AppsViewModel(get(), get(), get()) }
187+
188+
viewModel { UpdatesViewModel(get(), get(), get(), get(), get(), get(), get(), get()) }
189+
190+
viewModel { SettingsViewModel(get(), get(), WorkManager.getInstance(get()), get(), get(), get(), get()) }
191+
192+
viewModel { SearchViewModel(get(), get(), get(), get(), get(), get(), get(), get()) }
178193

179194
}

app/src/main/kotlin/com/apkupdater/prefs/Prefs.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ class Prefs(
1818
val excludeStore = boolean("excludeStore", defValue = false, backed = true)
1919
val portraitColumns = int("portraitColumns", 3, true)
2020
val landscapeColumns = int("landscapeColumns", 6, true)
21-
val playTextAnimations = boolean("playTextAnimaions", defValue = true, backed = true)
21+
val playTextAnimations = boolean("playTextAnimations", defValue = true, backed = true)
2222
val ignoreAlpha = boolean("ignoreAlpha", defValue = true, backed = true)
2323
val ignoreBeta = boolean("ignoreBeta", defValue = true, backed = true)
2424
val ignorePreRelease = boolean("ignorePreRelease", defValue = true, backed = true)
2525
val useSafeStores = boolean("useSafeStores", defValue = true, backed = true)
26-
val useApkMirror = boolean("useApkMirror", defValue = !isAndroidTv, backed = true)
26+
val useApkMirror = boolean("useApkMirror", defValue = false, backed = true)
2727
val useGitHub = boolean("useGitHub", defValue = true, backed = true)
2828
val useGitLab = boolean("useGitLab", defValue = true, backed = true)
2929
val useFdroid = boolean("useFdroid", defValue = true, backed = true)
@@ -34,9 +34,10 @@ class Prefs(
3434
val enableAlarm = boolean("enableAlarm", defValue = false, backed = true)
3535
val alarmHour = int("alarmHour", defValue = 12, backed = true)
3636
val alarmFrequency = int("alarmFrequency", 0, backed = true)
37-
val androidTvUi = boolean("androidTvUi", defValue = isAndroidTv, backed = true)
37+
val androidTvUi = boolean("androidTvUi", defValue = true, backed = true)
3838
val rootInstall = boolean("rootInstall", defValue = false, backed = true)
3939
val theme = int("theme", defValue = 0, backed = true)
4040
val lastTab = string("lastTab", defValue = Screen.Updates.route, backed = true)
4141
val playAuthData = json("playAuthData", AuthData("", ""), true)
42+
val lastPlayCheck = long("lastPlayCheck", 0L, true)
4243
}

app/src/main/kotlin/com/apkupdater/repository/GitHubRepository.kt

+9-9
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class GitHubRepository(
121121
versionCode = 0L,
122122
oldVersionCode = app?.versionCode ?: 0L,
123123
source = GitHubSource,
124-
link = Link.Url(findApkAssetArch(releases[0].assets, extra)),
124+
link = findApkAssetArch(releases[0].assets, extra).let { Link.Url(it.browser_download_url, it.size) },
125125
whatsNew = releases[0].body,
126126
iconUri = if (apps == null) Uri.parse(releases[0].author.avatar_url) else Uri.EMPTY
127127
)))
@@ -154,49 +154,49 @@ class GitHubRepository(
154154
private fun findApkAssetArch(
155155
assets: List<GitHubReleaseAsset>,
156156
extra: Regex?
157-
): String {
157+
): GitHubReleaseAsset {
158158
val apks = assets
159159
.filter { it.browser_download_url.endsWith(".apk", true) }
160160
.filter { filterExtra(it, extra) }
161161

162162
when {
163-
apks.isEmpty() -> return ""
164-
apks.size == 1 -> return apks.first().browser_download_url
163+
apks.isEmpty() -> return GitHubReleaseAsset(0L, "")
164+
apks.size == 1 -> return apks.first()
165165
else -> {
166166
// Try to match exact arch
167167
Build.SUPPORTED_ABIS.forEach { arch ->
168168
apks.forEach { apk ->
169169
if (apk.browser_download_url.contains(arch, true)) {
170-
return apk.browser_download_url
170+
return apk
171171
}
172172
}
173173
}
174174
// Try to match arm64
175175
if (Build.SUPPORTED_ABIS.contains("arm64-v8a")) {
176176
apks.forEach { apk ->
177177
if (apk.browser_download_url.contains("arm64", true)) {
178-
return apk.browser_download_url
178+
return apk
179179
}
180180
}
181181
}
182182
// Try to match x64
183183
if (Build.SUPPORTED_ABIS.contains("x86_64")) {
184184
apks.forEach { apk ->
185185
if (apk.browser_download_url.contains("x64", true)) {
186-
return apk.browser_download_url
186+
return apk
187187
}
188188
}
189189
}
190190
// Try to match arm
191191
if (Build.SUPPORTED_ABIS.contains("armeabi-v7a")) {
192192
apks.forEach { apk ->
193193
if (apk.browser_download_url.contains("arm", true)) {
194-
return apk.browser_download_url
194+
return apk
195195
}
196196
}
197197
}
198198
// If no match, return biggest apk in the hope it's universal
199-
return apks.maxByOrNull { it.size }?.browser_download_url.orEmpty()
199+
return apks.maxByOrNull { it.size } ?: GitHubReleaseAsset(0L, "")
200200
}
201201
}
202202
}

app/src/main/kotlin/com/apkupdater/repository/PlayRepository.kt

+18-20
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,10 @@ import com.aurora.gplayapi.helpers.AppDetailsHelper
2020
import com.aurora.gplayapi.helpers.PurchaseHelper
2121
import com.aurora.gplayapi.helpers.SearchHelper
2222
import com.google.gson.Gson
23-
import kotlinx.coroutines.CoroutineScope
24-
import kotlinx.coroutines.Dispatchers
25-
import kotlinx.coroutines.FlowPreview
2623
import kotlinx.coroutines.flow.catch
27-
import kotlinx.coroutines.flow.debounce
28-
import kotlinx.coroutines.flow.filter
2924
import kotlinx.coroutines.flow.flow
30-
import kotlinx.coroutines.flow.launchIn
31-
import kotlinx.coroutines.flow.onEach
3225

3326

34-
@OptIn(FlowPreview::class)
3527
class PlayRepository(
3628
private val context: Context,
3729
private val gson: Gson,
@@ -41,16 +33,8 @@ class PlayRepository(
4133
const val AUTH_URL = "https://auroraoss.com/api/auth"
4234
}
4335

44-
init {
45-
// TODO: Needs testing.
46-
PlayHttpClient.responseCode
47-
.filter { it == 401 }
48-
.debounce(60 * 5 * 1_000)
49-
.onEach { runCatching { refreshAuth() }.getOrNull() }
50-
.launchIn(CoroutineScope(Dispatchers.IO))
51-
}
52-
5336
private fun refreshAuth(): AuthData {
37+
Log.i("PlayRepository", "Refreshing token.")
5438
val properties = NativeDeviceInfoProvider(context).getNativeDeviceProperties()
5539
val playResponse = PlayHttpClient.postAuth(AUTH_URL, gson.toJson(properties).toByteArray())
5640
if (playResponse.isSuccessful) {
@@ -66,6 +50,21 @@ class PlayRepository(
6650
if (savedData.email.isEmpty()) {
6751
return refreshAuth()
6852
}
53+
if (System.currentTimeMillis() - prefs.lastPlayCheck.get() > 60 * 60 * 1_000) {
54+
// Update check time
55+
prefs.lastPlayCheck.put(System.currentTimeMillis())
56+
Log.i("PlayRepository", "Checking token validity.")
57+
58+
// 1h has passed check if token still works
59+
val app = AppDetailsHelper(savedData)
60+
.using(PlayHttpClient)
61+
.getAppByPackageName("com.google.android.gm")
62+
63+
if (app.packageName.isEmpty()) {
64+
return refreshAuth()
65+
}
66+
Log.i("PlayRepository", "Token still valid.")
67+
}
6968
return savedData
7069
}
7170

@@ -77,7 +76,7 @@ class PlayRepository(
7776
.using(PlayHttpClient)
7877
.searchResults(text)
7978
.appList
80-
.take(5)
79+
.take(10)
8180
.map { it.toAppUpdate(::getInstallFiles) }
8281
emit(Result.success(updates))
8382
} else {
@@ -118,12 +117,11 @@ class PlayRepository(
118117
.using(PlayHttpClient)
119118
.purchase(app.packageName, app.versionCode, app.offerType)
120119
.filter { it.type == File.FileType.BASE || it.type == File.FileType.SPLIT }
121-
.map { it.url }
122120

123121
}
124122

125123
fun App.toAppUpdate(
126-
getInstallFiles: (App) -> List<String>,
124+
getInstallFiles: (App) -> List<File>,
127125
oldVersion: String = "",
128126
oldVersionCode: Long = 0L
129127
) = AppUpdate(

app/src/main/kotlin/com/apkupdater/ui/component/TvComponents.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import com.apkupdater.data.ui.AppInstalled
3232
import com.apkupdater.data.ui.AppUpdate
3333
import com.apkupdater.data.ui.Source
3434
import com.apkupdater.util.getAppName
35+
import com.apkupdater.util.to2f
3536
import com.apkupdater.util.toAnnotatedString
3637

3738

@@ -79,7 +80,12 @@ fun TvInstallButton(
7980
onClick = { onInstall(app.packageName) }
8081
) {
8182
if (app.isInstalling) {
82-
CircularProgressIndicator(Modifier.size(24.dp))
83+
if (app.total != 0L && app.progress != 0L) {
84+
val p = (app.progress.toFloat() / app.total) * 100f
85+
Text("${p.to2f()}%")
86+
} else {
87+
CircularProgressIndicator(Modifier.size(24.dp))
88+
}
8389
} else {
8490
Text(stringResource(R.string.install_cd))
8591
}

0 commit comments

Comments
 (0)