Skip to content

Commit b10fb75

Browse files
committed
Play Source
* Search: Add support for normal search. If you type a package name it will search for the specific package name, otherwise it will perform a normal search. * Play token refresh after 401. Five min limit. * Refactor install process to allow for different types of installs. * Settings: Add button to copy app logs. * Fix crash when search returns apps with no name.
1 parent 8886145 commit b10fb75

20 files changed

+156
-86
lines changed

app/src/main/kotlin/com/apkupdater/data/apkmirror/AppExistsResponseApk.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.apkupdater.data.apkmirror
33
import com.apkupdater.data.ui.ApkMirrorSource
44
import com.apkupdater.data.ui.AppInstalled
55
import com.apkupdater.data.ui.AppUpdate
6+
import com.apkupdater.data.ui.Link
67
import com.google.gson.annotations.SerializedName
78

89
data class AppExistsResponseApk(
@@ -29,6 +30,6 @@ fun AppExistsResponseApk.toAppUpdate(app: AppInstalled, release: AppExistsRespon
2930
app.versionCode,
3031
ApkMirrorSource,
3132
app.iconUri,
32-
"https://www.apkmirror.com$link",
33+
Link.Url("https://www.apkmirror.com$link"),
3334
release.whatsNew.orEmpty()
3435
)

app/src/main/kotlin/com/apkupdater/data/apkpure/AppUpdateResponse.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.net.Uri
44
import com.apkupdater.data.ui.ApkPureSource
55
import com.apkupdater.data.ui.AppInstalled
66
import com.apkupdater.data.ui.AppUpdate
7+
import com.apkupdater.data.ui.Link
78

89

910
data class AppUpdateResponse(
@@ -29,6 +30,6 @@ fun AppUpdateResponse.toAppUpdate(
2930
app?.versionCode ?: 0L,
3031
ApkPureSource,
3132
if (app == null) Uri.parse(icon.thumbnail.url) else Uri.EMPTY,
32-
asset.url.replace("http://", "https://"),
33+
if (asset.url.contains("/XAPK")) Link.Xapk(asset.url.replace("http://", "https://")) else Link.Url(asset.url.replace("http://", "https://")),
3334
if (app == null) description_short else whatsnew
3435
)

app/src/main/kotlin/com/apkupdater/data/aptoide/App.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import androidx.core.net.toUri
55
import com.apkupdater.data.ui.AppInstalled
66
import com.apkupdater.data.ui.AppUpdate
77
import com.apkupdater.data.ui.AptoideSource
8+
import com.apkupdater.data.ui.Link
89
import com.google.gson.annotations.SerializedName
910

1011
data class App(
@@ -24,5 +25,5 @@ fun App.toAppUpdate(app: AppInstalled?) = AppUpdate(
2425
oldVersionCode = app?.versionCode ?: 0L,
2526
source = AptoideSource,
2627
iconUri = icon?.toUri() ?: Uri.EMPTY,
27-
link = file.path
28+
link = Link.Url(file.path)
2829
)

app/src/main/kotlin/com/apkupdater/data/fdroid/FdroidUpdate.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.apkupdater.data.fdroid
33
import androidx.core.net.toUri
44
import com.apkupdater.data.ui.AppInstalled
55
import com.apkupdater.data.ui.AppUpdate
6+
import com.apkupdater.data.ui.Link
67
import com.apkupdater.data.ui.Source
78

89
data class FdroidUpdate(
@@ -22,6 +23,6 @@ fun FdroidUpdate.toAppUpdate(current: AppInstalled?, source: Source, url: String
2223
"https://f-droid.org/assets/ic_repo_app_default.png".toUri()
2324
else
2425
"${url}icons-640/${app.icon}".toUri(),
25-
"$url${apk.apkName}",
26+
Link.Url("$url${apk.apkName}"),
2627
if (current != null) app.localized["en-US"]?.whatsNew.orEmpty() else app.localized["en-US"]?.summary.orEmpty()
2728
)
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
package com.apkupdater.data.snack
22

3+
import androidx.annotation.StringRes
4+
35
data class InstallSnack(val success: Boolean, val name: String): ISnack
6+
7+
data class TextSnack(val message: String): ISnack
8+
9+
data class TextIdSnack(@StringRes val id: Int): ISnack

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ data class AppUpdate(
1111
val oldVersionCode: Long,
1212
val source: Source,
1313
val iconUri: Uri = Uri.EMPTY,
14-
val link: String = "",
14+
val link: Link = Link.Empty,
1515
val whatsNew: String = "",
1616
val isInstalling: Boolean = false,
1717
val id: Int = "${source.name}.$packageName.$versionCode.$version".hashCode()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.apkupdater.data.ui
2+
3+
4+
sealed class Link {
5+
data object Empty: Link()
6+
data class Url(val link: String): Link()
7+
data class Xapk(val link: String): Link()
8+
data class Play(val getInstallFiles: () -> List<String>): Link()
9+
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.apkupdater.data.apkmirror.toAppUpdate
1111
import com.apkupdater.data.ui.ApkMirrorSource
1212
import com.apkupdater.data.ui.AppInstalled
1313
import com.apkupdater.data.ui.AppUpdate
14+
import com.apkupdater.data.ui.Link
1415
import com.apkupdater.data.ui.getApp
1516
import com.apkupdater.data.ui.getPackageNames
1617
import com.apkupdater.data.ui.getSignature
@@ -63,7 +64,7 @@ class ApkMirrorRepository(
6364
val result = (0 until a.size).map {
6465
AppUpdate(
6566
name = h5[it].attr("title"),
66-
link = "$baseUrl${h5[it].selectFirst("a")?.attr("href")}",
67+
link = Link.Url("$baseUrl${h5[it].selectFirst("a")?.attr("href")}"),
6768
iconUri = Uri.parse("$baseUrl${img[it].attr("src")}".replace("=32", "=128")),
6869
version = "?",
6970
oldVersion = "?",

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.apkupdater.data.github.GitHubReleaseAsset
1010
import com.apkupdater.data.ui.AppInstalled
1111
import com.apkupdater.data.ui.AppUpdate
1212
import com.apkupdater.data.ui.GitHubSource
13+
import com.apkupdater.data.ui.Link
1314
import com.apkupdater.data.ui.getApp
1415
import com.apkupdater.prefs.Prefs
1516
import com.apkupdater.service.GitHubService
@@ -82,7 +83,7 @@ class GitHubRepository(
8283
versionCode = versions.second,
8384
oldVersionCode = BuildConfig.VERSION_CODE.toLong(),
8485
source = GitHubSource,
85-
link = releases[0].assets[0].browser_download_url,
86+
link = Link.Url(releases[0].assets[0].browser_download_url),
8687
whatsNew = releases[0].body
8788
)))
8889
} else {
@@ -120,7 +121,7 @@ class GitHubRepository(
120121
versionCode = 0L,
121122
oldVersionCode = app?.versionCode ?: 0L,
122123
source = GitHubSource,
123-
link = findApkAssetArch(releases[0].assets, extra),
124+
link = Link.Url(findApkAssetArch(releases[0].assets, extra)),
124125
whatsNew = releases[0].body,
125126
iconUri = if (apps == null) Uri.parse(releases[0].author.avatar_url) else Uri.EMPTY
126127
)))

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.apkupdater.data.gitlab.GitLabRelease
77
import com.apkupdater.data.ui.AppInstalled
88
import com.apkupdater.data.ui.AppUpdate
99
import com.apkupdater.data.ui.GitLabSource
10+
import com.apkupdater.data.ui.Link
1011
import com.apkupdater.data.ui.getApp
1112
import com.apkupdater.prefs.Prefs
1213
import com.apkupdater.service.GitLabService
@@ -60,7 +61,7 @@ class GitLabRepository(
6061
versionCode = 0L,
6162
oldVersionCode = app?.versionCode ?: 0L,
6263
source = GitLabSource,
63-
link = getApkUrl(packageName, releases[0]),
64+
link = Link.Url(getApkUrl(packageName, releases[0])),
6465
whatsNew = releases[0].description,
6566
iconUri = if (apps == null) Uri.parse(releases[0].author.avatar_url) else Uri.EMPTY
6667
)))

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

+71-26
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import android.net.Uri
55
import android.util.Log
66
import com.apkupdater.data.ui.AppInstalled
77
import com.apkupdater.data.ui.AppUpdate
8+
import com.apkupdater.data.ui.Link
89
import com.apkupdater.data.ui.PlaySource
910
import com.apkupdater.data.ui.getPackageNames
1011
import com.apkupdater.data.ui.getVersion
@@ -14,71 +15,115 @@ import com.apkupdater.util.play.NativeDeviceInfoProvider
1415
import com.apkupdater.util.play.PlayHttpClient
1516
import com.aurora.gplayapi.data.models.App
1617
import com.aurora.gplayapi.data.models.AuthData
18+
import com.aurora.gplayapi.data.models.File
1719
import com.aurora.gplayapi.helpers.AppDetailsHelper
1820
import com.aurora.gplayapi.helpers.PurchaseHelper
21+
import com.aurora.gplayapi.helpers.SearchHelper
1922
import com.google.gson.Gson
23+
import kotlinx.coroutines.CoroutineScope
24+
import kotlinx.coroutines.Dispatchers
25+
import kotlinx.coroutines.FlowPreview
2026
import kotlinx.coroutines.flow.catch
27+
import kotlinx.coroutines.flow.debounce
28+
import kotlinx.coroutines.flow.filter
2129
import kotlinx.coroutines.flow.flow
30+
import kotlinx.coroutines.flow.launchIn
31+
import kotlinx.coroutines.flow.onEach
2232

2333

34+
@OptIn(FlowPreview::class)
2435
class PlayRepository(
2536
private val context: Context,
2637
private val gson: Gson,
2738
private val prefs: Prefs
2839
) {
29-
private suspend fun auth(): AuthData {
40+
companion object {
41+
const val AUTH_URL = "https://auroraoss.com/api/auth"
42+
}
43+
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+
53+
private fun refreshAuth(): AuthData {
54+
val properties = NativeDeviceInfoProvider(context).getNativeDeviceProperties()
55+
val playResponse = PlayHttpClient.postAuth(AUTH_URL, gson.toJson(properties).toByteArray())
56+
if (playResponse.isSuccessful) {
57+
val authData = gson.fromJson(String(playResponse.responseBytes), AuthData::class.java)
58+
prefs.playAuthData.put(authData)
59+
return authData
60+
}
61+
throw IllegalStateException("Auth not successful.")
62+
}
63+
64+
private fun auth(): AuthData {
3065
val savedData = prefs.playAuthData.get()
3166
if (savedData.email.isEmpty()) {
32-
val properties = NativeDeviceInfoProvider(context).getNativeDeviceProperties()
33-
val playResponse = PlayHttpClient.postAuth(
34-
"https://auroraoss.com/api/auth",
35-
gson.toJson(properties).toByteArray()
36-
)
37-
if (playResponse.isSuccessful) {
38-
val authData = gson.fromJson(String(playResponse.responseBytes), AuthData::class.java)
39-
prefs.playAuthData.put(authData)
40-
return authData
41-
}
42-
throw IllegalStateException("Auth not successful.")
67+
return refreshAuth()
4368
}
4469
return savedData
4570
}
4671

4772
suspend fun search(text: String) = flow {
4873
if (text.contains(" ") || !text.contains(".")) {
49-
emit(Result.success(emptyList()))
50-
return@flow
74+
// Normal Search
75+
val authData = auth()
76+
val updates = SearchHelper(authData)
77+
.using(PlayHttpClient)
78+
.searchResults(text)
79+
.appList
80+
.take(5)
81+
.map { it.toAppUpdate(::getInstallFiles) }
82+
emit(Result.success(updates))
83+
} else {
84+
// Package Name Search
85+
val authData = auth()
86+
val update = AppDetailsHelper(authData)
87+
.using(PlayHttpClient)
88+
.getAppByPackageName(text)
89+
.toAppUpdate(::getInstallFiles)
90+
emit(Result.success(listOf(update)))
5191
}
52-
val authData = auth()
53-
val app = AppDetailsHelper(authData)
54-
.using(PlayHttpClient)
55-
.getAppByPackageName(text)
56-
val update = app.toAppUpdate(PurchaseHelper(authData))
57-
emit(Result.success(listOf(update)))
5892
}.catch {
5993
emit(Result.failure(it))
60-
Log.e("PlayRepository", "Error searching.", it)
94+
Log.e("PlayRepository", "Error searching for $text.", it)
6195
}
6296

6397
suspend fun updates(apps: List<AppInstalled>) = flow {
6498
val authData = auth()
6599
val details = AppDetailsHelper(authData)
66100
.using(PlayHttpClient)
67101
.getAppByPackageName(apps.getPackageNames())
68-
val purchaseHelper = PurchaseHelper(authData)
69102
val updates = details
70103
.filter { it.versionCode > apps.getVersionCode(it.packageName) }
71-
.map { it.toAppUpdate(purchaseHelper, apps.getVersion(it.packageName), apps.getVersionCode(it.packageName)) }
104+
.map {
105+
it.toAppUpdate(
106+
::getInstallFiles,
107+
apps.getVersion(it.packageName),
108+
apps.getVersionCode(it.packageName)
109+
)
110+
}
72111
emit(updates)
73112
}.catch {
74113
emit(emptyList())
75-
Log.e("AptoideRepository", "Error looking for updates.", it)
114+
Log.e("PlayRepository", "Error looking for updates.", it)
76115
}
77116

117+
private fun getInstallFiles(app: App) = PurchaseHelper(auth())
118+
.using(PlayHttpClient)
119+
.purchase(app.packageName, app.versionCode, app.offerType)
120+
.filter { it.type == File.FileType.BASE || it.type == File.FileType.SPLIT }
121+
.map { it.url }
122+
78123
}
79124

80125
fun App.toAppUpdate(
81-
purchaseHelper: PurchaseHelper,
126+
getInstallFiles: (App) -> List<String>,
82127
oldVersion: String = "",
83128
oldVersionCode: Long = 0L
84129
) = AppUpdate(
@@ -90,6 +135,6 @@ fun App.toAppUpdate(
90135
oldVersionCode,
91136
PlaySource,
92137
Uri.parse(iconArtwork.url),
93-
purchaseHelper.purchase(packageName, versionCode, offerType).joinToString(",") { it.url },
138+
Link.Play { getInstallFiles(this) },
94139
whatsNew = changes
95140
)

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ fun TvCommonItem(
5252
LoadingImage(uri, Modifier.height(100.dp).align(Alignment.CenterVertically).padding(top = 8.dp))
5353
}
5454
Column(Modifier.align(Alignment.CenterVertically).padding(start = 8.dp, end = 8.dp, top = 8.dp)) {
55-
LargeTitle(name.ifEmpty { LocalContext.current.getAppName(packageName) })
55+
LargeTitle(name.ifEmpty { LocalContext.current.getAppName(packageName) }.ifEmpty { packageName })
5656
MediumText(packageName)
5757
if (oldVersion != null && !single) {
5858
ScrollableText {

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

+8-15
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package com.apkupdater.ui.component
1515
import com.apkupdater.R
1616
import com.apkupdater.data.ui.AppInstalled
1717
import com.apkupdater.data.ui.AppUpdate
18+
import com.apkupdater.data.ui.Link
1819
import com.apkupdater.util.getAppName
1920

2021

@@ -25,38 +26,30 @@ fun AppImage(app: AppInstalled, onIgnore: (String) -> Unit = {}) = Box {
2526
IgnoreIcon(
2627
app.ignored,
2728
{ onIgnore(app.packageName) },
28-
Modifier
29-
.align(Alignment.TopEnd)
30-
.padding(4.dp)
29+
Modifier.align(Alignment.TopEnd).padding(4.dp)
3130
)
3231
}
3332

3433
@Composable
35-
fun UpdateImage(app: AppUpdate, onInstall: (String) -> Unit = {}) = Box {
34+
fun UpdateImage(app: AppUpdate, onInstall: (Link) -> Unit = {}) = Box {
3635
LoadingImageApp(app.packageName)
3736
TextBubble(app.versionCode, Modifier.align(Alignment.BottomStart))
3837
InstallProgressIcon(app.isInstalling) { onInstall(app.link) }
3938
SourceIcon(
4039
app.source,
41-
Modifier
42-
.align(Alignment.TopStart)
43-
.padding(4.dp)
44-
.size(28.dp)
40+
Modifier.align(Alignment.TopStart).padding(4.dp).size(28.dp)
4541
)
4642
}
4743

4844

4945
@Composable
50-
fun SearchImage(app: AppUpdate, onInstall: (String) -> Unit = {}) = Box {
46+
fun SearchImage(app: AppUpdate, onInstall: (Link) -> Unit = {}) = Box {
5147
LoadingImage(app.iconUri)
5248
TextBubble(app.versionCode, Modifier.align(Alignment.BottomStart))
5349
InstallProgressIcon(app.isInstalling) { onInstall(app.link) }
5450
SourceIcon(
5551
app.source,
56-
Modifier
57-
.align(Alignment.TopStart)
58-
.padding(4.dp)
59-
.size(28.dp)
52+
Modifier.align(Alignment.TopStart).padding(4.dp).size(28.dp)
6053
)
6154
}
6255

@@ -72,7 +65,7 @@ fun InstalledItem(app: AppInstalled, onIgnore: (String) -> Unit = {}) = Column(
7265
}
7366

7467
@Composable
75-
fun UpdateItem(app: AppUpdate, onInstall: (String) -> Unit = {}) = Column {
68+
fun UpdateItem(app: AppUpdate, onInstall: (Link) -> Unit = {}) = Column {
7669
UpdateImage(app, onInstall)
7770
Column(Modifier.padding(top = 4.dp)) {
7871
ScrollableText { SmallText(app.packageName) }
@@ -81,7 +74,7 @@ fun UpdateItem(app: AppUpdate, onInstall: (String) -> Unit = {}) = Column {
8174
}
8275

8376
@Composable
84-
fun SearchItem(app: AppUpdate, onInstall: (String) -> Unit = {}) = Column {
77+
fun SearchItem(app: AppUpdate, onInstall: (Link) -> Unit = {}) = Column {
8578
SearchImage(app, onInstall)
8679
Column(Modifier.padding(top = 4.dp)) {
8780
ScrollableText { SmallText(app.packageName) }

0 commit comments

Comments
 (0)