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

Commit 46049d65 authored by Abhishek Aggarwal's avatar Abhishek Aggarwal
Browse files

Merge branch '0000-clean_login-ai-review' into 'main'

refactor(login): move auth flows behind domain and app services

See merge request !717
parents 51e8add0 646c9a7d
Loading
Loading
Loading
Loading
Loading
+49 −18
Original line number Diff line number Diff line
@@ -66,6 +66,38 @@ def jacocoFileFilter = [
        '**/*Hilt*.*'
]

def jacocoCoverageProjects = [
        project(':app'),
        project(':data'),
        project(':domain'),
]

def collectJacocoClassDirectories = { Project module, String variantName ->
    return [
            module.fileTree("${module.buildDir}/intermediates/javac/${variantName}/classes") {
                exclude jacocoFileFilter
            },
            module.fileTree("${module.buildDir}/tmp/kotlin-classes/${variantName}") {
                exclude jacocoFileFilter
            }
    ]
}

def collectJacocoSourceDirectories = { Project module ->
    return [
            "${module.projectDir}/src/main/java",
            "${module.projectDir}/src/main/kotlin",
    ]
}

def collectJacocoExecutionData = { Project module, String unitTestTaskName ->
    return module.fileTree(dir: module.buildDir, includes: [
            "jacoco/${unitTestTaskName}.exec",
            "outputs/unit_test_code_coverage/**/${unitTestTaskName}.exec",
            "outputs/unit_test_code_coverage/**/*.ec",
    ])
}

tasks.withType(Test).configureEach {
    jacoco {
        includeNoLocationClasses = true
@@ -161,7 +193,9 @@ android.applicationVariants.configureEach { variant ->
    }

    tasks.register("jacoco${variantCap}Report", JacocoReport) {
        dependsOn(unitTestTask)
        dependsOn(jacocoCoverageProjects.collect { module ->
            "${module.path}:${unitTestTaskName}"
        })
        group = "verification"
        description = "Generates Jacoco coverage report for the ${variant.name} build."

@@ -170,26 +204,23 @@ android.applicationVariants.configureEach { variant ->
            html.required = true
        }

        def javaClasses = fileTree("${buildDir}/intermediates/javac/${variant.name}/classes") {
            exclude jacocoFileFilter
        }
        def kotlinClasses = fileTree("${buildDir}/tmp/kotlin-classes/${variant.name}") {
            exclude jacocoFileFilter
        classDirectories.from = files(
            jacocoCoverageProjects.collectMany { module ->
                collectJacocoClassDirectories(module, variant.name)
            }
        )

        classDirectories.from = files(javaClasses, kotlinClasses)

        def sourceDirs = variant.sourceSets.collect { sourceSet ->
            def dirs = []
            dirs.addAll(sourceSet.java.srcDirs)
            if (sourceSet.hasProperty('kotlin')) {
                dirs.addAll(sourceSet.kotlin.srcDirs)
        sourceDirectories.from = files(
            jacocoCoverageProjects.collectMany { module ->
                collectJacocoSourceDirectories(module)
            }
            return dirs
        }.flatten()
        )

        sourceDirectories.from = files(sourceDirs)
        executionData.from = file("${buildDir}/jacoco/${unitTestTaskName}.exec")
        executionData.from = files(
            jacocoCoverageProjects.collect { module ->
                collectJacocoExecutionData(module, unitTestTaskName)
            }
        )
    }
}

+21 −21
Original line number Diff line number Diff line
@@ -92,13 +92,6 @@ class AppLoungeApplication : Application(), Configuration.Provider {

        registerReceiver(pkgManagerBR, appLoungePackageManager.getFilter(), RECEIVER_EXPORTED)

        coroutineScope.launch {
            val currentVersion = sessionRepository.awaitTosVersion()
            if (!currentVersion.contentEquals(TOS_VERSION)) {
                sessionRepository.saveTocStatus(false, "")
            }
        }

        if (BuildConfig.DEBUG) {
            plant(Timber.DebugTree())
        } else {
@@ -118,6 +111,17 @@ class AppLoungeApplication : Application(), Configuration.Provider {
            })
        }

        // Robolectric eagerly creates the real Application for many unrelated unit tests.
        // Skip runtime startup jobs there to avoid leaking WorkManager/Room/DataStore work
        // across otherwise isolated tests.
        if (!isRunningUnderRobolectric()) {
            coroutineScope.launch {
                val currentVersion = sessionRepository.awaitTosVersion()
                if (!currentVersion.contentEquals(TOS_VERSION)) {
                    sessionRepository.saveTocStatus(false, "")
                }
            }

            coroutineScope.launch {
                migrateAnonymousUserUpdateIntervalUseCase()
                val updateInterval = getUpdateIntervalUseCase()
@@ -128,14 +132,10 @@ class AppLoungeApplication : Application(), Configuration.Provider {
                )
            }

        // Robolectric eagerly creates the real Application for many unrelated tests.
        // Skip install DB cleanup there to avoid background Room work leaking across tests.
        if (!isRunningUnderRobolectric()) {
            removeStalledInstallationFromDb()
        }

            installOrchestrator.init()
        }
    }

    private fun isRunningUnderRobolectric(): Boolean = Build.FINGERPRINT == "robolectric"

+60 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2026 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.di.bindings

import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import foundation.e.apps.data.login.microg.MicrogLoginManager
import foundation.e.apps.data.login.repository.AuthenticatorRepository
import foundation.e.apps.data.login.repository.OkHttpLoginCacheRepository
import foundation.e.apps.domain.login.LoginCacheRepository
import foundation.e.apps.login.MicrogAccountFetcher
import foundation.e.apps.login.PlayStoreAuthManager
import foundation.e.apps.login.StoreAuthCoordinator
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
interface LoginBindingModule {

    @Binds
    @Singleton
    fun bindLoginCacheRepository(
        okHttpLoginCacheRepository: OkHttpLoginCacheRepository,
    ): LoginCacheRepository

    @Binds
    @Singleton
    fun bindPlayStoreAuthManager(
        authenticatorRepository: AuthenticatorRepository,
    ): PlayStoreAuthManager

    @Binds
    @Singleton
    fun bindStoreAuthCoordinator(
        authenticatorRepository: AuthenticatorRepository,
    ): StoreAuthCoordinator

    @Binds
    @Singleton
    fun bindMicrogAccountFetcher(
        microgLoginManager: MicrogLoginManager,
    ): MicrogAccountFetcher
}
+7 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import foundation.e.apps.data.preference.AppLoungePreference
import foundation.e.apps.data.preference.PlayStoreAuthStore
import foundation.e.apps.data.preference.SessionDataStore
import foundation.e.apps.data.preference.updateinterval.UpdatePreferencesRepositoryImpl
import foundation.e.apps.domain.login.PlayStoreCredentialsRepository
import foundation.e.apps.domain.preferences.AppPreferencesRepository
import foundation.e.apps.domain.preferences.SessionRepository
import foundation.e.apps.domain.preferences.updateinterval.UpdatePreferencesRepository
@@ -58,4 +59,10 @@ interface PreferenceBindingModule {
    fun bindPlayStoreAuthStore(
        sessionDataStore: SessionDataStore
    ): PlayStoreAuthStore

    @Binds
    @Singleton
    fun bindPlayStoreCredentialsRepository(
        sessionDataStore: SessionDataStore
    ): PlayStoreCredentialsRepository
}
+20 −35
Original line number Diff line number Diff line
@@ -21,28 +21,28 @@ import foundation.e.apps.data.event.AppEvent
import foundation.e.apps.data.event.EventBus
import foundation.e.apps.data.gitlab.SystemAppsUpdatesRepository
import foundation.e.apps.data.install.workmanager.AppInstallProcessor
import foundation.e.apps.data.login.repository.AuthenticatorRepository
import foundation.e.apps.data.updates.UpdatesManagerRepository
import foundation.e.apps.domain.model.User
import foundation.e.apps.domain.preferences.AppPreferencesRepository
import foundation.e.apps.domain.preferences.SessionRepository
import foundation.e.apps.login.PlayStoreAuthManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import timber.log.Timber

@Suppress("LongParameterList")
@HiltWorker
@Suppress("LongParameterList")
class UpdatesWorker @AssistedInject constructor(
    @Assisted private val context: Context,
    @Assisted private val params: WorkerParameters,
    private val updatesManagerRepository: UpdatesManagerRepository,
    private val blockedAppRepository: BlockedAppRepository,
    private val systemAppsUpdatesRepository: SystemAppsUpdatesRepository,
    private val sessionRepository: SessionRepository,
    private val appPreferencesRepository: AppPreferencesRepository,
    private val authenticatorRepository: AuthenticatorRepository,
    private val playStoreAuthManager: PlayStoreAuthManager,
    private val appInstallProcessor: AppInstallProcessor,
    private val blockedAppRepository: BlockedAppRepository,
    private val systemAppsUpdatesRepository: SystemAppsUpdatesRepository,
) : CoroutineWorker(context, params) {

    companion object {
@@ -154,36 +154,19 @@ class UpdatesWorker @AssistedInject constructor(
    @VisibleForTesting
    suspend fun getAvailableUpdates(): Pair<List<Application>, ResultStatus> {
        loadSettings()
        val appsNeededToUpdate = mutableListOf<Application>()
        val user = getUser()
        val authData = authenticatorRepository.getValidatedAuthData().data
        val resultStatus: ResultStatus

        if (user in listOf(User.ANONYMOUS, User.GOOGLE) && authData != null) {
            /*
             * Signifies valid Google user and valid auth data to update
             * apps from Google Play store.
             * The user check will be more useful in No-Google mode.
             */
            val (apps, status) = updatesManagerRepository.getUpdates()
            appsNeededToUpdate.addAll(apps)
            resultStatus = status
        } else if (user == User.NO_GOOGLE) {
            /*
             * If authData is null, update apps from cleanapk only.
             */
            val (apps, status) = updatesManagerRepository.getUpdatesOSS()
            appsNeededToUpdate.addAll(apps)
            resultStatus = status
        } else {
            /*
             * If user in UNAVAILABLE, don't do anything.
             */
        val authData = playStoreAuthManager.getValidatedAuthData().data
        val canUsePlayStoreUpdates =
            (user == User.ANONYMOUS || user == User.GOOGLE) && authData != null

        return when {
            canUsePlayStoreUpdates -> updatesManagerRepository.getUpdates()
            user == User.NO_GOOGLE -> updatesManagerRepository.getUpdatesOSS()
            else -> {
                Timber.e("Update is aborted for unavailable user!")
            resultStatus = ResultStatus.UNKNOWN
                Pair(emptyList(), ResultStatus.UNKNOWN)
            }
        }

        return Pair(appsNeededToUpdate, resultStatus)
    }

    @VisibleForTesting
@@ -224,9 +207,11 @@ class UpdatesWorker @AssistedInject constructor(

    // returns list of Pair(app, status(success|failed))
    @VisibleForTesting
    suspend fun startUpdateProcess(appsNeededToUpdate: List<Application>): List<Pair<Application, Boolean>> {
    suspend fun startUpdateProcess(
        appsNeededToUpdate: List<Application>
    ): List<Pair<Application, Boolean>> {
        val response = mutableListOf<Pair<Application, Boolean>>()
        val authData = authenticatorRepository.getValidatedAuthData()
        val authData = playStoreAuthManager.getValidatedAuthData()
        val isNotLoggedIntoPersonalAccount =
            !authData.isValidData() || authData.data?.isAnonymous == true
        for (fusedApp in appsNeededToUpdate) {
Loading