Loading app/src/main/java/foundation/e/apps/data/application/data/Application.kt +1 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,7 @@ data class Application( var contentRating: ContentRating = ContentRating(), @SerializedName(value = "antifeatures") val antiFeatures: List<Map<String, String>> = emptyList(), var isSystemApp: Boolean = false, ) { fun updateType() { this.type = if (this.is_pwa) PWA else NATIVE Loading app/src/main/java/foundation/e/apps/data/gitlab/SystemAppDefinitionApi.kt 0 → 100644 +38 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021-2024 MURENA SAS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.gitlab import foundation.e.apps.data.gitlab.models.SystemAppInfo import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Path interface SystemAppDefinitionApi { companion object { const val BASE_URL = "https://gitlab.e.foundation/api/v4/projects/" } @GET("{projectId}/releases/permalink/latest/downloads/json/{releaseType}.json") suspend fun getSystemAppUpdateInfo( @Path("projectId") projectId: Int, @Path("releaseType") releaseType: String, ): Response<SystemAppInfo> } app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021-2024 MURENA SAS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.gitlab import android.content.Context 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.models.SystemAppInfo import foundation.e.apps.data.gitlab.models.SystemAppProject import foundation.e.apps.data.gitlab.models.toApplication import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.install.pkg.AppLoungePackageManager import foundation.e.apps.utils.SystemInfoProvider import javax.inject.Inject import javax.inject.Singleton import timber.log.Timber @Singleton class SystemAppsUpdatesRepository @Inject constructor( @ApplicationContext private val context: Context, private val updatableSystemAppsApi: UpdatableSystemAppsApi, private val systemAppDefinitionApi: SystemAppDefinitionApi, private val applicationDataManager: ApplicationDataManager, private val appLoungePackageManager: AppLoungePackageManager, ) { private var systemAppProjectList = mutableListOf<SystemAppProject>() suspend fun fetchUpdatableSystemApps() { val result = handleNetworkResult { val response = updatableSystemAppsApi.getUpdatableSystemApps() if (response.isSuccessful && !response.body().isNullOrEmpty()) { response.body()?.let { systemAppProjectList.addAll(it) } } else { Timber.e("Failed to fetch updatable apps: ${response.errorBody()?.string()}") } } if (!result.isSuccess()) { Timber.e("Network error when fetching updatable apps - ${result.message}") } } fun getUpdatableSystemApps(): List<String> { return systemAppProjectList.map { it.packageName } } private fun isSystemAppBlocked( systemAppInfo: SystemAppInfo, sdkLevel: Int, device: String, ): Boolean { return systemAppInfo.run { sdkLevel < minSdk || blockedAndroid?.contains(sdkLevel) == true || blockedDevices?.contains(device) == true || blockedDevices?.contains("${device}@${sdkLevel}") == true } } private suspend fun getSystemAppUpdateInfo( packageName: String, releaseType: String, sdkLevel: Int, device: String, ): Application? { val projectId = systemAppProjectList.find { it.packageName == packageName }?.projectId ?: return null val response = systemAppDefinitionApi.getSystemAppUpdateInfo(projectId, releaseType) val systemAppInfo = response.body() return if (systemAppInfo == null) { Timber.e("Null app info for: $packageName, response: ${response.errorBody()?.string()}") null } else if (isSystemAppBlocked(systemAppInfo, sdkLevel, device)) { Timber.e("Blocked system app: $packageName, details: $systemAppInfo") null } else { systemAppInfo.toApplication() } } private fun getSdkLevel(): Int { return Build.VERSION.SDK_INT } private fun getDevice(): String { return SystemInfoProvider.getSystemProperty(SystemInfoProvider.KEY_LINEAGE_DEVICE) ?: "" } private fun getSystemReleaseType(): String { return SystemInfoProvider.getSystemProperty(SystemInfoProvider.KEY_LINEAGE_RELEASE_TYPE) ?: "" } suspend fun getSystemUpdates(): List<Application> { val updateList = mutableListOf<Application>() val releaseType = getSystemReleaseType() val sdkLevel = getSdkLevel() val device = getDevice() val updatableApps = getUpdatableSystemApps() updatableApps.forEach { if (!appLoungePackageManager.isInstalled(it)) { // Don't install for system apps which are removed (by root or otherwise) return@forEach } val result = handleNetworkResult { getSystemAppUpdateInfo( it, releaseType, sdkLevel, device, ) } result.data?.run { applicationDataManager.updateStatus(this) updateList.add(this) updateSource(context) } if (!result.isSuccess()) { Timber.e("Failed to get system app info for $it - ${result.message}") } } return updateList } } app/src/main/java/foundation/e/apps/data/gitlab/UpdatableSystemAppsApi.kt 0 → 100644 +34 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021-2024 MURENA SAS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.gitlab import foundation.e.apps.data.gitlab.models.SystemAppProject import retrofit2.Response import retrofit2.http.GET interface UpdatableSystemAppsApi { companion object { const val BASE_URL = "https://gitlab.e.foundation/e/os/system-apps-update-info/-/raw/main/" } @GET("updatable_system_apps.json?inline=false") suspend fun getUpdatableSystemApps(): Response<List<SystemAppProject>> } app/src/main/java/foundation/e/apps/data/gitlab/models/SystemAppInfo.kt 0 → 100644 +59 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021-2024 MURENA SAS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.gitlab.models import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.squareup.moshi.Json import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.enums.FilterLevel import foundation.e.apps.data.enums.Origin import java.util.UUID @JsonIgnoreProperties(ignoreUnknown = true) data class SystemAppInfo( val name: String, @Json(name = "package_name") val packageName: String, @Json(name = "version_code") val versionCode: Int, @Json(name = "min_sdk") val minSdk: Int, @Json(name = "version_name") val versionName: String, @Json(name = "url") val downloadUrl: String, @Json(name = "size") val size: Long?, @Json(name = "author_name") val authorName: String?, val priority: Boolean?, @Json(name = "blocked_android") val blockedAndroid: List<Int>?, @Json(name = "blocked_devices") val blockedDevices: List<String>?, ) private const val RANDOM_SIZE = 1L fun SystemAppInfo.toApplication(): Application { return Application( _id = UUID.randomUUID().toString(), author = authorName ?: "eFoundation", description = "", latest_version_code = versionCode, latest_version_number = versionName, name = name, package_name = packageName, origin = Origin.GITLAB_RELEASES, originalSize = size ?: RANDOM_SIZE, url = downloadUrl, isSystemApp = true, filterLevel = FilterLevel.NONE, ) } Loading
app/src/main/java/foundation/e/apps/data/application/data/Application.kt +1 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,7 @@ data class Application( var contentRating: ContentRating = ContentRating(), @SerializedName(value = "antifeatures") val antiFeatures: List<Map<String, String>> = emptyList(), var isSystemApp: Boolean = false, ) { fun updateType() { this.type = if (this.is_pwa) PWA else NATIVE Loading
app/src/main/java/foundation/e/apps/data/gitlab/SystemAppDefinitionApi.kt 0 → 100644 +38 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021-2024 MURENA SAS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.gitlab import foundation.e.apps.data.gitlab.models.SystemAppInfo import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Path interface SystemAppDefinitionApi { companion object { const val BASE_URL = "https://gitlab.e.foundation/api/v4/projects/" } @GET("{projectId}/releases/permalink/latest/downloads/json/{releaseType}.json") suspend fun getSystemAppUpdateInfo( @Path("projectId") projectId: Int, @Path("releaseType") releaseType: String, ): Response<SystemAppInfo> }
app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021-2024 MURENA SAS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.gitlab import android.content.Context 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.models.SystemAppInfo import foundation.e.apps.data.gitlab.models.SystemAppProject import foundation.e.apps.data.gitlab.models.toApplication import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.install.pkg.AppLoungePackageManager import foundation.e.apps.utils.SystemInfoProvider import javax.inject.Inject import javax.inject.Singleton import timber.log.Timber @Singleton class SystemAppsUpdatesRepository @Inject constructor( @ApplicationContext private val context: Context, private val updatableSystemAppsApi: UpdatableSystemAppsApi, private val systemAppDefinitionApi: SystemAppDefinitionApi, private val applicationDataManager: ApplicationDataManager, private val appLoungePackageManager: AppLoungePackageManager, ) { private var systemAppProjectList = mutableListOf<SystemAppProject>() suspend fun fetchUpdatableSystemApps() { val result = handleNetworkResult { val response = updatableSystemAppsApi.getUpdatableSystemApps() if (response.isSuccessful && !response.body().isNullOrEmpty()) { response.body()?.let { systemAppProjectList.addAll(it) } } else { Timber.e("Failed to fetch updatable apps: ${response.errorBody()?.string()}") } } if (!result.isSuccess()) { Timber.e("Network error when fetching updatable apps - ${result.message}") } } fun getUpdatableSystemApps(): List<String> { return systemAppProjectList.map { it.packageName } } private fun isSystemAppBlocked( systemAppInfo: SystemAppInfo, sdkLevel: Int, device: String, ): Boolean { return systemAppInfo.run { sdkLevel < minSdk || blockedAndroid?.contains(sdkLevel) == true || blockedDevices?.contains(device) == true || blockedDevices?.contains("${device}@${sdkLevel}") == true } } private suspend fun getSystemAppUpdateInfo( packageName: String, releaseType: String, sdkLevel: Int, device: String, ): Application? { val projectId = systemAppProjectList.find { it.packageName == packageName }?.projectId ?: return null val response = systemAppDefinitionApi.getSystemAppUpdateInfo(projectId, releaseType) val systemAppInfo = response.body() return if (systemAppInfo == null) { Timber.e("Null app info for: $packageName, response: ${response.errorBody()?.string()}") null } else if (isSystemAppBlocked(systemAppInfo, sdkLevel, device)) { Timber.e("Blocked system app: $packageName, details: $systemAppInfo") null } else { systemAppInfo.toApplication() } } private fun getSdkLevel(): Int { return Build.VERSION.SDK_INT } private fun getDevice(): String { return SystemInfoProvider.getSystemProperty(SystemInfoProvider.KEY_LINEAGE_DEVICE) ?: "" } private fun getSystemReleaseType(): String { return SystemInfoProvider.getSystemProperty(SystemInfoProvider.KEY_LINEAGE_RELEASE_TYPE) ?: "" } suspend fun getSystemUpdates(): List<Application> { val updateList = mutableListOf<Application>() val releaseType = getSystemReleaseType() val sdkLevel = getSdkLevel() val device = getDevice() val updatableApps = getUpdatableSystemApps() updatableApps.forEach { if (!appLoungePackageManager.isInstalled(it)) { // Don't install for system apps which are removed (by root or otherwise) return@forEach } val result = handleNetworkResult { getSystemAppUpdateInfo( it, releaseType, sdkLevel, device, ) } result.data?.run { applicationDataManager.updateStatus(this) updateList.add(this) updateSource(context) } if (!result.isSuccess()) { Timber.e("Failed to get system app info for $it - ${result.message}") } } return updateList } }
app/src/main/java/foundation/e/apps/data/gitlab/UpdatableSystemAppsApi.kt 0 → 100644 +34 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021-2024 MURENA SAS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.gitlab import foundation.e.apps.data.gitlab.models.SystemAppProject import retrofit2.Response import retrofit2.http.GET interface UpdatableSystemAppsApi { companion object { const val BASE_URL = "https://gitlab.e.foundation/e/os/system-apps-update-info/-/raw/main/" } @GET("updatable_system_apps.json?inline=false") suspend fun getUpdatableSystemApps(): Response<List<SystemAppProject>> }
app/src/main/java/foundation/e/apps/data/gitlab/models/SystemAppInfo.kt 0 → 100644 +59 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021-2024 MURENA SAS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.gitlab.models import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.squareup.moshi.Json import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.enums.FilterLevel import foundation.e.apps.data.enums.Origin import java.util.UUID @JsonIgnoreProperties(ignoreUnknown = true) data class SystemAppInfo( val name: String, @Json(name = "package_name") val packageName: String, @Json(name = "version_code") val versionCode: Int, @Json(name = "min_sdk") val minSdk: Int, @Json(name = "version_name") val versionName: String, @Json(name = "url") val downloadUrl: String, @Json(name = "size") val size: Long?, @Json(name = "author_name") val authorName: String?, val priority: Boolean?, @Json(name = "blocked_android") val blockedAndroid: List<Int>?, @Json(name = "blocked_devices") val blockedDevices: List<String>?, ) private const val RANDOM_SIZE = 1L fun SystemAppInfo.toApplication(): Application { return Application( _id = UUID.randomUUID().toString(), author = authorName ?: "eFoundation", description = "", latest_version_code = versionCode, latest_version_number = versionName, name = name, package_name = packageName, origin = Origin.GITLAB_RELEASES, originalSize = size ?: RANDOM_SIZE, url = downloadUrl, isSystemApp = true, filterLevel = FilterLevel.NONE, ) }