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

Verified Commit 1954d148 authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

refactor: clean up core searching logic in SearchRepositoryImpl

parent 7fdf3fa4
Loading
Loading
Loading
Loading
+6 −10
Original line number Diff line number Diff line
@@ -27,18 +27,14 @@ typealias SearchResult = ResultSupreme<Pair<List<Application>, Boolean>>
interface SearchRepository {

    /**
     * Fetches search results from cleanAPK and GPlay servers and returns them
     * @param query Query
     * @return ResultSupreme which contains a Pair<List<FusedApp>, Boolean> where List<FusedApp>
     *     is the app list and [Boolean] indicates more data to load or not.
     * Fetches search results from CleanAPK.
     *
     * @return ResultSupreme containing a Pair<List<Application>, Boolean>,
     * where List<Application> contains search results and [Boolean] indicates more data to load or not.
     */
    suspend fun getCleanApkSearchResults(
        query: String
    ): SearchResult
    suspend fun getOpenSourceSearchResults(query: String): SearchResult

    suspend fun getGplaySearchResult(
        query: String,
    ): SearchResult
    suspend fun getPlayStoreSearchResults(query: String): SearchResult

    suspend fun getSearchSuggestions(query: String): List<SearchSuggestion>
}
+65 −117
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@ import foundation.e.apps.data.cleanapk.repositories.CleanApkRepository
import foundation.e.apps.data.enums.ResultStatus
import foundation.e.apps.data.enums.Source
import foundation.e.apps.data.handleNetworkResult
import foundation.e.apps.data.login.exceptions.CleanApkIOException
import foundation.e.apps.data.login.exceptions.GPlayIOException
import foundation.e.apps.data.playstore.PlayStoreRepository
import timber.log.Timber
@@ -47,124 +46,93 @@ class SearchRepositoryImpl @Inject constructor(
    @ApplicationContext
    lateinit var context: Context

    /**
     * Fetches search results from cleanAPK and returns them
     * @param query Query
     * @return A ResultSupreme with Pair of list of non-nullable [Application] and
     * a Boolean signifying if more search results are being loaded.
     */
    override suspend fun getCleanApkSearchResults(
        query: String,
    ): SearchResult {
        var finalSearchResult: SearchResult = ResultSupreme.Error(
            message = "",
            exception = CleanApkIOException("Unable to reach CleanAPK API")
        )

        val packageSpecificResults = if (isPackageName(query)) {
            fetchPackageSpecificResult(query).data?.first ?: emptyList()
    override suspend fun getOpenSourceSearchResults(query: String): SearchResult {
        val searchResultsByPackageName = if (isPackageName(query)) {
            fetchPackageSpecificResult(query).data?.first.orEmpty()
        } else {
            emptyList()
        }

        val searchResult = mutableListOf<Application>()
        val searchResultsByKeyword = buildList {
            if (stores.isStoreEnabled(Source.OPEN_SOURCE)) {
            finalSearchResult = fetchOpenSourceSearchResult(
                query,
                searchResult,
                packageSpecificResults
            )
                addAll(fetchOpenSourceSearchResult(query, searchResultsByPackageName))
            }

            if (stores.isStoreEnabled(Source.PWA)) {
            finalSearchResult = fetchPWASearchResult(
                query,
                searchResult,
                packageSpecificResults
            )
                addAll(fetchPwaSearchResult(query, searchResultsByPackageName))
            }
        }

        if (!stores.isStoreEnabled(Source.OPEN_SOURCE) && !stores.isStoreEnabled(Source.PWA)) {
            finalSearchResult = ResultSupreme.Success(
        return buildFinalSearchResult(searchResultsByKeyword, searchResultsByPackageName)
    }

    /**
     * Builds the final search result, combining keyword and package-specific results.
     */
    private fun buildFinalSearchResult(
        keywordSpecificResults: List<Application>,
        packageSpecificResults: List<Application>,
    ): SearchResult {
        return ResultSupreme.Success(
            Pair(
                filterWithKeywordSearch(
                    keywordSpecificResults,
                    packageSpecificResults,
                    "" // Query string no longer needed
                ),
                // Add loading indication for Play Store if enabled
                stores.isStoreEnabled(Source.PLAY_STORE)
            )
        )
    }

        return finalSearchResult
    }

    private fun isPackageName(keyword: String): Boolean {
        val packageNameRegex = ".*\\..*".toRegex() // com.example
        return packageNameRegex.matches(keyword)
    }

    private suspend fun fetchPWASearchResult(
    private suspend fun fetchPwaSearchResult(
        query: String,
        searchResult: MutableList<Application>,
        packageSpecificResults: List<Application>
    ): SearchResult {
        val pwaApps: MutableList<Application> = mutableListOf()
        packageSpecificResults: List<Application>,
    ): List<Application> {
        val result = handleNetworkResult {
            val apps =
                stores.getStore(Source.PWA)?.getSearchResults(query) ?: emptyList()
            stores.getStore(Source.PWA)?.getSearchResults(query).orEmpty()
        }

            apps.forEach {
                applicationDataManager.updateStatus(it)
                it.source = Source.PWA
                it.updateType()
                pwaApps.add(it)
        return if (result.isSuccess()) {
            result.data.orEmpty().map { updatePwa(it) }
                .apply { filterWithKeywordSearch(this, packageSpecificResults, query) }
        } else {
            emptyList()
        }
    }

        if (pwaApps.isNotEmpty() || result.getResultStatus() != ResultStatus.OK) {
            searchResult.addAll(pwaApps)
    private fun updatePwa(app: Application): Application {
        applicationDataManager.updateStatus(app)
        return app.apply {
            this.source = Source.PWA
            this.updateType()
        }

        return ResultSupreme.create(
            result.getResultStatus(),
            Pair(
                filterWithKeywordSearch(
                    searchResult,
                    packageSpecificResults,
                    query
                ),
                stores.isStoreEnabled(Source.PLAY_STORE)
            ),
            exception = result.exception
        )
    }

    private suspend fun fetchOpenSourceSearchResult(
        query: String,
        searchResult: MutableList<Application>,
        packageSpecificResults: List<Application>
    ): SearchResult {
        val cleanApkResults = mutableListOf<Application>()

        packageSpecificResults: List<Application>,
    ): List<Application> {
        val result = handleNetworkResult {
            cleanApkResults.addAll(getCleanAPKSearchResults(query))
            cleanApkResults
            stores.getStore(Source.OPEN_SOURCE)?.getSearchResults(query).orEmpty().map {
                applicationDataManager.updateStatus(it)
                it.updateType()
                it
            }

        if (cleanApkResults.isNotEmpty()) {
            searchResult.addAll(cleanApkResults)
        }

        return ResultSupreme.create(
            result.getResultStatus(),
            Pair(
                filterWithKeywordSearch(
                    searchResult,
                    packageSpecificResults,
                    query
                ),
                stores.isStoreEnabled(Source.PLAY_STORE) || stores.isStoreEnabled(Source.PWA)
            ),
            exception = result.exception
        )
        return if (result.isSuccess()) {
            result.data.orEmpty().apply {
                filterWithKeywordSearch(this, packageSpecificResults, query)
            }
        } else {
            emptyList()
        }
    }

    private suspend fun fetchPackageSpecificResult(
@@ -230,9 +198,7 @@ class SearchRepositoryImpl @Inject constructor(
        return finalList
    }

    private suspend fun getCleanApkPackageResult(
        query: String,
    ): Application? {
    private suspend fun getCleanApkPackageResult(query: String): Application? {
        getCleanApkSearchResult(query).let {
            if (it.isSuccess() && it.data!!.package_name.isNotBlank()) {
                return it.data!!
@@ -242,9 +208,7 @@ class SearchRepositoryImpl @Inject constructor(
        return null
    }

    private suspend fun getGplayPackageResult(
        query: String,
    ): Application? {
    private suspend fun getGplayPackageResult(query: String): Application? {
        val storeRepository = stores.getStore(Source.PLAY_STORE) as? PlayStoreRepository
        return storeRepository?.getAppDetailsWeb(query)
    }
@@ -259,7 +223,8 @@ class SearchRepositoryImpl @Inject constructor(
    private suspend fun getCleanApkSearchResult(packageName: String): ResultSupreme<Application> {
        var application = Application()
        val result = handleNetworkResult {
            val results = stores.getStore(Source.OPEN_SOURCE)?.getSearchResults(packageName) ?: emptyList()
            val results =
                stores.getStore(Source.OPEN_SOURCE)?.getSearchResults(packageName).orEmpty()

            if (results.isNotEmpty() && results.size == 1) {
                application = results[0]
@@ -282,35 +247,18 @@ class SearchRepositoryImpl @Inject constructor(
        return searchSuggestions
    }

    private suspend fun getCleanAPKSearchResults(
        keyword: String
    ): List<Application> {
        val list = mutableListOf<Application>()
        val response =
            stores.getStore(Source.OPEN_SOURCE)?.getSearchResults(keyword) ?: emptyList()
    override suspend fun getPlayStoreSearchResults(query: String): SearchResult {
        val source = Source.PLAY_STORE

        response.forEach {
            applicationDataManager.updateStatus(it)
            it.updateType()
            list.add(it)
        }

        return list
    }

    override suspend fun getGplaySearchResult(
        query: String,
    ): SearchResult {
        val result = handleNetworkResult {
            if (!stores.isStoreEnabled(Source.PLAY_STORE)) {
            if (!stores.isStoreEnabled(source)) {
                return@handleNetworkResult Pair(
                    listOf<Application>(),
                    setOf<SearchBundle.SubBundle>()
                )
            }

            val searchResults =
                stores.getStore(Source.PLAY_STORE)?.getSearchResults(query)
            val searchResults = stores.getStore(source)?.getSearchResults(query)
                    ?: throw IllegalStateException("Could not get store")

            val apps = replaceWithFDroid(searchResults).toMutableList()
+5 −8
Original line number Diff line number Diff line
@@ -114,12 +114,10 @@ class SearchViewModel @Inject constructor(
     * without having to wait for all of the apps.
     * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5171
     */
    private fun fetchCleanApkData(
        query: String
    ) {
    private fun fetchCleanApkData(query: String) {
        viewModelScope.launch(Dispatchers.IO) {
            val searchResultSupreme = searchRepository.getCleanApkSearchResults(query)
            emitFilteredResults(searchResultSupreme)
            val searchResults = searchRepository.getOpenSourceSearchResults(query)
            emitFilteredResults(searchResults)
        }
    }

@@ -141,10 +139,9 @@ class SearchViewModel @Inject constructor(
        viewModelScope.launch(Dispatchers.IO) {
            isLoading = true

            val gplaySearchResult =
                searchRepository.getGplaySearchResult(query)
            val playStoreSearchResults = searchRepository.getPlayStoreSearchResults(query)

            val currentAppList = updateCurrentAppList(gplaySearchResult)
            val currentAppList = updateCurrentAppList(playStoreSearchResults)

            val finalResult = ResultSupreme.Success(
                Pair(currentAppList.toList(), false)
+2 −2
Original line number Diff line number Diff line
@@ -182,7 +182,7 @@ class SearchRepositoryImplTest {
        setupMockingSearchApp(playStoreApps, gplayPackageResult)

        val searchResultLiveData =
            searchRepository.getCleanApkSearchResults("com.search.package")
            searchRepository.getOpenSourceSearchResults("com.search.package")

        val size = searchResultLiveData.data?.first?.size ?: -2
        assertEquals("getSearchResult", 8, size)
@@ -270,7 +270,7 @@ class SearchRepositoryImplTest {
        preferenceManagerModule.isGplaySelectedFake = true

        val searchResultLiveData =
            searchRepository.getCleanApkSearchResults("com.search.package")
            searchRepository.getOpenSourceSearchResults("com.search.package")

        val size = searchResultLiveData.data?.first?.size ?: -2
        assertEquals("getSearchResult", 4, size)