Skip to content

Commit

Permalink
Play Source
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
rumboalla committed Mar 26, 2024
1 parent 8886145 commit b10fb75
Show file tree
Hide file tree
Showing 20 changed files with 156 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.apkupdater.data.apkmirror
import com.apkupdater.data.ui.ApkMirrorSource
import com.apkupdater.data.ui.AppInstalled
import com.apkupdater.data.ui.AppUpdate
import com.apkupdater.data.ui.Link
import com.google.gson.annotations.SerializedName

data class AppExistsResponseApk(
Expand All @@ -29,6 +30,6 @@ fun AppExistsResponseApk.toAppUpdate(app: AppInstalled, release: AppExistsRespon
app.versionCode,
ApkMirrorSource,
app.iconUri,
"https://www.apkmirror.com$link",
Link.Url("https://www.apkmirror.com$link"),
release.whatsNew.orEmpty()
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.net.Uri
import com.apkupdater.data.ui.ApkPureSource
import com.apkupdater.data.ui.AppInstalled
import com.apkupdater.data.ui.AppUpdate
import com.apkupdater.data.ui.Link


data class AppUpdateResponse(
Expand All @@ -29,6 +30,6 @@ fun AppUpdateResponse.toAppUpdate(
app?.versionCode ?: 0L,
ApkPureSource,
if (app == null) Uri.parse(icon.thumbnail.url) else Uri.EMPTY,
asset.url.replace("http://", "https://"),
if (asset.url.contains("/XAPK")) Link.Xapk(asset.url.replace("http://", "https://")) else Link.Url(asset.url.replace("http://", "https://")),
if (app == null) description_short else whatsnew
)
3 changes: 2 additions & 1 deletion app/src/main/kotlin/com/apkupdater/data/aptoide/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.core.net.toUri
import com.apkupdater.data.ui.AppInstalled
import com.apkupdater.data.ui.AppUpdate
import com.apkupdater.data.ui.AptoideSource
import com.apkupdater.data.ui.Link
import com.google.gson.annotations.SerializedName

data class App(
Expand All @@ -24,5 +25,5 @@ fun App.toAppUpdate(app: AppInstalled?) = AppUpdate(
oldVersionCode = app?.versionCode ?: 0L,
source = AptoideSource,
iconUri = icon?.toUri() ?: Uri.EMPTY,
link = file.path
link = Link.Url(file.path)
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.apkupdater.data.fdroid
import androidx.core.net.toUri
import com.apkupdater.data.ui.AppInstalled
import com.apkupdater.data.ui.AppUpdate
import com.apkupdater.data.ui.Link
import com.apkupdater.data.ui.Source

data class FdroidUpdate(
Expand All @@ -22,6 +23,6 @@ fun FdroidUpdate.toAppUpdate(current: AppInstalled?, source: Source, url: String
"https://f-droid.org/assets/ic_repo_app_default.png".toUri()
else
"${url}icons-640/${app.icon}".toUri(),
"$url${apk.apkName}",
Link.Url("$url${apk.apkName}"),
if (current != null) app.localized["en-US"]?.whatsNew.orEmpty() else app.localized["en-US"]?.summary.orEmpty()
)
6 changes: 6 additions & 0 deletions app/src/main/kotlin/com/apkupdater/data/snack/InstallSnack.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
package com.apkupdater.data.snack

import androidx.annotation.StringRes

data class InstallSnack(val success: Boolean, val name: String): ISnack

data class TextSnack(val message: String): ISnack

data class TextIdSnack(@StringRes val id: Int): ISnack
2 changes: 1 addition & 1 deletion app/src/main/kotlin/com/apkupdater/data/ui/AppUpdate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ data class AppUpdate(
val oldVersionCode: Long,
val source: Source,
val iconUri: Uri = Uri.EMPTY,
val link: String = "",
val link: Link = Link.Empty,
val whatsNew: String = "",
val isInstalling: Boolean = false,
val id: Int = "${source.name}.$packageName.$versionCode.$version".hashCode()
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/kotlin/com/apkupdater/data/ui/Link.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.apkupdater.data.ui


sealed class Link {
data object Empty: Link()
data class Url(val link: String): Link()
data class Xapk(val link: String): Link()
data class Play(val getInstallFiles: () -> List<String>): Link()
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.apkupdater.data.apkmirror.toAppUpdate
import com.apkupdater.data.ui.ApkMirrorSource
import com.apkupdater.data.ui.AppInstalled
import com.apkupdater.data.ui.AppUpdate
import com.apkupdater.data.ui.Link
import com.apkupdater.data.ui.getApp
import com.apkupdater.data.ui.getPackageNames
import com.apkupdater.data.ui.getSignature
Expand Down Expand Up @@ -63,7 +64,7 @@ class ApkMirrorRepository(
val result = (0 until a.size).map {
AppUpdate(
name = h5[it].attr("title"),
link = "$baseUrl${h5[it].selectFirst("a")?.attr("href")}",
link = Link.Url("$baseUrl${h5[it].selectFirst("a")?.attr("href")}"),
iconUri = Uri.parse("$baseUrl${img[it].attr("src")}".replace("=32", "=128")),
version = "?",
oldVersion = "?",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.apkupdater.data.github.GitHubReleaseAsset
import com.apkupdater.data.ui.AppInstalled
import com.apkupdater.data.ui.AppUpdate
import com.apkupdater.data.ui.GitHubSource
import com.apkupdater.data.ui.Link
import com.apkupdater.data.ui.getApp
import com.apkupdater.prefs.Prefs
import com.apkupdater.service.GitHubService
Expand Down Expand Up @@ -82,7 +83,7 @@ class GitHubRepository(
versionCode = versions.second,
oldVersionCode = BuildConfig.VERSION_CODE.toLong(),
source = GitHubSource,
link = releases[0].assets[0].browser_download_url,
link = Link.Url(releases[0].assets[0].browser_download_url),
whatsNew = releases[0].body
)))
} else {
Expand Down Expand Up @@ -120,7 +121,7 @@ class GitHubRepository(
versionCode = 0L,
oldVersionCode = app?.versionCode ?: 0L,
source = GitHubSource,
link = findApkAssetArch(releases[0].assets, extra),
link = Link.Url(findApkAssetArch(releases[0].assets, extra)),
whatsNew = releases[0].body,
iconUri = if (apps == null) Uri.parse(releases[0].author.avatar_url) else Uri.EMPTY
)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.apkupdater.data.gitlab.GitLabRelease
import com.apkupdater.data.ui.AppInstalled
import com.apkupdater.data.ui.AppUpdate
import com.apkupdater.data.ui.GitLabSource
import com.apkupdater.data.ui.Link
import com.apkupdater.data.ui.getApp
import com.apkupdater.prefs.Prefs
import com.apkupdater.service.GitLabService
Expand Down Expand Up @@ -60,7 +61,7 @@ class GitLabRepository(
versionCode = 0L,
oldVersionCode = app?.versionCode ?: 0L,
source = GitLabSource,
link = getApkUrl(packageName, releases[0]),
link = Link.Url(getApkUrl(packageName, releases[0])),
whatsNew = releases[0].description,
iconUri = if (apps == null) Uri.parse(releases[0].author.avatar_url) else Uri.EMPTY
)))
Expand Down
97 changes: 71 additions & 26 deletions app/src/main/kotlin/com/apkupdater/repository/PlayRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.net.Uri
import android.util.Log
import com.apkupdater.data.ui.AppInstalled
import com.apkupdater.data.ui.AppUpdate
import com.apkupdater.data.ui.Link
import com.apkupdater.data.ui.PlaySource
import com.apkupdater.data.ui.getPackageNames
import com.apkupdater.data.ui.getVersion
Expand All @@ -14,71 +15,115 @@ import com.apkupdater.util.play.NativeDeviceInfoProvider
import com.apkupdater.util.play.PlayHttpClient
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.File
import com.aurora.gplayapi.helpers.AppDetailsHelper
import com.aurora.gplayapi.helpers.PurchaseHelper
import com.aurora.gplayapi.helpers.SearchHelper
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach


@OptIn(FlowPreview::class)
class PlayRepository(
private val context: Context,
private val gson: Gson,
private val prefs: Prefs
) {
private suspend fun auth(): AuthData {
companion object {
const val AUTH_URL = "https://auroraoss.com/api/auth"
}

init {
// TODO: Needs testing.
PlayHttpClient.responseCode
.filter { it == 401 }
.debounce(60 * 5 * 1_000)
.onEach { runCatching { refreshAuth() }.getOrNull() }
.launchIn(CoroutineScope(Dispatchers.IO))
}

private fun refreshAuth(): AuthData {
val properties = NativeDeviceInfoProvider(context).getNativeDeviceProperties()
val playResponse = PlayHttpClient.postAuth(AUTH_URL, gson.toJson(properties).toByteArray())
if (playResponse.isSuccessful) {
val authData = gson.fromJson(String(playResponse.responseBytes), AuthData::class.java)
prefs.playAuthData.put(authData)
return authData
}
throw IllegalStateException("Auth not successful.")
}

private fun auth(): AuthData {
val savedData = prefs.playAuthData.get()
if (savedData.email.isEmpty()) {
val properties = NativeDeviceInfoProvider(context).getNativeDeviceProperties()
val playResponse = PlayHttpClient.postAuth(
"https://auroraoss.com/api/auth",
gson.toJson(properties).toByteArray()
)
if (playResponse.isSuccessful) {
val authData = gson.fromJson(String(playResponse.responseBytes), AuthData::class.java)
prefs.playAuthData.put(authData)
return authData
}
throw IllegalStateException("Auth not successful.")
return refreshAuth()
}
return savedData
}

suspend fun search(text: String) = flow {
if (text.contains(" ") || !text.contains(".")) {
emit(Result.success(emptyList()))
return@flow
// Normal Search
val authData = auth()
val updates = SearchHelper(authData)
.using(PlayHttpClient)
.searchResults(text)
.appList
.take(5)
.map { it.toAppUpdate(::getInstallFiles) }
emit(Result.success(updates))
} else {
// Package Name Search
val authData = auth()
val update = AppDetailsHelper(authData)
.using(PlayHttpClient)
.getAppByPackageName(text)
.toAppUpdate(::getInstallFiles)
emit(Result.success(listOf(update)))
}
val authData = auth()
val app = AppDetailsHelper(authData)
.using(PlayHttpClient)
.getAppByPackageName(text)
val update = app.toAppUpdate(PurchaseHelper(authData))
emit(Result.success(listOf(update)))
}.catch {
emit(Result.failure(it))
Log.e("PlayRepository", "Error searching.", it)
Log.e("PlayRepository", "Error searching for $text.", it)
}

suspend fun updates(apps: List<AppInstalled>) = flow {
val authData = auth()
val details = AppDetailsHelper(authData)
.using(PlayHttpClient)
.getAppByPackageName(apps.getPackageNames())
val purchaseHelper = PurchaseHelper(authData)
val updates = details
.filter { it.versionCode > apps.getVersionCode(it.packageName) }
.map { it.toAppUpdate(purchaseHelper, apps.getVersion(it.packageName), apps.getVersionCode(it.packageName)) }
.map {
it.toAppUpdate(
::getInstallFiles,
apps.getVersion(it.packageName),
apps.getVersionCode(it.packageName)
)
}
emit(updates)
}.catch {
emit(emptyList())
Log.e("AptoideRepository", "Error looking for updates.", it)
Log.e("PlayRepository", "Error looking for updates.", it)
}

private fun getInstallFiles(app: App) = PurchaseHelper(auth())
.using(PlayHttpClient)
.purchase(app.packageName, app.versionCode, app.offerType)
.filter { it.type == File.FileType.BASE || it.type == File.FileType.SPLIT }
.map { it.url }

}

fun App.toAppUpdate(
purchaseHelper: PurchaseHelper,
getInstallFiles: (App) -> List<String>,
oldVersion: String = "",
oldVersionCode: Long = 0L
) = AppUpdate(
Expand All @@ -90,6 +135,6 @@ fun App.toAppUpdate(
oldVersionCode,
PlaySource,
Uri.parse(iconArtwork.url),
purchaseHelper.purchase(packageName, versionCode, offerType).joinToString(",") { it.url },
Link.Play { getInstallFiles(this) },
whatsNew = changes
)
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fun TvCommonItem(
LoadingImage(uri, Modifier.height(100.dp).align(Alignment.CenterVertically).padding(top = 8.dp))
}
Column(Modifier.align(Alignment.CenterVertically).padding(start = 8.dp, end = 8.dp, top = 8.dp)) {
LargeTitle(name.ifEmpty { LocalContext.current.getAppName(packageName) })
LargeTitle(name.ifEmpty { LocalContext.current.getAppName(packageName) }.ifEmpty { packageName })
MediumText(packageName)
if (oldVersion != null && !single) {
ScrollableText {
Expand Down
23 changes: 8 additions & 15 deletions app/src/main/kotlin/com/apkupdater/ui/component/UiComponents.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package com.apkupdater.ui.component
import com.apkupdater.R
import com.apkupdater.data.ui.AppInstalled
import com.apkupdater.data.ui.AppUpdate
import com.apkupdater.data.ui.Link
import com.apkupdater.util.getAppName


Expand All @@ -25,38 +26,30 @@ fun AppImage(app: AppInstalled, onIgnore: (String) -> Unit = {}) = Box {
IgnoreIcon(
app.ignored,
{ onIgnore(app.packageName) },
Modifier
.align(Alignment.TopEnd)
.padding(4.dp)
Modifier.align(Alignment.TopEnd).padding(4.dp)
)
}

@Composable
fun UpdateImage(app: AppUpdate, onInstall: (String) -> Unit = {}) = Box {
fun UpdateImage(app: AppUpdate, onInstall: (Link) -> Unit = {}) = Box {
LoadingImageApp(app.packageName)
TextBubble(app.versionCode, Modifier.align(Alignment.BottomStart))
InstallProgressIcon(app.isInstalling) { onInstall(app.link) }
SourceIcon(
app.source,
Modifier
.align(Alignment.TopStart)
.padding(4.dp)
.size(28.dp)
Modifier.align(Alignment.TopStart).padding(4.dp).size(28.dp)
)
}


@Composable
fun SearchImage(app: AppUpdate, onInstall: (String) -> Unit = {}) = Box {
fun SearchImage(app: AppUpdate, onInstall: (Link) -> Unit = {}) = Box {
LoadingImage(app.iconUri)
TextBubble(app.versionCode, Modifier.align(Alignment.BottomStart))
InstallProgressIcon(app.isInstalling) { onInstall(app.link) }
SourceIcon(
app.source,
Modifier
.align(Alignment.TopStart)
.padding(4.dp)
.size(28.dp)
Modifier.align(Alignment.TopStart).padding(4.dp).size(28.dp)
)
}

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

@Composable
fun UpdateItem(app: AppUpdate, onInstall: (String) -> Unit = {}) = Column {
fun UpdateItem(app: AppUpdate, onInstall: (Link) -> Unit = {}) = Column {
UpdateImage(app, onInstall)
Column(Modifier.padding(top = 4.dp)) {
ScrollableText { SmallText(app.packageName) }
Expand All @@ -81,7 +74,7 @@ fun UpdateItem(app: AppUpdate, onInstall: (String) -> Unit = {}) = Column {
}

@Composable
fun SearchItem(app: AppUpdate, onInstall: (String) -> Unit = {}) = Column {
fun SearchItem(app: AppUpdate, onInstall: (Link) -> Unit = {}) = Column {
SearchImage(app, onInstall)
Column(Modifier.padding(top = 4.dp)) {
ScrollableText { SmallText(app.packageName) }
Expand Down
Loading

0 comments on commit b10fb75

Please sign in to comment.