diff --git a/app/build.gradle b/app/build.gradle index 3320d6921ea6d93a67574761b1397a7a59fccec4..4336fbf0adcf40834c51362739f50c2aa5a13d52 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,7 +42,7 @@ def getSentryDsn = { -> } android { - compileSdk 33 + compileSdk 34 defaultConfig { applicationId "foundation.e.apps" diff --git a/app/src/main/java/foundation/e/apps/data/gitlab/ReleaseInfoApi.kt b/app/src/main/java/foundation/e/apps/data/gitlab/ReleaseInfoApi.kt index 4b2a3fbf76cfc139ae6e045c1113a6b84c06fcdc..76062c758cccce0e29a05fde954975ac667d9b5c 100644 --- a/app/src/main/java/foundation/e/apps/data/gitlab/ReleaseInfoApi.kt +++ b/app/src/main/java/foundation/e/apps/data/gitlab/ReleaseInfoApi.kt @@ -17,11 +17,14 @@ package foundation.e.apps.data.gitlab -import foundation.e.apps.data.gitlab.models.ReleaseInfo +import foundation.e.apps.data.gitlab.models.GitLabReleaseInfo import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Path +/* +Provides GitLab API client method (https://docs.gitlab.com/ee/api/releases/) + */ interface ReleaseInfoApi { companion object { @@ -32,6 +35,5 @@ interface ReleaseInfoApi { @GET("{projectId}/releases") suspend fun getReleases( @Path("projectId") projectId: Int, - ): Response> - + ): Response> } diff --git a/app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt b/app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt index c928cface437b5237011967bfef8b312da418e4d..86e0995c2557622f20612eb526fd27c743f1f76b 100644 --- a/app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt @@ -22,6 +22,7 @@ import android.os.Build import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.data.application.ApplicationDataManager import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.gitlab.UpdatableSystemAppsApi.* import foundation.e.apps.data.gitlab.models.OsReleaseType import foundation.e.apps.data.gitlab.models.SystemAppInfo import foundation.e.apps.data.gitlab.models.SystemAppProject @@ -43,6 +44,15 @@ class SystemAppsUpdatesRepository @Inject constructor( private val releaseInfoApi: ReleaseInfoApi, ) { + private val androidVersionCode by lazy { + try { getAndroidVersionCodeChar() } + catch (exception: UnsupportedAndroidApiException) { + Timber.w(exception.message, + "Android API isn't in supported range to update some system apps") + "UnsupportedAndroidAPI" + } + } + private val systemAppProjectList = mutableListOf() private fun getUpdatableSystemApps(): List { @@ -55,13 +65,7 @@ class SystemAppsUpdatesRepository @Inject constructor( return@handleNetworkResult } - val systemName = getFullSystemName() - val endPoint = if (isEligibleToFetchAppListFromTest(systemName)) { - UpdatableSystemAppsApi.EndPoint.ENDPOINT_TEST - } else { - UpdatableSystemAppsApi.EndPoint.ENDPOINT_RELEASE - } - + val endPoint = getUpdatableSystemAppEndPoint() val response = updatableSystemAppsApi.getUpdatableSystemApps(endPoint) if (response.isSuccessful && !response.body().isNullOrEmpty()) { @@ -77,6 +81,15 @@ class SystemAppsUpdatesRepository @Inject constructor( } } + private fun getUpdatableSystemAppEndPoint(): EndPoint { + val systemName = getFullSystemName() + return if (isEligibleToFetchAppListFromTest(systemName)) { + EndPoint.ENDPOINT_TEST + } else { + EndPoint.ENDPOINT_RELEASE + } + } + private fun isEligibleToFetchAppListFromTest(systemName: String) = systemName.isBlank() || systemName.contains("beta") || systemName.contains("rc") || @@ -96,17 +109,23 @@ class SystemAppsUpdatesRepository @Inject constructor( } private suspend fun getReleaseDetailsUrl( - projectId: Int, + systemAppProject: SystemAppProject, releaseType: OsReleaseType, ): String? { + val projectId = systemAppProject.projectId val releaseResponse = releaseInfoApi.getReleases(projectId) - val releases = releaseResponse.body() + var releases = releaseResponse.body() if (!releaseResponse.isSuccessful || releases == null) { Timber.e("Failed to fetch releases for project id - $projectId") return null } + if (systemAppProject.dependsOnAndroidVersion) { + val versionSuffix = "-$androidVersionCode" + releases = releases.filter { isVersionedTag(it.tagName, versionSuffix) } + } + val sortedReleases = releases.sortedByDescending { it.releasedAt } @@ -120,25 +139,23 @@ class SystemAppsUpdatesRepository @Inject constructor( return null } - private suspend fun getSystemAppUpdateInfo( + private fun isVersionedTag(tag: String, versionSuffix: String): Boolean { + return tag.endsWith(suffix = versionSuffix, ignoreCase = true) + } + + private suspend fun getApplication( packageName: String, releaseType: OsReleaseType, sdkLevel: Int, device: String, ): Application? { - val projectId = - systemAppProjectList.find { it.packageName == packageName }?.projectId ?: return null - - val detailsUrl = getReleaseDetailsUrl(projectId, releaseType) ?: return null + val systemAppProject = systemAppProjectList.find { it.packageName == packageName } ?: return null + val detailsUrl = getReleaseDetailsUrl(systemAppProject, releaseType) ?: return null - val response = systemAppDefinitionApi.getSystemAppUpdateInfo(detailsUrl) - val systemAppInfo = response.body() + val systemAppInfo = getSystemAppInfo(packageName, detailsUrl) ?: return null - return if (systemAppInfo == null) { - Timber.e("Null app info for: $packageName, response: ${response.errorBody()?.string()}") - null - } else if (isSystemAppBlocked(systemAppInfo, sdkLevel, device)) { + return if (isSystemAppBlocked(systemAppInfo, sdkLevel, device)) { Timber.e("Blocked system app: $packageName, details: $systemAppInfo") null } else { @@ -146,6 +163,20 @@ class SystemAppsUpdatesRepository @Inject constructor( } } + private suspend fun getSystemAppInfo( + packageName: String, + detailsUrl: String + ): SystemAppInfo? { + val response = systemAppDefinitionApi.getSystemAppUpdateInfo(detailsUrl) + + return if (response.isSuccessful) { + response.body() + } else { + Timber.e("Can't get AppInfo for $packageName, response: ${response.errorBody()?.string()}") + null + } + } + private fun getFullSystemName(): String { return SystemInfoProvider.getSystemProperty(SystemInfoProvider.KEY_LINEAGE_VERSION) ?: "" } @@ -164,6 +195,23 @@ class SystemAppsUpdatesRepository @Inject constructor( } } + /** + * This method must be updated when Murena or /e/ foundation support new Android version + * or stop to support an old one + */ + private fun getAndroidVersionCodeChar(): String { + /* TODO manually add new supported android version when they will be supported. + * VANILLA_ICE_CREAM (A15) => https://gitlab.e.foundation/e/os/backlog/-/issues/2772 + * BAKLAVA (A16) => https://gitlab.e.foundation/e/os/backlog/-/issues/2773 + */ + return when (val currentAPI = Build.VERSION.SDK_INT) { + Build.VERSION_CODES.S, Build.VERSION_CODES.S_V2 -> "S" + Build.VERSION_CODES.TIRAMISU -> "T" + Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> "U" + else -> throw UnsupportedAndroidApiException("API level $currentAPI is not supported") + } + } + suspend fun getSystemUpdates(): List { val updateList = mutableListOf() val releaseType = getSystemReleaseType() @@ -179,7 +227,7 @@ class SystemAppsUpdatesRepository @Inject constructor( } val result = handleNetworkResult { - getSystemAppUpdateInfo( + getApplication( it, releaseType, sdkLevel, @@ -200,5 +248,6 @@ class SystemAppsUpdatesRepository @Inject constructor( return updateList } - } + +private class UnsupportedAndroidApiException(message: String) : RuntimeException(message) \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/data/gitlab/models/ReleaseInfo.kt b/app/src/main/java/foundation/e/apps/data/gitlab/models/ReleaseInfo.kt index 6284fe92ad511ae354ddbf62eece5432f4bd9159..cb51761d0aa0e9904214ceaa9c074974beda593f 100644 --- a/app/src/main/java/foundation/e/apps/data/gitlab/models/ReleaseInfo.kt +++ b/app/src/main/java/foundation/e/apps/data/gitlab/models/ReleaseInfo.kt @@ -19,10 +19,10 @@ package foundation.e.apps.data.gitlab.models import com.squareup.moshi.Json -data class ReleaseInfo( +data class GitLabReleaseInfo( val name: String, - @Json(name = "released_at") - val releasedAt: String, + @Json(name = "tag_name") val tagName: String, + @Json(name = "released_at") val releasedAt: String, val assets: ReleaseAssets, ) { fun getAssetWebLink(assetName: String): String? { diff --git a/app/src/main/java/foundation/e/apps/data/gitlab/models/SystemAppProject.kt b/app/src/main/java/foundation/e/apps/data/gitlab/models/SystemAppProject.kt index edc7f2bffdeaf584e4e54aaceae0412b5dd5ed9f..054a869f5d746d34c42f4bc92c1d626174e1639b 100644 --- a/app/src/main/java/foundation/e/apps/data/gitlab/models/SystemAppProject.kt +++ b/app/src/main/java/foundation/e/apps/data/gitlab/models/SystemAppProject.kt @@ -20,4 +20,5 @@ package foundation.e.apps.data.gitlab.models data class SystemAppProject( val packageName: String, val projectId: Int, + val dependsOnAndroidVersion: Boolean = false )