Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c6849508 authored by Sayantan Roychowdhury's avatar Sayantan Roychowdhury
Browse files

Merge branch '6750-respect_update_channels_installers' into 'main'

Issue 6750: Respect app source during update using installer name

See merge request !299
parents f9bf20fb da8b16c2
Loading
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -128,6 +128,15 @@ class PkgManagerModule @Inject constructor(
        }
    }

    fun getInstallerName(packageName: String): String {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val installerInfo = packageManager.getInstallSourceInfo(packageName)
            installerInfo.originatingPackageName ?: installerInfo.installingPackageName ?: ""
        } else {
            packageManager.getInstallerPackageName(packageName) ?: ""
        }
    }

    /**
     * Installs the given package using system API
     * @param list List of [File] to be written to install session.
+130 −56
Original line number Diff line number Diff line
@@ -18,104 +18,178 @@

package foundation.e.apps.updates.manager

import android.content.Context
import android.content.pm.ApplicationInfo
import com.aurora.gplayapi.data.models.AuthData
import dagger.hilt.android.qualifiers.ApplicationContext
import foundation.e.apps.api.faultyApps.FaultyAppRepository
import foundation.e.apps.api.fused.FusedAPIImpl.Companion.APP_TYPE_ANY
import foundation.e.apps.api.fused.FusedAPIRepository
import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.manager.pkg.PkgManagerModule
import foundation.e.apps.utils.Constants
import foundation.e.apps.utils.enums.Origin
import foundation.e.apps.utils.enums.ResultStatus
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.enums.isUnFiltered
import foundation.e.apps.utils.modules.PreferenceManagerModule
import javax.inject.Inject

class UpdatesManagerImpl @Inject constructor(
    @ApplicationContext private val context: Context,
    private val pkgManagerModule: PkgManagerModule,
    private val fusedAPIRepository: FusedAPIRepository,
    private val faultyAppRepository: FaultyAppRepository
    private val faultyAppRepository: FaultyAppRepository,
    private val preferenceManagerModule: PreferenceManagerModule,
) {

    companion object {
        const val PACKAGE_NAME_F_DROID = "org.fdroid.fdroid"
        const val PACKAGE_NAME_F_DROID_PRIVILEGED = "org.fdroid.fdroid.privileged"
        const val PACKAGE_NAME_ANDROID_VENDING = "com.android.vending"
    }

    private val TAG = UpdatesManagerImpl::class.java.simpleName

    // TODO: MAKE THIS LOGIC MORE SANE
    private val userApplications: List<ApplicationInfo>
        get() = pkgManagerModule.getAllUserApps()

    suspend fun getUpdates(authData: AuthData): Pair<List<FusedApp>, ResultStatus> {
        val pkgList = mutableListOf<String>()
        val updateList = mutableListOf<FusedApp>()
        var status = ResultStatus.OK

        val userApplications = pkgManagerModule.getAllUserApps()
        userApplications.forEach { pkgList.add(it.packageName) }
        val openSourceInstalledApps = getOpenSourceInstalledApps().toMutableList()
        val gPlayInstalledApps = getGPlayInstalledApps().toMutableList()

        val otherStoreApps = getAppsFromOtherStores()

        if (pkgList.isNotEmpty()) {
            // Get updates from CleanAPK
        if (preferenceManagerModule.shouldUpdateAppsFromOtherStores()) {
            openSourceInstalledApps.addAll(otherStoreApps)
        }

            val cleanAPKResult = fusedAPIRepository.getApplicationDetails(
                pkgList,
        // Get open source app updates
        if (openSourceInstalledApps.isNotEmpty()) {
            status = getUpdatesFromApi({
                fusedAPIRepository.getApplicationDetails(
                    openSourceInstalledApps,
                    authData,
                    Origin.CLEANAPK
                )
            cleanAPKResult.first.forEach {
                if (it.package_name in pkgList) pkgList.remove(it.package_name)
                if (it.status == Status.UPDATABLE && it.filterLevel.isUnFiltered()) updateList.add(it)
            }
            cleanAPKResult.second.let {
                if (it != ResultStatus.OK) {
                    status = it
            }, updateList)
        }

        if (preferenceManagerModule.shouldUpdateAppsFromOtherStores()) {
            val updateListFromFDroid = updateList.map { it.package_name }
            val otherStoreAppsForGPlay = otherStoreApps - updateListFromFDroid.toSet()
            gPlayInstalledApps.addAll(otherStoreAppsForGPlay)
        }

            if (getApplicationCategoryPreference().contains(APP_TYPE_ANY)) {
                // Check for remaining apps from GPlay
                val gPlayResult = fusedAPIRepository.getApplicationDetails(
                    pkgList,
        // Get GPlay app updates
        if (getApplicationCategoryPreference().contains(APP_TYPE_ANY) &&
            gPlayInstalledApps.isNotEmpty()) {

            status = getUpdatesFromApi({
                fusedAPIRepository.getApplicationDetails(
                    gPlayInstalledApps,
                    authData,
                    Origin.GPLAY
                )
                gPlayResult.first.forEach {
                    if (it.status == Status.UPDATABLE && it.filterLevel.isUnFiltered()) updateList.add(
                        it
                    )
                }
                gPlayResult.second.let {
                    if (it != ResultStatus.OK) {
                        status = it
                    }
                }
            }
            }, updateList)
        }

        val nonFaultyUpdateList = faultyAppRepository.removeFaultyApps(updateList)
        return Pair(nonFaultyUpdateList, status)
    }

    suspend fun getUpdatesOSS(): Pair<List<FusedApp>, ResultStatus> {
        val pkgList = mutableListOf<String>()
        val updateList = mutableListOf<FusedApp>()
        var status = ResultStatus.OK

        val userApplications = pkgManagerModule.getAllUserApps()
        userApplications.forEach { pkgList.add(it.packageName) }
        val openSourceInstalledApps = getOpenSourceInstalledApps().toMutableList()

        if (pkgList.isNotEmpty()) {
            // Get updates from CleanAPK
            val cleanAPKResult = fusedAPIRepository.getApplicationDetails(
                pkgList,
        val otherStoreApps = getAppsFromOtherStores()

        if (preferenceManagerModule.shouldUpdateAppsFromOtherStores()) {
            openSourceInstalledApps.addAll(otherStoreApps)
        }

        if (openSourceInstalledApps.isNotEmpty()) {
            status = getUpdatesFromApi({
                fusedAPIRepository.getApplicationDetails(
                    openSourceInstalledApps,
                    AuthData("", ""),
                    Origin.CLEANAPK
                )
            cleanAPKResult.first.forEach {
                if (it.status == Status.UPDATABLE && it.filterLevel.isUnFiltered()) updateList.add(
                    it
                )
            }, updateList)
        }
            cleanAPKResult.second.let {
                if (it != ResultStatus.OK) {
                    status = it

        val nonFaultyUpdateList = faultyAppRepository.removeFaultyApps(updateList)
        return Pair(nonFaultyUpdateList, status)
    }

    /**
     * Lists apps directly updatable by App Lounge from the Open Source category.
     * (This includes apps installed by F-Droid client app, if used by the user;
     * F-Droid is not considered a third party source.)
     */
    private fun getOpenSourceInstalledApps(): List<String> {
        return userApplications.filter {
            pkgManagerModule.getInstallerName(it.packageName) in listOf(
                context.packageName,
                PACKAGE_NAME_F_DROID,
                PACKAGE_NAME_F_DROID_PRIVILEGED,
            )
        }.map { it.packageName }
    }

    /**
     * Lists GPlay apps directly updatable by App Lounge.
     *
     * GPlay apps installed by App Lounge alone can have their installer package
     * set as "com.android.vending".
     */
    private fun getGPlayInstalledApps(): List<String> {
        return userApplications.filter {
            pkgManagerModule.getInstallerName(it.packageName) in listOf(
                PACKAGE_NAME_ANDROID_VENDING,
            )
        }.map { it.packageName }
    }

        val nonFaultyUpdateList = faultyAppRepository.removeFaultyApps(updateList)
        return Pair(nonFaultyUpdateList, status)
    /**
     * Lists apps installed from other app stores.
     * (F-Droid client is not considered a third party source.)
     *
     * @return List of package names of apps installed from other app stores like
     * Aurora Store, Apk mirror, apps installed from browser, apps from ADB etc.
     */
    private fun getAppsFromOtherStores(): List<String> {
        return userApplications.filter {
            it.packageName !in (getGPlayInstalledApps() + getOpenSourceInstalledApps())
        }.map { it.packageName }
    }

    /**
     * Runs API (GPlay api or CleanApk) and accumulates the updatable apps
     * into a provided list.
     *
     * @param apiFunction Function that calls an API method to fetch update information.
     * Apps returned is filtered to get only the apps which can be downloaded and updated.
     * @param updateAccumulationList A list into which the filtered results from
     * [apiFunction] is stored. The caller needs to read this list to get the update info.
     *
     * @return ResultStatus from calling [apiFunction].
     */
    private suspend fun getUpdatesFromApi(
        apiFunction: suspend () -> Pair<List<FusedApp>, ResultStatus>,
        updateAccumulationList: MutableList<FusedApp>,
    ): ResultStatus {
        val apiResult = apiFunction()
        val updatableApps = apiResult.first.filter {
            it.status == Status.UPDATABLE && it.filterLevel.isUnFiltered()
        }
        updateAccumulationList.addAll(updatableApps)
        return apiResult.second
    }

    fun getApplicationCategoryPreference(): List<String> {
+5 −0
Original line number Diff line number Diff line
@@ -60,4 +60,9 @@ class PreferenceManagerModule @Inject constructor(
        context.getString(R.string.update_check_intervals),
        context.getString(R.string.preference_update_interval_default)
    )!!.toLong()

    fun shouldUpdateAppsFromOtherStores() = preferenceManager.getBoolean(
        context.getString(R.string.update_apps_from_other_stores),
        true
    )
}
+3 −0
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@
    <string name="preference_update_interval_title">Update check interval</string>
    <string name="preference_update_wifi_only_title">Only on un-metered networks</string>
    <string name="preference_update_wifi_only_description">Update apps automatically only on un-metered networks such as Wi-Fi</string>
    <string name="preference_update_apps_from_other_stores_title">Update apps installed by other stores</string>
    <string name="preference_update_apps_from_other_stores_description">Update apps installed from other app stores.\nSuch apps will be attempted to be updated from common apps and open source category.</string>
    <string name="preference_update_install_automatically_title">Automatically install updates</string>
    <string name="preference_update_install_automatically_description">Download and install app updates in the background</string>
    <string name="preference_update_notify_available_title">Show available updates</string>
@@ -176,6 +178,7 @@
    <string name="updateNotify" translatable="false">updateNotify</string>
    <string name="auto_install_enabled" translatable="false">updateInstallAuto</string>
    <string name="only_unmetered_network" translatable="false">updateUnmeteredOnly</string>
    <string name="update_apps_from_other_stores" translatable="false">updateAppsFromOtherStores</string>

    <!-- No Internet Fragment -->
    <string name="no_internet_message">Can\'t connect! Please check your internet connection and try again</string>
+8 −0
Original line number Diff line number Diff line
@@ -61,6 +61,14 @@
            android:title="@string/preference_update_wifi_only_title"
            app:singleLineTitle="false"
            app:iconSpaceReserved="false" />

        <CheckBoxPreference
            android:defaultValue="true"
            android:key="@string/update_apps_from_other_stores"
            android:summary="@string/preference_update_apps_from_other_stores_description"
            android:title="@string/preference_update_apps_from_other_stores_title"
            app:singleLineTitle="false"
            app:iconSpaceReserved="false" />
    </PreferenceCategory>

    <PreferenceCategory