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

Verified Commit 53792be6 authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

refactor: remove authentication from SearchViewModel

To exclude authentication logic, SearchViewModel now extends from Android ViewModel instead of LoadingViewModel.

References to auth objects are removed from viewmodel.
parent 8795f48c
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -313,9 +313,7 @@ class SearchFragment :

    private fun initiateSearch() {
        showLoadingUI()
        searchViewModel.loadData(searchText) {
            true
        }
        searchViewModel.loadData(searchText)
    }

    private fun showLoadingUI() {
+25 −74
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ package foundation.e.apps.ui.search
import androidx.annotation.GuardedBy
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.aurora.gplayapi.SearchSuggestEntry
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -31,14 +32,9 @@ import foundation.e.apps.data.application.search.SearchResult
import foundation.e.apps.data.enums.Source
import foundation.e.apps.data.exodus.repositories.IAppPrivacyInfoRepository
import foundation.e.apps.data.exodus.repositories.PrivacyScoreRepository
import foundation.e.apps.data.login.AuthObject
import foundation.e.apps.data.login.AuthenticatorRepository
import foundation.e.apps.ui.parentFragment.LoadingViewModel
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -50,9 +46,8 @@ import javax.inject.Inject
class SearchViewModel @Inject constructor(
    private val applicationRepository: ApplicationRepository,
    private val privacyScoreRepository: PrivacyScoreRepository,
    private val appPrivacyInfoRepository: IAppPrivacyInfoRepository,
    private val authenticatorRepository: AuthenticatorRepository
) : LoadingViewModel() {
    private val appPrivacyInfoRepository: IAppPrivacyInfoRepository
) : ViewModel() {

    val searchSuggest: MutableLiveData<List<SearchSuggestEntry>?> = MutableLiveData()

@@ -60,8 +55,6 @@ class SearchViewModel @Inject constructor(
        MutableLiveData()
    val searchResult: LiveData<SearchResult> = _searchResult

    private var lastAuthObjects: List<AuthObject>? = null

    private var isLoading: Boolean = false

    @GuardedBy("mutex")
@@ -72,10 +65,6 @@ class SearchViewModel @Inject constructor(
    private var flagOpenSource: Boolean = false
    private var flagPWA: Boolean = false

    companion object {
        private const val PREVENT_HTTP_429_DELAY_IN_MS = 1000L
    }

    fun setFilterFlags(
        flagNoTrackers: Boolean = false,
        flagOpenSource: Boolean = false,
@@ -85,51 +74,37 @@ class SearchViewModel @Inject constructor(
        this.flagOpenSource = flagOpenSource
        this.flagPWA = flagPWA

        viewModelScope.launch(IO) {
        viewModelScope.launch(Dispatchers.IO) {
            emitFilteredResults(null)
        }
    }

    fun getSearchSuggestions(query: String) {
        viewModelScope.launch(IO) {
        viewModelScope.launch(Dispatchers.IO) {
            searchSuggest.postValue(
                applicationRepository.getSearchSuggestions(query)
            )
        }
    }

    fun loadData(
        query: String,
        retryBlock: (failedObjects: List<AuthObject>) -> Boolean
    ) {
        viewModelScope.launch {
            if (query.isBlank()) return@launch

            val authObjects = authenticatorRepository.getAuthObjects()
    fun loadData(query: String) {
        if (query.isBlank()) return

            lastAuthObjects = authObjects

            super.onLoadData(authObjects, { successObjects, failedObjects ->
        viewModelScope.launch {
            mutex.withLock {
                accumulatedList.clear()
            }
        }

                successObjects.find { it is AuthObject.CleanApk }?.run {
                    fetchCleanApkData(query)
                }

                successObjects.find { it is AuthObject.GPlayAuth }?.run {
                    fetchGplayData(query)
                }
        _searchResult.postValue(
            ResultSupreme.Success(
                Pair(emptyList(), false)
            )
        )

                failedObjects.find { it is AuthObject.GPlayAuth }?.run {
        fetchCleanApkData(query)
        fetchGplayData(query)
    }
            }, retryBlock)
        }
    }

    /*
     * Observe data from Fused API and publish the result in searchResult.
@@ -140,27 +115,18 @@ class SearchViewModel @Inject constructor(
    private fun fetchCleanApkData(
        query: String
    ) {
        viewModelScope.launch(IO) {
        viewModelScope.launch(Dispatchers.IO) {
            val searchResultSupreme = applicationRepository.getCleanApkSearchResults(query)

            emitFilteredResults(searchResultSupreme)

            if (!searchResultSupreme.isSuccess()) {
                searchResultSupreme.exception?.let { handleException(it) }
            }
        }
    }

    fun loadMore(query: String, autoTriggered: Boolean = false) {
        viewModelScope.launch(Main) {
    fun loadMore(query: String) {
        viewModelScope.launch(Dispatchers.Main) {
            if (isLoading) {
                Timber.d("Search result is loading....")
                return@launch
            }

            if (autoTriggered) {
                delay(PREVENT_HTTP_429_DELAY_IN_MS)
            }
            fetchGplayData(query)
        }
    }
@@ -170,18 +136,12 @@ class SearchViewModel @Inject constructor(
    }

    private fun fetchGplayData(query: String) {
        viewModelScope.launch(IO) {
        viewModelScope.launch(Dispatchers.IO) {
            isLoading = true

            val gplaySearchResult =
                applicationRepository.getGplaySearchResults(query)

            if (!gplaySearchResult.isSuccess()) {
                gplaySearchResult.exception?.let {
                    handleException(it)
                }
            }

            val currentAppList = updateCurrentAppList(gplaySearchResult)

            val finalResult = ResultSupreme.Success(
@@ -204,11 +164,6 @@ class SearchViewModel @Inject constructor(
        return currentAppList.distinctBy { it.package_name }
    }

    private fun handleException(exception: Exception) {
        exceptionsList.add(exception)
        exceptionsLiveData.postValue(exceptionsList)
    }

    /**
     * @return returns true if there is changes in data, otherwise false
     */
@@ -217,10 +172,6 @@ class SearchViewModel @Inject constructor(
        oldApplications: List<Application>
    ) = applicationRepository.isAnyFusedAppUpdated(newApplications, oldApplications)

    fun isAuthObjectListSame(authObjectList: List<AuthObject>?): Boolean {
        return lastAuthObjects == authObjectList
    }

    private fun hasTrackers(app: Application): Boolean {
        return when {
            app.privacyScore == 0 -> true               // Manually blocked apps (Facebook etc.)
@@ -236,7 +187,7 @@ class SearchViewModel @Inject constructor(
        }
    }

    private suspend fun getFilteredList(): List<Application> = withContext(IO) {
    private suspend fun getFilteredList(): List<Application> = withContext(Dispatchers.IO) {
        if (flagNoTrackers) {
            mutex.withLock {
                val deferredCheck = accumulatedList.map {