Loading app/src/main/java/foundation/e/apps/data/application/data/Application.kt +8 −3 Original line number Diff line number Diff line Loading @@ -105,6 +105,8 @@ 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 @@ -112,9 +114,12 @@ data class Application( fun updateSource(context: Context) { this.apply { source = if (origin != Origin.CLEANAPK) "" else if (is_pwa) context.getString(R.string.pwa) else context.getString(R.string.open_source) source = when { origin == Origin.GITLAB -> context.getString(R.string.system_app) origin == Origin.GPLAY -> "" is_pwa -> context.getString(R.string.pwa) else -> context.getString(R.string.open_source) } } } } Loading app/src/main/java/foundation/e/apps/data/gitlab/EligibleSystemAppsApi.kt 0 → 100644 +34 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019-2023 E FOUNDATION * * 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 EligibleSystemAppsApi { 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 getAllEligibleApps(): Response<List<SystemAppProject>> } No newline at end of file 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) 2019-2023 E FOUNDATION * * 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> } No newline at end of file app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt 0 → 100644 +136 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019-2023 E FOUNDATION * * 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.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 eligibleSystemAppsApi: EligibleSystemAppsApi, private val systemAppDefinitionApi: SystemAppDefinitionApi, private val applicationDataManager: ApplicationDataManager, private val appLoungePackageManager: AppLoungePackageManager, ) { private var systemAppProjectList = mutableListOf<SystemAppProject>() suspend fun fetchAllEligibleApps() { val response = eligibleSystemAppsApi.getAllEligibleApps() if (response.isSuccessful && !response.body().isNullOrEmpty()) { response.body()?.let { systemAppProjectList.addAll(it) } } } fun getAllEligibleApps(): List<String> { return systemAppProjectList.map { it.packageName } } private fun isSystemAppBlacklisted( systemAppInfo: SystemAppInfo, sdkLevel: Int, device: String, ): Boolean { return systemAppInfo.run { sdkLevel < minSdk || blacklistedAndroid?.contains(sdkLevel) == true || blacklistedDevices?.contains(device) == true || blacklistedDevices?.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) if (!response.isSuccessful) { Timber.e("Failed to fetch system app update definition for: $packageName, $releaseType") return null } val systemAppInfo = response.body() ?: return null if (isSystemAppBlacklisted(systemAppInfo, sdkLevel, device)) { Timber.e("Blacklisted system app: $packageName, $systemAppInfo") return null } return 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 eligibleApps = getAllEligibleApps() eligibleApps.forEach { if (!appLoungePackageManager.isInstalled(it)) { // Don't install for system apps which are removed (by root or otherwise) return@forEach } getSystemAppUpdateInfo( it, releaseType, sdkLevel, device, )?.run { applicationDataManager.updateStatus(this) updateList.add(this) updateSource(context) } } return updateList } } No newline at end of file app/src/main/java/foundation/e/apps/data/gitlab/models/SystemAppInfo.kt 0 → 100644 +55 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019-2023 E FOUNDATION * * 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, val priority: Boolean?, @Json(name = "blacklisted_android") val blacklistedAndroid: List<Int>?, @Json(name = "blacklisted_devices") val blacklistedDevices: List<String>?, ) fun SystemAppInfo.toApplication(): Application { return Application( _id = UUID.randomUUID().toString(), author = "Murena SAS", description = "", latest_version_code = versionCode, latest_version_number = versionName, name = name, package_name = packageName, origin = Origin.GITLAB, originalSize = 1, // so that the app is not filtered out, url = downloadUrl, isSystemApp = true, filterLevel = FilterLevel.NONE, ) } No newline at end of file Loading
app/src/main/java/foundation/e/apps/data/application/data/Application.kt +8 −3 Original line number Diff line number Diff line Loading @@ -105,6 +105,8 @@ 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 @@ -112,9 +114,12 @@ data class Application( fun updateSource(context: Context) { this.apply { source = if (origin != Origin.CLEANAPK) "" else if (is_pwa) context.getString(R.string.pwa) else context.getString(R.string.open_source) source = when { origin == Origin.GITLAB -> context.getString(R.string.system_app) origin == Origin.GPLAY -> "" is_pwa -> context.getString(R.string.pwa) else -> context.getString(R.string.open_source) } } } } Loading
app/src/main/java/foundation/e/apps/data/gitlab/EligibleSystemAppsApi.kt 0 → 100644 +34 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019-2023 E FOUNDATION * * 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 EligibleSystemAppsApi { 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 getAllEligibleApps(): Response<List<SystemAppProject>> } No newline at end of file
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) 2019-2023 E FOUNDATION * * 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> } No newline at end of file
app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt 0 → 100644 +136 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019-2023 E FOUNDATION * * 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.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 eligibleSystemAppsApi: EligibleSystemAppsApi, private val systemAppDefinitionApi: SystemAppDefinitionApi, private val applicationDataManager: ApplicationDataManager, private val appLoungePackageManager: AppLoungePackageManager, ) { private var systemAppProjectList = mutableListOf<SystemAppProject>() suspend fun fetchAllEligibleApps() { val response = eligibleSystemAppsApi.getAllEligibleApps() if (response.isSuccessful && !response.body().isNullOrEmpty()) { response.body()?.let { systemAppProjectList.addAll(it) } } } fun getAllEligibleApps(): List<String> { return systemAppProjectList.map { it.packageName } } private fun isSystemAppBlacklisted( systemAppInfo: SystemAppInfo, sdkLevel: Int, device: String, ): Boolean { return systemAppInfo.run { sdkLevel < minSdk || blacklistedAndroid?.contains(sdkLevel) == true || blacklistedDevices?.contains(device) == true || blacklistedDevices?.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) if (!response.isSuccessful) { Timber.e("Failed to fetch system app update definition for: $packageName, $releaseType") return null } val systemAppInfo = response.body() ?: return null if (isSystemAppBlacklisted(systemAppInfo, sdkLevel, device)) { Timber.e("Blacklisted system app: $packageName, $systemAppInfo") return null } return 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 eligibleApps = getAllEligibleApps() eligibleApps.forEach { if (!appLoungePackageManager.isInstalled(it)) { // Don't install for system apps which are removed (by root or otherwise) return@forEach } getSystemAppUpdateInfo( it, releaseType, sdkLevel, device, )?.run { applicationDataManager.updateStatus(this) updateList.add(this) updateSource(context) } } return updateList } } No newline at end of file
app/src/main/java/foundation/e/apps/data/gitlab/models/SystemAppInfo.kt 0 → 100644 +55 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019-2023 E FOUNDATION * * 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, val priority: Boolean?, @Json(name = "blacklisted_android") val blacklistedAndroid: List<Int>?, @Json(name = "blacklisted_devices") val blacklistedDevices: List<String>?, ) fun SystemAppInfo.toApplication(): Application { return Application( _id = UUID.randomUUID().toString(), author = "Murena SAS", description = "", latest_version_code = versionCode, latest_version_number = versionName, name = name, package_name = packageName, origin = Origin.GITLAB, originalSize = 1, // so that the app is not filtered out, url = downloadUrl, isSystemApp = true, filterLevel = FilterLevel.NONE, ) } No newline at end of file