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

Verified Commit 8795f48c authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

refactor: remove authentication from Search fragment

As search suggestions and search results are now using WebSearchHelper from GPlayAPI, these requests don't need to tie with authentication.

Search Fragment now extends from Android Fragment instead of TimeoutFragment to exclude authentication functionalities.
parent acc17cdc
Loading
Loading
Loading
Loading
+11 −66
Original line number Diff line number Diff line
@@ -29,10 +29,10 @@ import android.widget.CompoundButton.OnCheckedChangeListener
import android.widget.EditText
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SearchView
import androidx.cursoradapter.widget.CursorAdapter
import androidx.cursoradapter.widget.SimpleCursorAdapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
@@ -48,8 +48,6 @@ import foundation.e.apps.data.application.ApplicationInstaller
import foundation.e.apps.data.application.data.Application
import foundation.e.apps.data.enums.Status
import foundation.e.apps.data.install.models.AppInstall
import foundation.e.apps.data.login.AuthObject
import foundation.e.apps.data.login.exceptions.GPlayLoginException
import foundation.e.apps.databinding.FragmentSearchBinding
import foundation.e.apps.install.download.data.DownloadProgress
import foundation.e.apps.install.pkg.PwaManager
@@ -59,7 +57,6 @@ import foundation.e.apps.ui.MainActivityViewModel
import foundation.e.apps.ui.PrivacyInfoViewModel
import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment
import foundation.e.apps.ui.applicationlist.ApplicationListRVAdapter
import foundation.e.apps.ui.parentFragment.TimeoutFragment
import foundation.e.apps.utils.isNetworkAvailable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -68,9 +65,10 @@ import kotlinx.coroutines.launch
import java.util.Locale
import javax.inject.Inject

@Suppress("TooManyFunctions") // TODO: Remove after refactoring is complete
@AndroidEntryPoint
class SearchFragment :
    TimeoutFragment(R.layout.fragment_search),
    Fragment(R.layout.fragment_search),
    SearchView.OnQueryTextListener,
    SearchView.OnSuggestionListener,
    ApplicationInstaller {
@@ -84,7 +82,7 @@ class SearchFragment :
    protected val searchViewModel: SearchViewModel by viewModels()
    private val privacyInfoViewModel: PrivacyInfoViewModel by viewModels()
    private val appInfoFetchViewModel: AppInfoFetchViewModel by viewModels()
    override val mainActivityViewModel: MainActivityViewModel by activityViewModels()
    val mainActivityViewModel: MainActivityViewModel by activityViewModels()
    private val appProgressViewModel: AppProgressViewModel by viewModels()

    private val SUGGESTION_KEY = "suggestion"
@@ -130,25 +128,7 @@ class SearchFragment :

        setupSearchFilters()

        setupListening()

        authObjects.observe(viewLifecycleOwner) {
            val currentQuery = searchView?.query?.toString() ?: ""
            if (it == null || shouldIgnore(it, currentQuery)) {
                return@observe
            }

            if (currentQuery.isNotEmpty()) searchText = currentQuery

            val applicationListRVAdapter = recyclerView?.adapter as ApplicationListRVAdapter
            applicationListRVAdapter.setData(mutableListOf())

            loadDataWhenNetworkAvailable(it)
        }

        searchViewModel.exceptionsLiveData.observe(viewLifecycleOwner) {
            handleExceptionsCommon(it)
        }
        initiateSearch()

        binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
@@ -157,23 +137,12 @@ class SearchFragment :
                    if (!requireContext().isNetworkAvailable()) {
                        return
                    }

                    if (authObjects.value?.none { it is AuthObject.GPlayAuth } == true) {
                        return
                    }

                    searchViewModel.loadMore(searchText)
                }
            }
        })
    }

    private fun shouldIgnore(
        authObjectList: List<AuthObject>?,
        currentQuery: String
    ) = currentQuery.isNotEmpty() && searchViewModel.isAuthObjectListSame(authObjectList) &&
        lastSearch == currentQuery

    private fun observeSearchResult(listAdapter: ApplicationListRVAdapter?) {
        searchViewModel.searchResult.observe(viewLifecycleOwner) { result ->
            val apps = result.data?.first
@@ -342,41 +311,19 @@ class SearchFragment :
        updateSearchResult(applicationListRVAdapter, searchList)
    }

    override fun onTimeout(
        exception: Exception,
        predefinedDialog: AlertDialog.Builder
    ): AlertDialog.Builder? {
        return predefinedDialog
    }

    override fun onDataLoadError(
        exception: Exception,
        predefinedDialog: AlertDialog.Builder
    ): AlertDialog.Builder? {
        return predefinedDialog
    }

    override fun onSignInError(
        exception: GPlayLoginException,
        predefinedDialog: AlertDialog.Builder
    ): AlertDialog.Builder? {
        return predefinedDialog
    }

    override fun loadData(authObjectList: List<AuthObject>) {
    private fun initiateSearch() {
        showLoadingUI()
        searchViewModel.loadData(searchText, authObjectList) {
            clearAndRestartGPlayLogin()
        searchViewModel.loadData(searchText) {
            true
        }
    }

    override fun showLoadingUI() {
    private fun showLoadingUI() {
        shimmerLayout?.visibility = View.VISIBLE
        shimmerLayout?.startShimmer()
    }

    override fun stopLoadingUI() {
    private fun stopLoadingUI() {
        shimmerLayout?.stopShimmer()
        shimmerLayout?.visibility = View.GONE
    }
@@ -449,7 +396,7 @@ class SearchFragment :
             * Set the search text and call for network result.
             */
            searchText = text
            repostAuthObjects()
            initiateSearch()
        }
        return false
    }
@@ -463,11 +410,9 @@ class SearchFragment :
        searchJob?.cancel()
        searchJob = lifecycleScope.launch(Dispatchers.Main.immediate) {
            delay(SEARCH_DEBOUNCE_DELAY_MILLIS)
            authObjects.value?.find { it is AuthObject.GPlayAuth }?.run {
            searchViewModel.getSearchSuggestions(text)
        }
    }
    }

    override fun onSuggestionSelect(position: Int): Boolean {
        return true
+25 −20
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ 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
@@ -49,7 +50,8 @@ import javax.inject.Inject
class SearchViewModel @Inject constructor(
    private val applicationRepository: ApplicationRepository,
    private val privacyScoreRepository: PrivacyScoreRepository,
    private val appPrivacyInfoRepository: IAppPrivacyInfoRepository
    private val appPrivacyInfoRepository: IAppPrivacyInfoRepository,
    private val authenticatorRepository: AuthenticatorRepository
) : LoadingViewModel() {

    val searchSuggest: MutableLiveData<List<SearchSuggestEntry>?> = MutableLiveData()
@@ -98,12 +100,15 @@ class SearchViewModel @Inject constructor(

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

            val authObjects = authenticatorRepository.getAuthObjects()

            lastAuthObjects = authObjects

        this.lastAuthObjects = authObjects
            super.onLoadData(authObjects, { successObjects, failedObjects ->
                viewModelScope.launch {
                    mutex.withLock {
@@ -122,9 +127,9 @@ class SearchViewModel @Inject constructor(
                failedObjects.find { it is AuthObject.GPlayAuth }?.run {
                    fetchGplayData(query)
                }

            }, retryBlock)
        }
    }

    /*
     * Observe data from Fused API and publish the result in searchResult.