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

Commit 36864b3f authored by Sayantan Roychowdhury's avatar Sayantan Roychowdhury
Browse files

MR 9 - Adapt Search fragment

parent ede28063
Loading
Loading
Loading
Loading
+47 −41
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SearchView
import androidx.core.view.isVisible
import androidx.cursoradapter.widget.CursorAdapter
@@ -38,7 +39,6 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.AuthData
import com.facebook.shimmer.ShimmerFrameLayout
import dagger.hilt.android.AndroidEntryPoint
import foundation.e.apps.AppInfoFetchViewModel
@@ -52,17 +52,19 @@ import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.application.subFrags.ApplicationDialogFragment
import foundation.e.apps.applicationlist.ApplicationListRVAdapter
import foundation.e.apps.databinding.FragmentSearchBinding
import foundation.e.apps.login.AuthObject
import foundation.e.apps.manager.download.data.DownloadProgress
import foundation.e.apps.manager.pkg.PkgManagerModule
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.exceptions.GPlayValidationException
import foundation.e.apps.utils.modules.PWAManagerModule
import foundation.e.apps.utils.parentFragment.TimeoutFragment
import foundation.e.apps.utils.parentFragment.TimeoutFragment2
import kotlinx.coroutines.launch
import javax.inject.Inject

@AndroidEntryPoint
class SearchFragment :
    TimeoutFragment(R.layout.fragment_search),
    TimeoutFragment2(R.layout.fragment_search),
    SearchView.OnQueryTextListener,
    SearchView.OnSuggestionListener,
    FusedAPIInterface {
@@ -92,7 +94,7 @@ class SearchFragment :
    private var noAppsFoundLayout: LinearLayout? = null

    /*
     * Store the string from onQueryTextSubmit() and access it from refreshData()
     * Store the string from onQueryTextSubmit() and access it from loadData()
     * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413
     */
    private var searchText = ""
@@ -114,6 +116,17 @@ class SearchFragment :
        val listAdapter = setupSearchResult(view)

        observeSearchResult(listAdapter)

        setupListening()

        authObjects.observe(viewLifecycleOwner) {
            if (it == null) return@observe
            loadData(it)
        }

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

    private fun observeSearchResult(listAdapter: ApplicationListRVAdapter?) {
@@ -129,13 +142,6 @@ class SearchFragment :
            }

            observeScrollOfSearchResult(listAdapter)
            if (searchText.isNotBlank() && !it.isSuccess()) {
                /*
                 * If blank check is not performed then timeout dialog keeps
                 * popping up whenever search tab is opened.
                 */
                onTimeout()
            }
        }
    }

@@ -253,39 +259,42 @@ class SearchFragment :
        }
    }

    override fun onTimeout() {
        if (!isTimeoutDialogDisplayed()) {
            binding.loadingProgressBar.isVisible = false
            stopLoadingUI()
            displayTimeoutAlertDialog(
                timeoutFragment = this,
                activity = requireActivity(),
                message = getString(R.string.timeout_desc_cleanapk),
                positiveButtonText = getString(R.string.retry),
                positiveButtonBlock = {
                    showLoadingUI()
                    resetTimeoutDialogLock()
                    mainActivityViewModel.retryFetchingTokenAfterTimeout()
                },
                negativeButtonText = getString(android.R.string.ok),
                negativeButtonBlock = {},
                allowCancel = true,
            )
    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: GPlayValidationException,
        predefinedDialog: AlertDialog.Builder
    ): AlertDialog.Builder? {
        return predefinedDialog
    }

    override fun refreshData(authData: AuthData) {
    override fun loadData(authObjectList: List<AuthObject>) {
        showLoadingUI()
        searchViewModel.getSearchResults(searchText, authData, viewLifecycleOwner)
        searchViewModel.loadData(searchText, viewLifecycleOwner, authObjectList) {
            clearAndRestartGPlayLogin()
            true
        }
    }

    private fun showLoadingUI() {
    override fun showLoadingUI() {
        binding.shimmerLayout.startShimmer()
        binding.shimmerLayout.visibility = View.VISIBLE
        binding.recyclerView.visibility = View.GONE
    }

    private fun stopLoadingUI() {
    override fun stopLoadingUI() {
        binding.shimmerLayout.stopShimmer()
        binding.shimmerLayout.visibility = View.GONE
        binding.recyclerView.visibility = View.VISIBLE
@@ -315,16 +324,13 @@ class SearchFragment :

    override fun onResume() {
        super.onResume()
        resetTimeoutDialogLock()
        binding.shimmerLayout.startShimmer()
        appProgressViewModel.downloadProgress.observe(viewLifecycleOwner) {
            updateProgressOfInstallingApps(it)
        }

        if (shouldRefreshData()) {
            mainActivityViewModel.authData.value?.let {
                refreshData(it)
            }
            repostAuthObjects()
        }

        if (searchText.isEmpty() && (recyclerView?.adapter as ApplicationListRVAdapter).currentList.isEmpty()) {
@@ -358,15 +364,15 @@ class SearchFragment :
             * Set the search text and call for network result.
             */
            searchText = text
            refreshDataOrRefreshToken(mainActivityViewModel)
            repostAuthObjects()
        }
        return false
    }

    override fun onQueryTextChange(newText: String?): Boolean {
        newText?.let { text ->
            mainActivityViewModel.authData.value?.let {
                searchViewModel.getSearchSuggestions(text, it)
            authObjects.value?.find { it is AuthObject.GPlayAuth }?.run {
                searchViewModel.getSearchSuggestions(text, this as AuthObject.GPlayAuth)
            }
        }
        return true
+43 −4
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ package foundation.e.apps.search

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.AuthData
@@ -28,6 +27,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import foundation.e.apps.api.ResultSupreme
import foundation.e.apps.api.fused.FusedAPIRepository
import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.login.AuthObject
import foundation.e.apps.utils.exceptions.CleanApkException
import foundation.e.apps.utils.exceptions.GPlayException
import foundation.e.apps.utils.parentFragment.LoadingViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -35,18 +38,43 @@ import javax.inject.Inject
@HiltViewModel
class SearchViewModel @Inject constructor(
    private val fusedAPIRepository: FusedAPIRepository,
) : ViewModel() {
) : LoadingViewModel() {

    val searchSuggest: MutableLiveData<List<SearchSuggestEntry>?> = MutableLiveData()
    val searchResult: MutableLiveData<ResultSupreme<Pair<List<FusedApp>, Boolean>>> =
        MutableLiveData()

    fun getSearchSuggestions(query: String, authData: AuthData) {
    fun getSearchSuggestions(query: String, gPlayAuth: AuthObject.GPlayAuth) {
        viewModelScope.launch(Dispatchers.IO) {
            searchSuggest.postValue(fusedAPIRepository.getSearchSuggestions(query, authData))
            if (gPlayAuth.result.isSuccess())
            searchSuggest.postValue(fusedAPIRepository.getSearchSuggestions(query, gPlayAuth.result.data!!))
        }
    }

    fun loadData(
        query: String,
        lifecycleOwner: LifecycleOwner,
        authObjectList: List<AuthObject>,
        retryBlock: (failedObjects: List<AuthObject>) -> Boolean
    ) {

        if (query.isBlank()) return

        super.onLoadData(authObjectList, { successAuthList, _ ->

            successAuthList.find { it is AuthObject.GPlayAuth }?.run {
                getSearchResults(query, result.data!! as AuthData, lifecycleOwner)
                return@onLoadData
            }

            successAuthList.find { it is AuthObject.CleanApk }?.run {
                getSearchResults(query, AuthData("", ""), lifecycleOwner)
                return@onLoadData
            }

        }, retryBlock)
    }

    /*
     * Observe data from Fused API and publish the result in searchResult.
     * This allows us to show apps as they are being fetched from the network,
@@ -57,6 +85,17 @@ class SearchViewModel @Inject constructor(
        viewModelScope.launch(Dispatchers.Main) {
            fusedAPIRepository.getSearchResults(query, authData).observe(lifecycleOwner) {
                searchResult.postValue(it)

                if (!it.isSuccess()) {
                    val exception =
                        if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) {
                            GPlayException(it.isTimeout(), "Data load error")
                        }
                        else CleanApkException(it.isTimeout(), "Data load error")

                    exceptionsList.add(exception)
                    exceptionsLiveData.postValue(exceptionsList)
                }
            }
        }
    }