From 045f2985fd40ede75567134553a66852a76f87c4 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Fri, 13 May 2022 22:58:06 +0530 Subject: [PATCH 01/53] App lounge: (issue_5413) update displayTimeoutAlertDialog() method --- .../e/apps/MainActivityViewModel.kt | 94 +++++++++---------- .../foundation/e/apps/home/HomeFragment.kt | 25 +++-- 2 files changed, 66 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index d3023bd80..9e856b820 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -102,67 +102,67 @@ class MainActivityViewModel @Inject constructor( * Display timeout alert dialog. * * @param activity Activity class. Basically the MainActivity. - * @param positiveButtonBlock Code block when "Retry" is pressed. - * @param openSettings Code block when "Open Settings" button is pressed. - * This should open the [SettingsFragment] fragment. - * @param applicationTypeFromPreferences Application type string, can be one of - * [FusedAPIImpl.APP_TYPE_ANY], [FusedAPIImpl.APP_TYPE_OPEN], [FusedAPIImpl.APP_TYPE_PWA] + * @param message Alert dialog body. + * @param positiveButtonText Positive button text. Example "Retry" + * @param positiveButtonBlock Code block when [positiveButtonText] is pressed. + * @param negativeButtonText Negative button text. Example "Retry" + * @param negativeButtonBlock Code block when [negativeButtonText] is pressed. + * @param positiveButtonText Positive button text. Example "Retry" + * @param positiveButtonBlock Code block when [positiveButtonText] is pressed. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ fun displayTimeoutAlertDialog( activity: Activity, - positiveButtonBlock: () -> Unit, - openSettings: () -> Unit, - applicationTypeFromPreferences: String, + message: String, + positiveButtonText: String? = null, + positiveButtonBlock: (() -> Unit)? = null, + negativeButtonText: String? = null, + negativeButtonBlock: (() -> Unit)? = null, + neutralButtonText: String? = null, + neutralButtonBlock: (() -> Unit)? = null, + allowCancel: Boolean = true, ) { if (!this::timeoutAlertDialog.isInitialized) { timeoutAlertDialog = AlertDialog.Builder(activity).apply { setTitle(R.string.timeout_title) - /* - * Prevent dismissing the dialog from pressing outside as it will only - * show a blank screen below the dialog. - */ - setCancelable(false) - /* - * If user presses back button to close the dialog without selecting anything, - * close App Lounge. - */ - setOnKeyListener { dialog, keyCode, _ -> - if (keyCode == KeyEvent.KEYCODE_BACK) { - dialog.dismiss() - activity.finish() + if (!allowCancel) { + /* + * Prevent dismissing the dialog from pressing outside as it will only + * show a blank screen below the dialog. + */ + setCancelable(false) + /* + * If user presses back button to close the dialog without selecting anything, + * close App Lounge. + */ + setOnKeyListener { dialog, keyCode, _ -> + if (keyCode == KeyEvent.KEYCODE_BACK) { + dialog.dismiss() + activity.finish() + } + true } - true } }.create() } timeoutAlertDialog.apply { - /* - * Set retry button. - */ - setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.retry)) {_, _ -> - positiveButtonBlock() + setMessage(message) + positiveButtonText?.let { + setButton(DialogInterface.BUTTON_POSITIVE, it) { _, _ -> + positiveButtonBlock?.invoke() + } } - /* - * Set message based on apps from GPlay of cleanapk. - */ - setMessage( - activity.getString( - when (applicationTypeFromPreferences) { - FusedAPIImpl.APP_TYPE_ANY -> R.string.timeout_desc_gplay - else -> R.string.timeout_desc_cleanapk - } - ) - ) - /* - * Show "Open Setting" only for GPlay apps. - */ - if (applicationTypeFromPreferences == FusedAPIImpl.APP_TYPE_ANY) { - setButton( - DialogInterface.BUTTON_NEUTRAL, - activity.getString(R.string.open_settings) - ) { _, _ -> - openSettings() + negativeButtonText?.let { + setButton(DialogInterface.BUTTON_NEGATIVE, it) { _, _ -> + negativeButtonBlock?.invoke() + } + } + neutralButtonText?.let { + setButton(DialogInterface.BUTTON_NEUTRAL, it) { _, _ -> + neutralButtonBlock?.invoke() } } } diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index 4ece61dde..27178187c 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -33,6 +33,7 @@ import foundation.e.apps.AppInfoFetchViewModel import foundation.e.apps.AppProgressViewModel import foundation.e.apps.MainActivityViewModel import foundation.e.apps.R +import foundation.e.apps.api.fused.FusedAPIImpl import foundation.e.apps.api.fused.FusedAPIInterface import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.application.subFrags.ApplicationDialogFragment @@ -114,12 +115,24 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { if (!homeViewModel.isFusedHomesEmpty(it.first)) { homeParentRVAdapter.setData(it.first) } else if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { - mainActivityViewModel.displayTimeoutAlertDialog(requireActivity(), { - showLoadingShimmer() - mainActivityViewModel.retryFetchingTokenAfterTimeout() - }, { - openSettings() - }, it.second) + mainActivityViewModel.displayTimeoutAlertDialog( + activity = requireActivity(), + message = if (it.second == FusedAPIImpl.APP_TYPE_ANY) { + getString(R.string.timeout_desc_gplay) + } else { + getString(R.string.timeout_desc_cleanapk) + }, + positiveButtonText = getString(R.string.retry), + positiveButtonBlock = { + showLoadingShimmer() + mainActivityViewModel.retryFetchingTokenAfterTimeout() + }, + negativeButtonText = getString(R.string.open_settings), + negativeButtonBlock = { + openSettings() + }, + allowCancel = false, + ) } } -- GitLab From 7157f7c5c19d2edf117c5bc221ac02fdbdbe1177 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Fri, 13 May 2022 22:59:13 +0530 Subject: [PATCH 02/53] App lounge: (issue_5413) update setFirstTokenFetchTime() to only set if non-zero --- .../main/java/foundation/e/apps/MainActivityViewModel.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index 9e856b820..6b1de1ee8 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -41,7 +41,6 @@ import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.exceptions.ApiException import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel -import foundation.e.apps.api.fused.FusedAPIImpl import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.manager.database.fusedDownload.FusedDownload @@ -210,7 +209,9 @@ class MainActivityViewModel @Inject constructor( } fun setFirstTokenFetchTime() { - firstAuthDataFetchTime = SystemClock.uptimeMillis() + if (firstAuthDataFetchTime == 0L) { + firstAuthDataFetchTime = SystemClock.uptimeMillis() + } } fun isTimeEligibleForTokenRefresh(): Boolean { @@ -225,6 +226,7 @@ class MainActivityViewModel @Inject constructor( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ fun retryFetchingTokenAfterTimeout() { + firstAuthDataFetchTime = 0 setFirstTokenFetchTime() authValidity.postValue(false) } -- GitLab From fbc5c3acac3aec2f353ce2acd2cfb913db95391b Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Fri, 13 May 2022 23:33:15 +0530 Subject: [PATCH 03/53] App lounge: (issue_5413) GPlayAPIImpl: Send boolean result for fetchAuthData() --- .../foundation/e/apps/api/fused/FusedAPIImpl.kt | 2 +- .../e/apps/api/fused/FusedAPIRepository.kt | 2 +- .../foundation/e/apps/api/gplay/GPlayAPIImpl.kt | 15 +++++++++++++-- .../e/apps/api/gplay/GPlayAPIRepository.kt | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 8130bf9ae..104309842 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -206,7 +206,7 @@ class FusedAPIImpl @Inject constructor( return gPlayAPIRepository.getSearchSuggestions(query, authData) } - suspend fun fetchAuthData(): Unit? { + suspend fun fetchAuthData(): Boolean { return gPlayAPIRepository.fetchAuthData() } diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 745dd0773..1e83cb181 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -83,7 +83,7 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.getSearchSuggestions(query, authData) } - suspend fun fetchAuthData(): Unit? { + suspend fun fetchAuthData(): Boolean { return fusedAPIImpl.fetchAuthData() } diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt index f367cdaba..e4b8fc65e 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt @@ -51,12 +51,23 @@ class GPlayAPIImpl @Inject constructor( private val gPlayHttpClient: GPlayHttpClient ) { + /** + * Save auth data to preferences. + * Updated for network failures. + * Issue: + * https://gitlab.e.foundation/e/backlog/-/issues/5413 + * https://gitlab.e.foundation/e/backlog/-/issues/5404 + * + * @return true or false based on if the request was successful. + */ // TODO: DON'T HARDCODE DISPATCHERS IN ANY METHODS - suspend fun fetchAuthData() = withContext(Dispatchers.IO) { + suspend fun fetchAuthData(): Boolean = withContext(Dispatchers.IO) { val data = async { tokenRepository.getAuthData() } - data.await()?.let { + data.await().let { + if (it == null) return@withContext false it.locale = context.resources.configuration.locales[0] // update locale with the default locale from settings dataStoreModule.saveCredentials(it) + return@withContext true } } diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt index 56243a6a5..18f37ba54 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt @@ -30,7 +30,7 @@ class GPlayAPIRepository @Inject constructor( private val gPlayAPIImpl: GPlayAPIImpl ) { - suspend fun fetchAuthData(): Unit? { + suspend fun fetchAuthData(): Boolean { return gPlayAPIImpl.fetchAuthData() } -- GitLab From 65a0f96923afaf14d2aa70e78f0d5e2d8d9f7ac5 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Fri, 13 May 2022 23:50:35 +0530 Subject: [PATCH 04/53] App lounge: (issue_5413) MainActivityViewModel.getAuthData - check return of fetchAuthData() and retry getting auth data --- .../foundation/e/apps/MainActivityViewModel.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index 6b1de1ee8..7d4e14e9b 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -235,7 +235,19 @@ class MainActivityViewModel @Inject constructor( if (!authRequestRunning) { authRequestRunning = true viewModelScope.launch { - fusedAPIRepository.fetchAuthData() + /* + * If getting auth data failed, try getting again. + * Sending false in authValidity, triggers observer in MainActivity, + * causing it to destroy credentials and try to regenerate auth data. + * + * Issue: + * https://gitlab.e.foundation/e/backlog/-/issues/5413 + * https://gitlab.e.foundation/e/backlog/-/issues/5404 + */ + if (!fusedAPIRepository.fetchAuthData()) { + authRequestRunning = false + authValidity.postValue(false) + } } } } -- GitLab From 52116e0c78fb8a011b74d3ebf0338d19d9d622d3 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Fri, 13 May 2022 23:55:08 +0530 Subject: [PATCH 05/53] App lounge: (issue_5413) create getApplicationCategoryPreference() in HomeViewModel --- app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt | 4 ++++ .../java/foundation/e/apps/api/fused/FusedAPIRepository.kt | 4 ++++ app/src/main/java/foundation/e/apps/home/HomeViewModel.kt | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 104309842..f33a5ca97 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -111,6 +111,10 @@ class FusedAPIImpl @Inject constructor( return true } + fun getApplicationCategoryPreference(): String { + return preferenceManagerModule.preferredApplicationType() + } + /* * Offload fetching application to a different method to dynamically fallback to a different * app source if the user selected app source times out. diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 1e83cb181..e9d493ae8 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -42,6 +42,10 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.isFusedHomesEmpty(fusedHomes) } + fun getApplicationCategoryPreference(): String { + return fusedAPIImpl.getApplicationCategoryPreference() + } + suspend fun validateAuthData(authData: AuthData): Boolean { return fusedAPIImpl.validateAuthData(authData) } diff --git a/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt b/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt index 48e020bee..6cb074c70 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt @@ -47,6 +47,10 @@ class HomeViewModel @Inject constructor( } } + fun getApplicationCategoryPreference(): String { + return fusedAPIRepository.getApplicationCategoryPreference() + } + fun isFusedHomesEmpty(fusedHomes: List): Boolean { return fusedAPIRepository.isFusedHomesEmpty(fusedHomes) } -- GitLab From 4173417933aa8e4a56edbfb0f5e116287fa25214 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sat, 14 May 2022 00:09:21 +0530 Subject: [PATCH 06/53] App lounge: (issue_5413) create TimeoutFragment interface to allow calling timeout dialog from MainActivity. HomeFragment is TimeoutFragment. Other fragments will follow. --- .../java/foundation/e/apps/MainActivity.kt | 6 +++ .../foundation/e/apps/home/HomeFragment.kt | 47 +++++++++++-------- .../apps/utils/interfaces/TimeoutFragment.kt | 5 ++ 3 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragment.kt diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index f1e809af4..d6119851f 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -45,6 +45,7 @@ import foundation.e.apps.setup.signin.SignInViewModel import foundation.e.apps.updates.UpdatesNotifier import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User +import foundation.e.apps.utils.interfaces.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule import kotlinx.coroutines.launch import java.io.File @@ -140,6 +141,11 @@ class MainActivity : AppCompatActivity() { generateAuthDataBasedOnUserType(user) } else { Log.d(TAG, "Timeout validating auth data!") + val lastFragment = navHostFragment.childFragmentManager.fragments[0] + if (lastFragment is TimeoutFragment) { + Log.d(TAG, "Displaying timeout from MainActivity!") + lastFragment.onTimeout() + } } } } else { diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index 27178187c..9f6d415e4 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -44,13 +44,14 @@ 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.enums.User +import foundation.e.apps.utils.interfaces.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule.safeNavigate import foundation.e.apps.utils.modules.PWAManagerModule import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { +class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, TimeoutFragment { private var _binding: FragmentHomeBinding? = null private val binding get() = _binding!! @@ -114,25 +115,8 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { binding.parentRV.visibility = View.VISIBLE if (!homeViewModel.isFusedHomesEmpty(it.first)) { homeParentRVAdapter.setData(it.first) - } else if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { - mainActivityViewModel.displayTimeoutAlertDialog( - activity = requireActivity(), - message = if (it.second == FusedAPIImpl.APP_TYPE_ANY) { - getString(R.string.timeout_desc_gplay) - } else { - getString(R.string.timeout_desc_cleanapk) - }, - positiveButtonText = getString(R.string.retry), - positiveButtonBlock = { - showLoadingShimmer() - mainActivityViewModel.retryFetchingTokenAfterTimeout() - }, - negativeButtonText = getString(R.string.open_settings), - negativeButtonBlock = { - openSettings() - }, - allowCancel = false, - ) + } else { + onTimeout() } } @@ -141,6 +125,29 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { } } + override fun onTimeout() { + if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + mainActivityViewModel.displayTimeoutAlertDialog( + activity = requireActivity(), + message = if (homeViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { + getString(R.string.timeout_desc_gplay) + } else { + getString(R.string.timeout_desc_cleanapk) + }, + positiveButtonText = getString(R.string.retry), + positiveButtonBlock = { + showLoadingShimmer() + mainActivityViewModel.retryFetchingTokenAfterTimeout() + }, + negativeButtonText = getString(R.string.open_settings), + negativeButtonBlock = { + openSettings() + }, + allowCancel = false, + ) + } + } + /* * Offload loading home data to a different function, to allow retrying mechanism. */ diff --git a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragment.kt b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragment.kt new file mode 100644 index 000000000..fbf3dfd81 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragment.kt @@ -0,0 +1,5 @@ +package foundation.e.apps.utils.interfaces + +interface TimeoutFragment { + fun onTimeout() +} \ No newline at end of file -- GitLab From bc40864a970324565799ec61f1db8920a3741c58 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sat, 14 May 2022 00:10:09 +0530 Subject: [PATCH 07/53] App lounge: (issue_5413) create new enum class ResultStatus.kt --- .../java/foundation/e/apps/utils/enums/ResultStatus.kt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 app/src/main/java/foundation/e/apps/utils/enums/ResultStatus.kt diff --git a/app/src/main/java/foundation/e/apps/utils/enums/ResultStatus.kt b/app/src/main/java/foundation/e/apps/utils/enums/ResultStatus.kt new file mode 100644 index 000000000..f9f3c5de7 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/utils/enums/ResultStatus.kt @@ -0,0 +1,7 @@ +package foundation.e.apps.utils.enums + +enum class ResultStatus { + OK, + TIMEOUT, + UNKNOWN, +} \ No newline at end of file -- GitLab From 853942b126e86fa9a59843f617eebb685dca4e56 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sat, 14 May 2022 00:14:02 +0530 Subject: [PATCH 08/53] App lounge: (issue_5413) FusedAPIImpl.getHomeScreenData - modify to return Pair, ResultStatus> --- .../e/apps/api/fused/FusedAPIImpl.kt | 26 +++++++++---------- .../e/apps/api/fused/FusedAPIRepository.kt | 3 ++- .../foundation/e/apps/home/HomeFragment.kt | 3 ++- .../foundation/e/apps/home/HomeViewModel.kt | 3 ++- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index f33a5ca97..16a2a5097 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -47,6 +47,7 @@ import foundation.e.apps.utils.enums.AppTag import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type +import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.PWAManagerModule import foundation.e.apps.utils.modules.PreferenceManagerModule @@ -83,20 +84,16 @@ class FusedAPIImpl @Inject constructor( private var TAG = FusedAPIImpl::class.java.simpleName /** - * Pass application source type along with list of apps. - * Application source type may change in case of timeout of GPlay/cleanapk api. + * Pass list of FusedHome and status. + * Second argument can be of [ResultStatus.TIMEOUT] to indicate timeout. * - * The second item of the Pair can be one of [APP_TYPE_ANY], [APP_TYPE_OPEN], [APP_TYPE_PWA]. - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 + * Issue: + * https://gitlab.e.foundation/e/backlog/-/issues/5404 + * https://gitlab.e.foundation/e/backlog/-/issues/5413 */ - suspend fun getHomeScreenData(authData: AuthData): Pair, String> { + suspend fun getHomeScreenData(authData: AuthData): Pair, ResultStatus> { val preferredApplicationType = preferenceManagerModule.preferredApplicationType() - val initialData = getHomeScreenDataBasedOnApplicationType(authData, preferredApplicationType) - if (isFusedHomesEmpty(initialData.first)) { - Log.d(TAG, "Received empty home data.") - } - return initialData + return getHomeScreenDataBasedOnApplicationType(authData, preferredApplicationType) } /** @@ -124,8 +121,9 @@ class FusedAPIImpl @Inject constructor( private suspend fun getHomeScreenDataBasedOnApplicationType( authData: AuthData, applicationType: String - ): Pair, String> { + ): Pair, ResultStatus> { val list = mutableListOf() + var apiStatus = ResultStatus.OK try { /* * Each category of home apps (example "Top Free Apps") will have its own timeout. @@ -156,11 +154,13 @@ class FusedAPIImpl @Inject constructor( } } catch (e: TimeoutCancellationException) { e.printStackTrace() + apiStatus = ResultStatus.TIMEOUT Log.d(TAG, "Timed out fetching home data for type: $applicationType") } catch (e: Exception) { + apiStatus = ResultStatus.UNKNOWN e.printStackTrace() } - return Pair(list, applicationType) + return Pair(list, apiStatus) } suspend fun getCategoriesList(type: Category.Type, authData: AuthData): List { diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index e9d493ae8..bcd4f620f 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -25,6 +25,7 @@ import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.fused.data.FusedCategory import foundation.e.apps.api.fused.data.FusedHome import foundation.e.apps.manager.database.fusedDownload.FusedDownload +import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import javax.inject.Inject @@ -34,7 +35,7 @@ import javax.inject.Singleton class FusedAPIRepository @Inject constructor( private val fusedAPIImpl: FusedAPIImpl ) { - suspend fun getHomeScreenData(authData: AuthData): Pair, String> { + suspend fun getHomeScreenData(authData: AuthData): Pair, ResultStatus> { return fusedAPIImpl.getHomeScreenData(authData) } diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index 9f6d415e4..cc9503abe 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -42,6 +42,7 @@ import foundation.e.apps.home.model.HomeChildRVAdapter import foundation.e.apps.home.model.HomeParentRVAdapter import foundation.e.apps.manager.download.data.DownloadProgress import foundation.e.apps.manager.pkg.PkgManagerModule +import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User import foundation.e.apps.utils.interfaces.TimeoutFragment @@ -113,7 +114,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou homeViewModel.homeScreenData.observe(viewLifecycleOwner) { binding.shimmerLayout.visibility = View.GONE binding.parentRV.visibility = View.VISIBLE - if (!homeViewModel.isFusedHomesEmpty(it.first)) { + if (it.second == ResultStatus.OK) { homeParentRVAdapter.setData(it.first) } else { onTimeout() diff --git a/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt b/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt index 6cb074c70..6e39962eb 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt @@ -25,6 +25,7 @@ import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedHome +import foundation.e.apps.utils.enums.ResultStatus import kotlinx.coroutines.launch import javax.inject.Inject @@ -39,7 +40,7 @@ class HomeViewModel @Inject constructor( * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ - var homeScreenData: MutableLiveData, String>> = MutableLiveData() + var homeScreenData: MutableLiveData, ResultStatus>> = MutableLiveData() fun getHomeScreenData(authData: AuthData) { viewModelScope.launch { -- GitLab From 9b5f1a660f01da21292e6d80f9761307d65e2a5d Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sat, 14 May 2022 00:27:21 +0530 Subject: [PATCH 09/53] App lounge: (issue_5413) minor fixes for timeout dialog in HomeFragment. Update HomeViewModel.isFusedHomesEmpty() to directly use homeScreenData. --- app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 3 ++- app/src/main/java/foundation/e/apps/home/HomeViewModel.kt | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index cc9503abe..8f795f44b 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -115,6 +115,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou binding.shimmerLayout.visibility = View.GONE binding.parentRV.visibility = View.VISIBLE if (it.second == ResultStatus.OK) { + mainActivityViewModel.dismissTimeoutDialog() homeParentRVAdapter.setData(it.first) } else { onTimeout() @@ -127,7 +128,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou } override fun onTimeout() { - if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + if (homeViewModel.isFusedHomesEmpty() && !mainActivityViewModel.isTimeoutDialogDisplayed()) { mainActivityViewModel.displayTimeoutAlertDialog( activity = requireActivity(), message = if (homeViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { diff --git a/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt b/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt index 6e39962eb..5e20a1033 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt @@ -52,7 +52,9 @@ class HomeViewModel @Inject constructor( return fusedAPIRepository.getApplicationCategoryPreference() } - fun isFusedHomesEmpty(fusedHomes: List): Boolean { - return fusedAPIRepository.isFusedHomesEmpty(fusedHomes) + fun isFusedHomesEmpty(): Boolean { + return homeScreenData.value?.first?.let { + fusedAPIRepository.isFusedHomesEmpty(it) + } ?: true } } -- GitLab From 257cb2cdf5d1a2473e8c88bbc8d26f2cc8ae65f2 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sat, 14 May 2022 02:16:32 +0530 Subject: [PATCH 10/53] App lounge: (issue_5413) fix timeout dialog not updating buttons correctly --- .../e/apps/MainActivityViewModel.kt | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index 7d4e14e9b..13aa86205 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -126,43 +126,57 @@ class MainActivityViewModel @Inject constructor( if (!this::timeoutAlertDialog.isInitialized) { timeoutAlertDialog = AlertDialog.Builder(activity).apply { setTitle(R.string.timeout_title) - if (!allowCancel) { - /* - * Prevent dismissing the dialog from pressing outside as it will only - * show a blank screen below the dialog. - */ - setCancelable(false) - /* - * If user presses back button to close the dialog without selecting anything, - * close App Lounge. - */ - setOnKeyListener { dialog, keyCode, _ -> - if (keyCode == KeyEvent.KEYCODE_BACK) { - dialog.dismiss() - activity.finish() - } - true - } - } }.create() } timeoutAlertDialog.apply { + if (!allowCancel) { + /* + * Prevent dismissing the dialog from pressing outside as it will only + * show a blank screen below the dialog. + */ + setCancelable(false) + /* + * If user presses back button to close the dialog without selecting anything, + * close App Lounge. + */ + setOnKeyListener { dialog, keyCode, _ -> + if (keyCode == KeyEvent.KEYCODE_BACK) { + dialog.dismiss() + activity.finish() + } + true + } + } else { + setCancelable(true) + } + + /* + * Set message + */ setMessage(message) + + /* + * Set buttons. The code may seem long and repetitive, but this works, + * most other approaches do not update the button texts. + */ positiveButtonText?.let { setButton(DialogInterface.BUTTON_POSITIVE, it) { _, _ -> positiveButtonBlock?.invoke() } + getButton(DialogInterface.BUTTON_POSITIVE)?.text = it } negativeButtonText?.let { setButton(DialogInterface.BUTTON_NEGATIVE, it) { _, _ -> negativeButtonBlock?.invoke() } + getButton(DialogInterface.BUTTON_NEGATIVE)?.text = it } neutralButtonText?.let { setButton(DialogInterface.BUTTON_NEUTRAL, it) { _, _ -> neutralButtonBlock?.invoke() } + getButton(DialogInterface.BUTTON_NEUTRAL)?.text = it } } -- GitLab From 4a4c336af5a491e08e34545fb37c53884b06c597 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sat, 14 May 2022 03:07:19 +0530 Subject: [PATCH 11/53] App lounge: (issue_5413) [WIP] modify category fetching methods to return Triple, String, ResultStatus> --- .../e/apps/api/fused/FusedAPIImpl.kt | 178 ++++++++++++++---- .../e/apps/api/fused/FusedAPIRepository.kt | 2 +- .../e/apps/categories/CategoriesViewModel.kt | 4 +- 3 files changed, 145 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 16a2a5097..627d77b37 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -163,17 +163,39 @@ class FusedAPIImpl @Inject constructor( return Pair(list, apiStatus) } - suspend fun getCategoriesList(type: Category.Type, authData: AuthData): List { + /* + * Return three elements from the function. + * - List : List of categories. + * - String : String of application type - By default it is the value in preferences. + * In case there is any failure, for a specific type in handleAllSourcesCategories(), + * the string value is of that type. + * - ResultStatus : ResultStatus - by default is ResultStatus.OK. But in case there is a failure in + * any application category type, then it takes value of that failure. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ + suspend fun getCategoriesList(type: Category.Type, authData: AuthData): Triple, String, ResultStatus> { val categoriesList = mutableListOf() val preferredApplicationType = preferenceManagerModule.preferredApplicationType() + var apiStatus: ResultStatus = ResultStatus.OK + var applicationCategoryType = preferredApplicationType if (preferredApplicationType != APP_TYPE_ANY) { - handleCleanApkCategories(preferredApplicationType, categoriesList, type) + handleCleanApkCategories(preferredApplicationType, categoriesList, type).run { + if (this != ResultStatus.OK) { + apiStatus = this + } + } } else { - handleAllSourcesCategories(categoriesList, type, authData) + handleAllSourcesCategories(categoriesList, type, authData).run { + if (first != ResultStatus.OK) { + apiStatus = first + applicationCategoryType = second + } + } } categoriesList.sortBy { item -> item.title.lowercase() } - return categoriesList + return Triple(categoriesList, applicationCategoryType, apiStatus) } /** @@ -383,18 +405,19 @@ class FusedAPIImpl @Inject constructor( preferredApplicationType: String, categoriesList: MutableList, type: Category.Type - ) { - val data = getCleanApkCategories(preferredApplicationType) - - data?.let { category -> - categoriesList.addAll( - getFusedCategoryBasedOnCategoryType( - category, - type, - getCategoryTag(preferredApplicationType) + ): ResultStatus { + return runCodeBlockWithTimeout({ + val data = getCleanApkCategories(preferredApplicationType) + data?.let { category -> + categoriesList.addAll( + getFusedCategoryBasedOnCategoryType( + category, + type, + getCategoryTag(preferredApplicationType) + ) ) - ) - } + } + }) } private fun getCategoryTag(preferredApplicationType: String): AppTag { @@ -413,35 +436,116 @@ class FusedAPIImpl @Inject constructor( } } + /* + * Function to populate a given category list, from all GPlay categories, open source categories, + * and PWAs. + * + * Returns: Pair of: + * - ResultStatus - by default ResultStatus.OK, but can be different in case of an error in any category. + * - String - Application category type having error. If no error, then blank string. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ private suspend fun handleAllSourcesCategories( categoriesList: MutableList, type: Category.Type, authData: AuthData - ) { - var data = getOpenSourceCategories() - data?.let { - categoriesList.addAll( - getFusedCategoryBasedOnCategoryType( - it, - type, - AppTag.OpenSource(context.getString(R.string.open_source)) + ): Pair { + var data: Categories? = null + var apiStatus = ResultStatus.OK + var errorApplicationCategory = "" + + /* + * Try within timeout limit for open source native apps categories. + */ + runCodeBlockWithTimeout({ + data = getOpenSourceCategories() + data?.let { + categoriesList.addAll( + getFusedCategoryBasedOnCategoryType( + it, + type, + AppTag.OpenSource(context.getString(R.string.open_source)) + ) ) - ) - } - data = getPWAsCategories() - data?.let { - categoriesList.addAll( - getFusedCategoryBasedOnCategoryType( - it, type, AppTag.PWA(context.getString(R.string.pwa)) + } + }, { + errorApplicationCategory = APP_TYPE_OPEN + apiStatus = ResultStatus.TIMEOUT + }, { + errorApplicationCategory = APP_TYPE_OPEN + apiStatus = ResultStatus.UNKNOWN + }) + + + /* + * Try within timeout limit to get PWA categories + */ + runCodeBlockWithTimeout({ + data = getPWAsCategories() + data?.let { + categoriesList.addAll( + getFusedCategoryBasedOnCategoryType( + it, type, AppTag.PWA(context.getString(R.string.pwa)) + ) ) - ) - } - val playResponse = gPlayAPIRepository.getCategoriesList(type, authData).map { app -> - val category = app.transformToFusedCategory() - updateCategoryDrawable(category, app) - category + } + }, { + errorApplicationCategory = APP_TYPE_PWA + apiStatus = ResultStatus.TIMEOUT + }, { + errorApplicationCategory = APP_TYPE_PWA + apiStatus = ResultStatus.UNKNOWN + }) + + /* + * Try within timeout limit to get native app categories from Play Store + */ + runCodeBlockWithTimeout({ + val playResponse = gPlayAPIRepository.getCategoriesList(type, authData).map { app -> + val category = app.transformToFusedCategory() + updateCategoryDrawable(category, app) + category + } + categoriesList.addAll(playResponse) + }, { + errorApplicationCategory = APP_TYPE_ANY + apiStatus = ResultStatus.TIMEOUT + }, { + errorApplicationCategory = APP_TYPE_ANY + apiStatus = ResultStatus.UNKNOWN + }) + + return Pair(apiStatus, errorApplicationCategory) + } + + /** + * Run a block of code with timeout. Returns status. + * + * @param block Main block to execute within [timeoutDurationInMillis] limit. + * @param timeoutBlock Optional code to execute in case of timeout. + * @param exceptionBlock Optional code to execute in case of an exception other than timeout. + * + * @return Instance of [ResultStatus] based on whether [block] was executed within timeout limit. + */ + private suspend fun runCodeBlockWithTimeout( + block: suspend () -> Unit, + timeoutBlock: (() -> Unit)? = null, + exceptionBlock: (() -> Unit)? = null, + ): ResultStatus { + return try { + withTimeout(timeoutDurationInMillis) { + block() + } + ResultStatus.OK + } catch (e: TimeoutCancellationException) { + timeoutBlock?.invoke() + ResultStatus.TIMEOUT + } catch (e: Exception) { + e.printStackTrace() + exceptionBlock?.invoke() + ResultStatus.UNKNOWN } - categoriesList.addAll(playResponse) } private fun updateCategoryDrawable( diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index bcd4f620f..87815c3ab 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -80,7 +80,7 @@ class FusedAPIRepository @Inject constructor( ) } - suspend fun getCategoriesList(type: Category.Type, authData: AuthData): List { + suspend fun getCategoriesList(type: Category.Type, authData: AuthData): Triple, String, ResultStatus> { return fusedAPIImpl.getCategoriesList(type, authData) } diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesViewModel.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesViewModel.kt index 100e63982..a2f0d10ae 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesViewModel.kt @@ -26,6 +26,7 @@ import com.aurora.gplayapi.data.models.Category import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedCategory +import foundation.e.apps.utils.enums.ResultStatus import kotlinx.coroutines.launch import javax.inject.Inject @@ -34,7 +35,8 @@ class CategoriesViewModel @Inject constructor( private val fusedAPIRepository: FusedAPIRepository ) : ViewModel() { - val categoriesList: MutableLiveData> = MutableLiveData() + val categoriesList: MutableLiveData, String, ResultStatus>> = + MutableLiveData() fun getCategoriesList(type: Category.Type, authData: AuthData) { viewModelScope.launch { -- GitLab From e073d6df0aee270cd8d7b032679c05e7898b99e2 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sat, 14 May 2022 03:08:28 +0530 Subject: [PATCH 12/53] App lounge: (issue_5413) implement timeout dialog in apps and games fragment --- .../e/apps/categories/AppsFragment.kt | 38 ++++++++++++++++++- .../e/apps/categories/CategoriesFragment.kt | 9 ++++- .../e/apps/categories/CategoriesViewModel.kt | 4 ++ .../e/apps/categories/GamesFragment.kt | 38 ++++++++++++++++++- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index 27ee9360d..ac181e991 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -30,9 +30,11 @@ import foundation.e.apps.MainActivityViewModel import foundation.e.apps.R import foundation.e.apps.categories.model.CategoriesRVAdapter import foundation.e.apps.databinding.FragmentAppsBinding +import foundation.e.apps.utils.enums.ResultStatus +import foundation.e.apps.utils.interfaces.TimeoutFragment @AndroidEntryPoint -class AppsFragment : Fragment(R.layout.fragment_apps) { +class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragment { private var _binding: FragmentAppsBinding? = null private val binding get() = _binding!! @@ -63,13 +65,45 @@ class AppsFragment : Fragment(R.layout.fragment_apps) { } categoriesViewModel.categoriesList.observe(viewLifecycleOwner) { - categoriesRVAdapter.setData(it) + categoriesRVAdapter.setData(it.first) binding.shimmerLayout.visibility = View.GONE recyclerView.visibility = View.VISIBLE + if (it.third != ResultStatus.OK) { + onTimeout() + } } } } + override fun onTimeout() { + if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + mainActivityViewModel.displayTimeoutAlertDialog( + activity = requireActivity(), + message = getString(R.string.timeout_desc_cleanapk), + positiveButtonText = if (!categoriesViewModel.isCategoriesEmpty()) { + /* + * It may happen that both GPlay and cleanapk is unreachable. + * In that case of user presses OK to dismiss the dialog, + * only blank screen will be shown. + */ + getString(android.R.string.ok) + } else null, + positiveButtonBlock = {}, + negativeButtonText = getString(R.string.retry), + negativeButtonBlock = { + showLoadingShimmer() + mainActivityViewModel.retryFetchingTokenAfterTimeout() + }, + allowCancel = !categoriesViewModel.isCategoriesEmpty(), + ) + } + } + + private fun showLoadingShimmer() { + binding.shimmerLayout.visibility = View.VISIBLE + binding.recyclerView.visibility = View.GONE + } + override fun onResume() { super.onResume() binding.shimmerLayout.startShimmer() diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt index 59e949435..973503e81 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt @@ -26,9 +26,10 @@ import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.categories.model.CategoriesVPAdapter import foundation.e.apps.databinding.FragmentCategoriesBinding +import foundation.e.apps.utils.interfaces.TimeoutFragment @AndroidEntryPoint -class CategoriesFragment : Fragment(R.layout.fragment_categories) { +class CategoriesFragment : Fragment(R.layout.fragment_categories), TimeoutFragment { private var _binding: FragmentCategoriesBinding? = null private val binding get() = _binding!! @@ -53,4 +54,10 @@ class CategoriesFragment : Fragment(R.layout.fragment_categories) { super.onDestroyView() _binding = null } + + override fun onTimeout() { + childFragmentManager.fragments[0]?.let { + if (it is TimeoutFragment) it.onTimeout() + } + } } diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesViewModel.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesViewModel.kt index a2f0d10ae..ec72f9150 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesViewModel.kt @@ -43,4 +43,8 @@ class CategoriesViewModel @Inject constructor( categoriesList.postValue(fusedAPIRepository.getCategoriesList(type, authData)) } } + + fun isCategoriesEmpty(): Boolean { + return categoriesList.value?.first?.isEmpty() ?: true + } } diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 320d23768..d19fe34aa 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -30,9 +30,11 @@ import foundation.e.apps.MainActivityViewModel import foundation.e.apps.R import foundation.e.apps.categories.model.CategoriesRVAdapter import foundation.e.apps.databinding.FragmentGamesBinding +import foundation.e.apps.utils.enums.ResultStatus +import foundation.e.apps.utils.interfaces.TimeoutFragment @AndroidEntryPoint -class GamesFragment : Fragment(R.layout.fragment_games) { +class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragment { private var _binding: FragmentGamesBinding? = null private val binding get() = _binding!! @@ -64,12 +66,44 @@ class GamesFragment : Fragment(R.layout.fragment_games) { } categoriesViewModel.categoriesList.observe(viewLifecycleOwner) { - categoriesRVAdapter.setData(it) + categoriesRVAdapter.setData(it.first) binding.shimmerLayout.visibility = View.GONE recyclerView.visibility = View.VISIBLE + if (it.third != ResultStatus.OK) { + onTimeout() + } + } + } + + override fun onTimeout() { + if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + mainActivityViewModel.displayTimeoutAlertDialog( + activity = requireActivity(), + message = getString(R.string.timeout_desc_cleanapk), + positiveButtonText = if (!categoriesViewModel.isCategoriesEmpty()) { + /* + * It may happen that both GPlay and cleanapk is unreachable. + * In that case of user presses OK to dismiss the dialog, + * only blank screen will be shown. + */ + getString(android.R.string.ok) + } else null, + positiveButtonBlock = {}, + negativeButtonText = getString(R.string.retry), + negativeButtonBlock = { + showLoadingShimmer() + mainActivityViewModel.retryFetchingTokenAfterTimeout() + }, + allowCancel = !categoriesViewModel.isCategoriesEmpty(), + ) } } + private fun showLoadingShimmer() { + binding.shimmerLayout.visibility = View.VISIBLE + binding.recyclerView.visibility = View.GONE + } + override fun onResume() { super.onResume() binding.shimmerLayout.startShimmer() -- GitLab From 8b3cb8bea1d91c76d501cdb084f7799c957572c5 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sat, 14 May 2022 03:11:14 +0530 Subject: [PATCH 13/53] App lounge: (issue_5413) rename TimeoutFragment -> TimeoutFragmentInterface --- app/src/main/java/foundation/e/apps/MainActivity.kt | 4 ++-- .../main/java/foundation/e/apps/categories/AppsFragment.kt | 4 ++-- .../java/foundation/e/apps/categories/CategoriesFragment.kt | 6 +++--- .../main/java/foundation/e/apps/categories/GamesFragment.kt | 4 ++-- app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 4 ++-- .../{TimeoutFragment.kt => TimeoutFragmentInterface.kt} | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) rename app/src/main/java/foundation/e/apps/utils/interfaces/{TimeoutFragment.kt => TimeoutFragmentInterface.kt} (63%) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index d6119851f..b29c87da3 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -45,7 +45,7 @@ import foundation.e.apps.setup.signin.SignInViewModel import foundation.e.apps.updates.UpdatesNotifier import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.interfaces.TimeoutFragment +import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface import foundation.e.apps.utils.modules.CommonUtilsModule import kotlinx.coroutines.launch import java.io.File @@ -142,7 +142,7 @@ class MainActivity : AppCompatActivity() { } else { Log.d(TAG, "Timeout validating auth data!") val lastFragment = navHostFragment.childFragmentManager.fragments[0] - if (lastFragment is TimeoutFragment) { + if (lastFragment is TimeoutFragmentInterface) { Log.d(TAG, "Displaying timeout from MainActivity!") lastFragment.onTimeout() } diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index ac181e991..581314981 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -31,10 +31,10 @@ import foundation.e.apps.R import foundation.e.apps.categories.model.CategoriesRVAdapter import foundation.e.apps.databinding.FragmentAppsBinding import foundation.e.apps.utils.enums.ResultStatus -import foundation.e.apps.utils.interfaces.TimeoutFragment +import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface @AndroidEntryPoint -class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragment { +class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface { private var _binding: FragmentAppsBinding? = null private val binding get() = _binding!! diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt index 973503e81..369ab70ed 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt @@ -26,10 +26,10 @@ import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.categories.model.CategoriesVPAdapter import foundation.e.apps.databinding.FragmentCategoriesBinding -import foundation.e.apps.utils.interfaces.TimeoutFragment +import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface @AndroidEntryPoint -class CategoriesFragment : Fragment(R.layout.fragment_categories), TimeoutFragment { +class CategoriesFragment : Fragment(R.layout.fragment_categories), TimeoutFragmentInterface { private var _binding: FragmentCategoriesBinding? = null private val binding get() = _binding!! @@ -57,7 +57,7 @@ class CategoriesFragment : Fragment(R.layout.fragment_categories), TimeoutFragme override fun onTimeout() { childFragmentManager.fragments[0]?.let { - if (it is TimeoutFragment) it.onTimeout() + if (it is TimeoutFragmentInterface) it.onTimeout() } } } diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index d19fe34aa..44b1e9daa 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -31,10 +31,10 @@ import foundation.e.apps.R import foundation.e.apps.categories.model.CategoriesRVAdapter import foundation.e.apps.databinding.FragmentGamesBinding import foundation.e.apps.utils.enums.ResultStatus -import foundation.e.apps.utils.interfaces.TimeoutFragment +import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface @AndroidEntryPoint -class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragment { +class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterface { private var _binding: FragmentGamesBinding? = null private val binding get() = _binding!! diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index 8f795f44b..ade1e3614 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -45,14 +45,14 @@ import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.interfaces.TimeoutFragment +import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface import foundation.e.apps.utils.modules.CommonUtilsModule.safeNavigate import foundation.e.apps.utils.modules.PWAManagerModule import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, TimeoutFragment { +class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, TimeoutFragmentInterface { private var _binding: FragmentHomeBinding? = null private val binding get() = _binding!! diff --git a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragment.kt b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt similarity index 63% rename from app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragment.kt rename to app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt index fbf3dfd81..e69163e24 100644 --- a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragment.kt +++ b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt @@ -1,5 +1,5 @@ package foundation.e.apps.utils.interfaces -interface TimeoutFragment { +interface TimeoutFragmentInterface { fun onTimeout() } \ No newline at end of file -- GitLab From 810a044bf606a9f45999f019482cf0a3293a1a98 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sat, 14 May 2022 18:21:29 +0530 Subject: [PATCH 14/53] App lounge: (issue_5413) change result of FusedAPIImpl.getApplicationDetails() to Pair(FusedApp, ResultStatus) --- .../e/apps/api/fused/FusedAPIImpl.kt | 30 +++++++++++-------- .../e/apps/api/fused/FusedAPIRepository.kt | 2 +- .../e/apps/application/ApplicationFragment.kt | 24 ++++++++++++--- .../apps/application/ApplicationViewModel.kt | 9 +++--- 4 files changed, 44 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 627d77b37..32640c623 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -383,18 +383,24 @@ class FusedAPIImpl @Inject constructor( packageName: String, authData: AuthData, origin: Origin - ): FusedApp { - val response = if (origin == Origin.CLEANAPK) { - cleanAPKRepository.getAppOrPWADetailsByID(id).body()?.app - } else { - val app = gPlayAPIRepository.getAppDetails(packageName, authData) - app?.transformToFusedApp() - } - response?.let { - it.updateStatus() - it.updateType() - } - return response ?: FusedApp() + ): Pair { + + var response : FusedApp? = null + + val status = runCodeBlockWithTimeout({ + response = if (origin == Origin.CLEANAPK) { + cleanAPKRepository.getAppOrPWADetailsByID(id).body()?.app + } else { + val app = gPlayAPIRepository.getAppDetails(packageName, authData) + app?.transformToFusedApp() + } + response?.let { + it.updateStatus() + it.updateType() + } + }) + + return Pair(response ?: FusedApp(), status) } /* diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 87815c3ab..8b58edb85 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -64,7 +64,7 @@ class FusedAPIRepository @Inject constructor( packageName: String, authData: AuthData, origin: Origin - ): FusedApp { + ): Pair { return fusedAPIImpl.getApplicationDetails(id, packageName, authData, origin) } diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index d5f360c6b..eeca73a10 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -57,6 +57,7 @@ import foundation.e.apps.databinding.FragmentApplicationBinding import foundation.e.apps.manager.download.data.DownloadProgress import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.utils.enums.Origin +import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User import foundation.e.apps.utils.modules.CommonUtilsModule.LIST_OF_NULL @@ -137,7 +138,22 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { applicationViewModel.updateApplicationStatus(list) } - applicationViewModel.fusedApp.observe(viewLifecycleOwner) { + applicationViewModel.fusedApp.observe(viewLifecycleOwner) { resultPair -> + if (resultPair.second != ResultStatus.OK) { + return@observe + } + + /* + * Previously fusedApp only had instance of FusedApp. + * As such previously all reference was simply using "it", the default variable in + * the scope. But now "it" is Pair(FusedApp, ResultStatus), not an instance of FusedApp. + * + * Avoid Git diffs by using a variable named "it". + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ + val it = resultPair.first + if (applicationViewModel.appStatus.value == null) { applicationViewModel.appStatus.value = it.status } @@ -233,7 +249,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { ).show(childFragmentManager, TAG) } appTrackers.setOnClickListener { - val fusedApp = applicationViewModel.fusedApp.value + val fusedApp = applicationViewModel.fusedApp.value?.first var trackers = privacyInfoViewModel.getTrackerListText(fusedApp) @@ -270,7 +286,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { val installButton = binding.downloadInclude.installButton val downloadPB = binding.downloadInclude.progressLayout val appSize = binding.downloadInclude.appSize - val fusedApp = applicationViewModel.fusedApp.value ?: FusedApp() + val fusedApp = applicationViewModel.fusedApp.value?.first ?: FusedApp() when (status) { Status.INSTALLED -> handleInstalled( @@ -538,7 +554,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { } private fun updatePrivacyScore() { - val privacyScore = privacyInfoViewModel.getPrivacyScore(applicationViewModel.fusedApp.value) + val privacyScore = privacyInfoViewModel.getPrivacyScore(applicationViewModel.fusedApp.value?.first) if (privacyScore != -1) { val appPrivacyScore = binding.ratingsInclude.appPrivacyScore appPrivacyScore.text = getString( diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationViewModel.kt b/app/src/main/java/foundation/e/apps/application/ApplicationViewModel.kt index ea96457cb..f64c95618 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationViewModel.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationViewModel.kt @@ -33,6 +33,7 @@ import foundation.e.apps.manager.download.data.DownloadProgressLD import foundation.e.apps.manager.fused.FusedManagerRepository import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.utils.enums.Origin +import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -46,7 +47,7 @@ class ApplicationViewModel @Inject constructor( private val pkgManagerModule: PkgManagerModule ) : ViewModel() { - val fusedApp: MutableLiveData = MutableLiveData() + val fusedApp: MutableLiveData> = MutableLiveData() val appStatus: MutableLiveData = MutableLiveData() val downloadProgress = downloadProgressLD private val _errorMessageLiveData: MutableLiveData = MutableLiveData() @@ -73,7 +74,7 @@ class ApplicationViewModel @Inject constructor( fun transformPermsToString(): String { var permissionString = "" - fusedApp.value?.let { + fusedApp.value?.first?.let { // Filter list to only keep platform permissions val filteredList = it.perms.filter { it.startsWith("android.permission.") @@ -97,7 +98,7 @@ class ApplicationViewModel @Inject constructor( } suspend fun calculateProgress(progress: DownloadProgress): Pair { - fusedApp.value?.let { app -> + fusedApp.value?.first?.let { app -> val appDownload = fusedManagerRepository.getDownloadList() .singleOrNull { it.id.contentEquals(app._id) } val downloadingMap = progress.totalSizeBytes.filter { item -> @@ -114,7 +115,7 @@ class ApplicationViewModel @Inject constructor( } fun updateApplicationStatus(downloadList: List) { - fusedApp.value?.let { app -> + fusedApp.value?.first?.let { app -> val downloadingItem = downloadList.find { it.origin == app.origin && (it.packageName == app.package_name || it.id == app.package_name) } appStatus.value = -- GitLab From b534c3ef1724f79d5de2cb92df0b1a1025e84118 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sat, 14 May 2022 18:50:28 +0530 Subject: [PATCH 15/53] App lounge: (issue_5413) show timeout dialog from ApplicationFragment --- .../e/apps/application/ApplicationFragment.kt | 30 ++++++++++++++++++- app/src/main/res/values/strings.xml | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index eeca73a10..fffdb8379 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -60,6 +60,7 @@ import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User +import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface import foundation.e.apps.utils.modules.CommonUtilsModule.LIST_OF_NULL import foundation.e.apps.utils.modules.PWAManagerModule import kotlinx.coroutines.Dispatchers @@ -67,7 +68,7 @@ import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class ApplicationFragment : Fragment(R.layout.fragment_application) { +class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFragmentInterface { private val args: ApplicationFragmentArgs by navArgs() private val TAG = ApplicationFragment::class.java.simpleName @@ -140,6 +141,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { applicationViewModel.fusedApp.observe(viewLifecycleOwner) { resultPair -> if (resultPair.second != ResultStatus.OK) { + onTimeout() return@observe } @@ -154,6 +156,8 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { */ val it = resultPair.first + mainActivityViewModel.dismissTimeoutDialog() + if (applicationViewModel.appStatus.value == null) { applicationViewModel.appStatus.value = it.status } @@ -281,6 +285,25 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { } } + override fun onTimeout() { + if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + mainActivityViewModel.displayTimeoutAlertDialog( + activity = requireActivity(), + message = getString(R.string.timeout_desc_cleanapk), + positiveButtonText = getString(R.string.retry), + positiveButtonBlock = { + showLoadingProgressBar() + mainActivityViewModel.retryFetchingTokenAfterTimeout() + }, + negativeButtonText = getString(R.string.close), + negativeButtonBlock = { + requireActivity().onBackPressed() + }, + allowCancel = false, + ) + } + } + private fun observeDownloadStatus(view: View) { applicationViewModel.appStatus.observe(viewLifecycleOwner) { status -> val installButton = binding.downloadInclude.installButton @@ -553,6 +576,11 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { } } + private fun showLoadingProgressBar() { + binding.applicationLayout.visibility = View.GONE + binding.progressBar.visibility = View.VISIBLE + } + private fun updatePrivacyScore() { val privacyScore = privacyInfoViewModel.getPrivacyScore(applicationViewModel.fusedApp.value?.first) if (privacyScore != -1) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7e0eb34c5..ae75f6ddf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -161,6 +161,7 @@ Some network issue is preventing fetching all applications. Open Settings + Close %1$s]]> -- GitLab From 6bf313d5399717bd0b3665b5e42ca7a733db37b1 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sat, 14 May 2022 18:51:37 +0530 Subject: [PATCH 16/53] App lounge: (issue_5413) better logging --- app/src/main/java/foundation/e/apps/MainActivity.kt | 3 ++- .../java/foundation/e/apps/categories/CategoriesFragment.kt | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index b29c87da3..8de3a6b44 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -143,7 +143,8 @@ class MainActivity : AppCompatActivity() { Log.d(TAG, "Timeout validating auth data!") val lastFragment = navHostFragment.childFragmentManager.fragments[0] if (lastFragment is TimeoutFragmentInterface) { - Log.d(TAG, "Displaying timeout from MainActivity!") + Log.d(TAG, "Displaying timeout from MainActivity on fragment: " + + lastFragment::class.java.name) lastFragment.onTimeout() } } diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt index 369ab70ed..1ad0ec11d 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt @@ -19,6 +19,7 @@ package foundation.e.apps.categories import android.os.Bundle +import android.util.Log import android.view.View import androidx.fragment.app.Fragment import com.google.android.material.tabs.TabLayoutMediator @@ -57,7 +58,10 @@ class CategoriesFragment : Fragment(R.layout.fragment_categories), TimeoutFragme override fun onTimeout() { childFragmentManager.fragments[0]?.let { - if (it is TimeoutFragmentInterface) it.onTimeout() + if (it is TimeoutFragmentInterface) { + Log.d(TAG, "Showing timeout on Categories fragment: " + it::class.java.name) + it.onTimeout() + } } } } -- GitLab From 7752a9adee963fb0e5b442a83df0c4693e657a3a Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sun, 15 May 2022 01:20:16 +0530 Subject: [PATCH 17/53] App lounge: (issue_5413) use AlertDialog.Builder for timeout dialog --- .../e/apps/MainActivityViewModel.kt | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index 13aa86205..7bca05c7d 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -47,7 +47,6 @@ import foundation.e.apps.manager.database.fusedDownload.FusedDownload import foundation.e.apps.manager.fused.FusedManagerRepository import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.manager.workmanager.InstallWorkManager -import foundation.e.apps.settings.SettingsFragment import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type @@ -95,7 +94,7 @@ class MainActivityViewModel @Inject constructor( * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ - private lateinit var timeoutAlertDialog: AlertDialog + private var timeoutAlertDialog: AlertDialog? = null /** * Display timeout alert dialog. @@ -123,13 +122,13 @@ class MainActivityViewModel @Inject constructor( neutralButtonBlock: (() -> Unit)? = null, allowCancel: Boolean = true, ) { - if (!this::timeoutAlertDialog.isInitialized) { - timeoutAlertDialog = AlertDialog.Builder(activity).apply { - setTitle(R.string.timeout_title) - }.create() - } + val timeoutAlertDialogBuilder = AlertDialog.Builder(activity).apply { + + /* + * Set title. + */ + setTitle(R.string.timeout_title) - timeoutAlertDialog.apply { if (!allowCancel) { /* * Prevent dismissing the dialog from pressing outside as it will only @@ -157,30 +156,34 @@ class MainActivityViewModel @Inject constructor( setMessage(message) /* - * Set buttons. The code may seem long and repetitive, but this works, - * most other approaches do not update the button texts. + * Set buttons. */ positiveButtonText?.let { - setButton(DialogInterface.BUTTON_POSITIVE, it) { _, _ -> + setPositiveButton(it) {_, _ -> positiveButtonBlock?.invoke() } - getButton(DialogInterface.BUTTON_POSITIVE)?.text = it } negativeButtonText?.let { - setButton(DialogInterface.BUTTON_NEGATIVE, it) { _, _ -> + setNegativeButton(it) {_, _ -> negativeButtonBlock?.invoke() } - getButton(DialogInterface.BUTTON_NEGATIVE)?.text = it } neutralButtonText?.let { - setButton(DialogInterface.BUTTON_NEUTRAL, it) { _, _ -> + setNeutralButton(it) {_, _ -> neutralButtonBlock?.invoke() } - getButton(DialogInterface.BUTTON_NEUTRAL)?.text = it } } - timeoutAlertDialog.show() + /* + * Dismiss alert dialog if already being shown + */ + try { + timeoutAlertDialog?.dismiss() + } catch (_: Exception) {} + + timeoutAlertDialog = timeoutAlertDialogBuilder.create() + timeoutAlertDialog?.show() } /** @@ -188,9 +191,7 @@ class MainActivityViewModel @Inject constructor( * Returs false if it is not initialised. */ fun isTimeoutDialogDisplayed(): Boolean { - return if (this::timeoutAlertDialog.isInitialized) { - timeoutAlertDialog.isShowing - } else false + return timeoutAlertDialog?.isShowing == true } /** @@ -201,7 +202,7 @@ class MainActivityViewModel @Inject constructor( fun dismissTimeoutDialog() { if (isTimeoutDialogDisplayed()) { try { - timeoutAlertDialog.dismiss() + timeoutAlertDialog?.dismiss() } catch (_: Exception) {} } } -- GitLab From 929309f6b3f2d6ee5192a204b303d37c20e748da Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Sun, 15 May 2022 01:31:55 +0530 Subject: [PATCH 18/53] App lounge: (issue_5413) Categories - try refreshing authData if blank. This will also trigger timeout if not yet done on the fragment. --- .../main/java/foundation/e/apps/categories/AppsFragment.kt | 6 ++++++ .../main/java/foundation/e/apps/categories/GamesFragment.kt | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index 581314981..1f5f1149b 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -53,6 +53,12 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface authData ) } + } ?: run { + /* + * Try refreshing authData if null. + * This will also trigger timeout if not yet done on this fragment. + */ + mainActivityViewModel.retryFetchingTokenAfterTimeout() } val categoriesRVAdapter = CategoriesRVAdapter() diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 44b1e9daa..324970de3 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -53,6 +53,12 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac authData ) } + } ?: run { + /* + * Try refreshing authData if null. + * This will also trigger timeout if not yet done on this fragment. + */ + mainActivityViewModel.retryFetchingTokenAfterTimeout() } } -- GitLab From baa9677e36c5ca298a8afa2027350de3a306352a Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 01:18:26 +0530 Subject: [PATCH 19/53] App lounge: (issue_5413) Home fragment - refreshHomeData() -> refreshDataOrRefreshToken() : refresh authData if null --- app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index ade1e3614..b09691399 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -82,7 +82,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { mainActivityViewModel.authData.observe(viewLifecycleOwner) { - refreshHomeData() + refreshDataOrRefreshToken() } } @@ -153,11 +153,13 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou /* * Offload loading home data to a different function, to allow retrying mechanism. */ - private fun refreshHomeData() { + private fun refreshDataOrRefreshToken() { if (mainActivityViewModel.internetConnection.value == true) { mainActivityViewModel.authData.value?.let { authData -> mainActivityViewModel.dismissTimeoutDialog() homeViewModel.getHomeScreenData(authData) + } ?: run { + mainActivityViewModel.retryFetchingTokenAfterTimeout() } } } -- GitLab From 1e639907c15db4ef5d96905ad8f67a0f50d35b01 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 01:26:38 +0530 Subject: [PATCH 20/53] App lounge: (issue_5413) Home fragment - set double observers for refreshDataOrRefreshToken() --- .../foundation/e/apps/home/HomeFragment.kt | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index b09691399..21c87261b 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -80,10 +80,49 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou } } + /* + * Previous code: + * internetConnection.observe { + * authData.observe { + * // refresh data here. + * } + * } + * + * Code regarding data fetch is placed in two separate observers compared to nested + * observers as was done previously. + * + * refreshDataOrRefreshToken() already checks for internet connectivity and authData. + * If authData is null, it requests to fetch new token data. + * + * With previous nested observer code (commit 8ca1647d), try the following: + * 1. Put garbage value in "Proxy" of APN settings of device, + * this will cause host unreachable error. + * 2. Open App Lounge. Let it show timeout dialog. + * 3. Click "Open Settings", now immediately open Home tab again. + * 4. Home keeps loading without any timeout error. + * + * Why is this happening? + * In case of host unreachable error, the authData is itself blank/null. This does not allow + * it to get "observed". But mainActivityViewModel.internetConnection always has a value, + * and is observable. + * When we open Home tab again from Settings tab, no refresh action is performed as + * authData.observe {} does not observe anything. + * + * In the new code, the first observer will always be executed on fragment attach + * (as mainActivityViewModel.internetConnection always has a value and is observable), + * this will call refreshDataOrRefreshToken(), which will refresh authData if it is null. + * Now with new valid authData, the second observer (authData.observe{}) will again call + * refreshDataOrRefreshToken() which will now fetch correct data. + * + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ + mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { - mainActivityViewModel.authData.observe(viewLifecycleOwner) { - refreshDataOrRefreshToken() - } + refreshDataOrRefreshToken() + } + mainActivityViewModel.authData.observe(viewLifecycleOwner) { + refreshDataOrRefreshToken() } val homeParentRVAdapter = HomeParentRVAdapter( -- GitLab From 0c0e10110b298bfe52337d18515fc8e3652f21c6 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 01:52:37 +0530 Subject: [PATCH 21/53] App lounge: (issue_5413) move refreshDataOrRefreshToken() implementation to TimeoutFragmentInterface --- .../foundation/e/apps/home/HomeFragment.kt | 19 ++++---------- .../interfaces/TimeoutFragmentInterface.kt | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index 21c87261b..601142256 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -28,6 +28,7 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.AppInfoFetchViewModel import foundation.e.apps.AppProgressViewModel @@ -119,10 +120,10 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou */ mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { - refreshDataOrRefreshToken() + refreshDataOrRefreshToken(mainActivityViewModel) } mainActivityViewModel.authData.observe(viewLifecycleOwner) { - refreshDataOrRefreshToken() + refreshDataOrRefreshToken(mainActivityViewModel) } val homeParentRVAdapter = HomeParentRVAdapter( @@ -189,18 +190,8 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou } } - /* - * Offload loading home data to a different function, to allow retrying mechanism. - */ - private fun refreshDataOrRefreshToken() { - if (mainActivityViewModel.internetConnection.value == true) { - mainActivityViewModel.authData.value?.let { authData -> - mainActivityViewModel.dismissTimeoutDialog() - homeViewModel.getHomeScreenData(authData) - } ?: run { - mainActivityViewModel.retryFetchingTokenAfterTimeout() - } - } + override fun refreshData(authData: AuthData) { + homeViewModel.getHomeScreenData(authData) } private fun showLoadingShimmer() { diff --git a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt index e69163e24..fd4aef2e5 100644 --- a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt +++ b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt @@ -1,5 +1,31 @@ package foundation.e.apps.utils.interfaces +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.MainActivityViewModel + interface TimeoutFragmentInterface { fun onTimeout() + + /* + * Recommended to put code to refresh data inside this block. + * But call refreshDataOrRefreshToken() to execute the refresh. + */ + fun refreshData(authData: AuthData) {} + + /* + * Checks if network connectivity is present. + * -- If yes, then checks if valid authData is present. + * ---- If yes, then dismiss timeout dialog (if showing) and call refreshData() + * ---- If no, then request new token data. + */ + fun refreshDataOrRefreshToken(mainActivityViewModel: MainActivityViewModel) { + if (mainActivityViewModel.internetConnection.value == true) { + mainActivityViewModel.authData.value?.let { authData -> + mainActivityViewModel.dismissTimeoutDialog() + refreshData(authData) + } ?: run { + mainActivityViewModel.retryFetchingTokenAfterTimeout() + } + } + } } \ No newline at end of file -- GitLab From 9d6d2e49c8b8c6a046993d19bdac15c4c3b2b502 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 02:04:50 +0530 Subject: [PATCH 22/53] App lounge: (issue_5413) Use new refreshDataOrRefreshToken() in AppsFragment and GamesFragment --- .../e/apps/categories/AppsFragment.kt | 65 ++++++++++--------- .../e/apps/categories/GamesFragment.kt | 32 ++++----- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index 1f5f1149b..1b5578798 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -24,6 +24,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager +import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.Category import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.MainActivityViewModel @@ -45,38 +46,37 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface super.onViewCreated(view, savedInstanceState) _binding = FragmentAppsBinding.bind(view) - mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { hasInternet -> - mainActivityViewModel.authData.value?.let { authData -> - if (hasInternet) { - categoriesViewModel.getCategoriesList( - Category.Type.APPLICATION, - authData - ) - } - } ?: run { - /* - * Try refreshing authData if null. - * This will also trigger timeout if not yet done on this fragment. - */ - mainActivityViewModel.retryFetchingTokenAfterTimeout() - } + /* + * Explanation of double observers in HomeFragment.kt + */ - val categoriesRVAdapter = CategoriesRVAdapter() - val recyclerView = binding.recyclerView + mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { + refreshDataOrRefreshToken(mainActivityViewModel) + } + mainActivityViewModel.authData.observe(viewLifecycleOwner) { + refreshDataOrRefreshToken(mainActivityViewModel) + } - recyclerView.apply { - adapter = categoriesRVAdapter - layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) - visibility = View.GONE - } + /* + * Code regarding is just moved outside the observers. + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ + + val categoriesRVAdapter = CategoriesRVAdapter() + val recyclerView = binding.recyclerView - categoriesViewModel.categoriesList.observe(viewLifecycleOwner) { - categoriesRVAdapter.setData(it.first) - binding.shimmerLayout.visibility = View.GONE - recyclerView.visibility = View.VISIBLE - if (it.third != ResultStatus.OK) { - onTimeout() - } + recyclerView.apply { + adapter = categoriesRVAdapter + layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + visibility = View.GONE + } + + categoriesViewModel.categoriesList.observe(viewLifecycleOwner) { + categoriesRVAdapter.setData(it.first) + binding.shimmerLayout.visibility = View.GONE + recyclerView.visibility = View.VISIBLE + if (it.third != ResultStatus.OK) { + onTimeout() } } } @@ -105,6 +105,13 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface } } + override fun refreshData(authData: AuthData) { + categoriesViewModel.getCategoriesList( + Category.Type.APPLICATION, + authData + ) + } + private fun showLoadingShimmer() { binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 324970de3..5b2805d51 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -24,6 +24,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager +import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.Category import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.MainActivityViewModel @@ -45,21 +46,15 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac super.onViewCreated(view, savedInstanceState) _binding = FragmentGamesBinding.bind(view) - mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { hasInternet -> - mainActivityViewModel.authData.value?.let { authData -> - if (hasInternet) { - categoriesViewModel.getCategoriesList( - Category.Type.GAME, - authData - ) - } - } ?: run { - /* - * Try refreshing authData if null. - * This will also trigger timeout if not yet done on this fragment. - */ - mainActivityViewModel.retryFetchingTokenAfterTimeout() - } + /* + * Explanation of double observers in HomeFragment.kt + */ + + mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { + refreshDataOrRefreshToken(mainActivityViewModel) + } + mainActivityViewModel.authData.observe(viewLifecycleOwner) { + refreshDataOrRefreshToken(mainActivityViewModel) } val categoriesRVAdapter = CategoriesRVAdapter() @@ -105,6 +100,13 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac } } + override fun refreshData(authData: AuthData) { + categoriesViewModel.getCategoriesList( + Category.Type.GAME, + authData + ) + } + private fun showLoadingShimmer() { binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE -- GitLab From 62e1430f0a27090942ec7a9f87e2e07035cd721e Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 02:16:52 +0530 Subject: [PATCH 23/53] App lounge: (issue_5413) Use new refreshDataOrRefreshToken() in ApplicationFragment --- .../e/apps/application/ApplicationFragment.kt | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index fffdb8379..103408b04 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -40,6 +40,7 @@ import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import coil.load +import com.aurora.gplayapi.data.models.AuthData import com.google.android.material.button.MaterialButton import com.google.android.material.snackbar.Snackbar import com.google.android.material.textview.MaterialTextView @@ -100,17 +101,15 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag super.onViewCreated(view, savedInstanceState) _binding = FragmentApplicationBinding.bind(view) - mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { hasInternet -> - mainActivityViewModel.authData.observe(viewLifecycleOwner) { authData -> - if (hasInternet) { - applicationViewModel.getApplicationDetails( - args.id, - args.packageName, - authData, - args.origin - ) - } - } + /* + * Explanation of double observers in HomeFragment.kt + */ + + mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { + refreshDataOrRefreshToken(mainActivityViewModel) + } + mainActivityViewModel.authData.observe(viewLifecycleOwner) { + refreshDataOrRefreshToken(mainActivityViewModel) } val startDestination = findNavController().graph.startDestination @@ -304,6 +303,15 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag } } + override fun refreshData(authData: AuthData) { + applicationViewModel.getApplicationDetails( + args.id, + args.packageName, + authData, + args.origin + ) + } + private fun observeDownloadStatus(view: View) { applicationViewModel.appStatus.observe(viewLifecycleOwner) { status -> val installButton = binding.downloadInclude.installButton -- GitLab From 694bb91f2308d304cd5124c42f996f216ccc4186 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 04:13:01 +0530 Subject: [PATCH 24/53] App lounge: (issue_5413) store ResultStatus in ApplicationListViewModel.appListLiveData. Run the respective FusedAPIImpl methods with timeout. Other changes for the changed data type. --- .../e/apps/api/fused/FusedAPIImpl.kt | 145 +++++++++++------- .../e/apps/api/fused/FusedAPIRepository.kt | 10 +- .../ApplicationListFragment.kt | 10 +- .../ApplicationListViewModel.kt | 32 ++-- .../updates/manager/UpdatesManagerImpl.kt | 4 +- 5 files changed, 123 insertions(+), 78 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 32640c623..f3971efb8 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -293,28 +293,40 @@ class FusedAPIImpl @Inject constructor( } } - suspend fun getPWAApps(category: String): List? { - val response = getPWAAppsResponse(category) - response?.apps?.forEach { - it.updateStatus() - it.updateType() - } - return response?.apps + suspend fun getPWAApps(category: String): Pair, ResultStatus> { + var list = mutableListOf() + val status = runCodeBlockWithTimeout({ + val response = getPWAAppsResponse(category) + response?.apps?.forEach { + it.updateStatus() + it.updateType() + list.add(it) + } + }) + return Pair(list, status) } - suspend fun getOpenSourceApps(category: String): List? { - val response = getOpenSourceAppsResponse(category) - response?.apps?.forEach { - it.updateStatus() - it.updateType() - } - return response?.apps + suspend fun getOpenSourceApps(category: String): Pair, ResultStatus> { + val list = mutableListOf() + val status = runCodeBlockWithTimeout({ + val response = getOpenSourceAppsResponse(category) + response?.apps?.forEach { + it.updateStatus() + it.updateType() + list.add(it) + } + }) + return Pair(list, status) } - suspend fun getPlayStoreApps(browseUrl: String, authData: AuthData): List { - return gPlayAPIRepository.listApps(browseUrl, authData).map { app -> - app.transformToFusedApp() - } + suspend fun getPlayStoreApps(browseUrl: String, authData: AuthData): Pair, ResultStatus> { + var list = mutableListOf() + val status = runCodeBlockWithTimeout({ + list.addAll(gPlayAPIRepository.listApps(browseUrl, authData).map { app -> + app.transformToFusedApp() + }) + }) + return Pair(list, status) } suspend fun getPlayStoreAppCategoryUrls(browseUrl: String, authData: AuthData): List { @@ -324,58 +336,75 @@ class FusedAPIImpl @Inject constructor( suspend fun getAppsAndNextClusterUrl( browseUrl: String, authData: AuthData - ): Pair, String> { - return gPlayAPIRepository.getAppsAndNextClusterUrl(browseUrl, authData).let { - Pair(it.first.map { app -> app.transformToFusedApp() }, it.second) - } + ): Triple, String, ResultStatus> { + val appsList = mutableListOf() + var nextUrl = "" + val status = runCodeBlockWithTimeout({ + val gPlayResult = gPlayAPIRepository.getAppsAndNextClusterUrl(browseUrl, authData) + appsList.addAll(gPlayResult.first.map { app -> app.transformToFusedApp() }) + nextUrl = gPlayResult.second + }) + + return Triple(appsList, nextUrl, status) } suspend fun getApplicationDetails( packageNameList: List, authData: AuthData, origin: Origin - ): List { + ): Pair, ResultStatus> { val list = mutableListOf() - val response = if (origin == Origin.CLEANAPK) { - val pkgList = mutableListOf() - packageNameList.forEach { - val result = cleanAPKRepository.searchApps( - keyword = it, - by = "package_name" - ).body() - if (result?.apps?.isNotEmpty() == true && result.numberOfResults == 1) { - pkgList.add(result.apps[0]) + + val status = runCodeBlockWithTimeout({ + + /* + * Code is just moved inside timeout block. + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ + + val response = if (origin == Origin.CLEANAPK) { + val pkgList = mutableListOf() + packageNameList.forEach { + val result = cleanAPKRepository.searchApps( + keyword = it, + by = "package_name" + ).body() + if (result?.apps?.isNotEmpty() == true && result.numberOfResults == 1) { + pkgList.add(result.apps[0]) + } } - } - pkgList - } else { - gPlayAPIRepository.getAppDetails(packageNameList, authData).map { app -> - /* - * Some apps are restricted to locations. Example "com.skype.m2". - * For restricted apps, check if it is possible to get their specific app info. - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5174 - */ - if (app.restriction != Constants.Restriction.NOT_RESTRICTED) { - try { - gPlayAPIRepository.getAppDetails(app.packageName, authData) - ?.transformToFusedApp() ?: FusedApp() - } catch (e: Exception) { - FusedApp() + pkgList + } else { + gPlayAPIRepository.getAppDetails(packageNameList, authData).map { app -> + /* + * Some apps are restricted to locations. Example "com.skype.m2". + * For restricted apps, check if it is possible to get their specific app info. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5174 + */ + if (app.restriction != Constants.Restriction.NOT_RESTRICTED) { + try { + gPlayAPIRepository.getAppDetails(app.packageName, authData) + ?.transformToFusedApp() ?: FusedApp() + } catch (e: Exception) { + FusedApp() + } + } else { + app.transformToFusedApp() } - } else { - app.transformToFusedApp() } } - } - response.forEach { - if (it.package_name.isNotBlank()) { - it.updateStatus() - it.updateType() - list.add(it) + response.forEach { + if (it.package_name.isNotBlank()) { + it.updateStatus() + it.updateType() + list.add(it) + } } - } - return list + + }) + + return Pair(list, status) } suspend fun getApplicationDetails( diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 8b58edb85..62195764a 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -55,7 +55,7 @@ class FusedAPIRepository @Inject constructor( packageNameList: List, authData: AuthData, origin: Origin - ): List { + ): Pair, ResultStatus> { return fusedAPIImpl.getApplicationDetails(packageNameList, authData, origin) } @@ -108,7 +108,7 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.getPlayStoreAppCategoryUrls(browseUrl, authData) } - suspend fun getAppsAndNextClusterUrl(browseUrl: String, authData: AuthData): Pair, String> { + suspend fun getAppsAndNextClusterUrl(browseUrl: String, authData: AuthData): Triple, String, ResultStatus> { return fusedAPIImpl.getAppsAndNextClusterUrl(browseUrl, authData) } @@ -117,10 +117,10 @@ class FusedAPIRepository @Inject constructor( browseUrl: String, authData: AuthData, source: String - ): List { + ): Pair, ResultStatus> { return when (source) { - "Open Source" -> fusedAPIImpl.getOpenSourceApps(category) ?: listOf() - "PWA" -> fusedAPIImpl.getPWAApps(category) ?: listOf() + "Open Source" -> fusedAPIImpl.getOpenSourceApps(category) + "PWA" -> fusedAPIImpl.getPWAApps(category) else -> fusedAPIImpl.getPlayStoreApps(browseUrl, authData) } } diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index e0b206aa5..e91c2caf1 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -88,11 +88,13 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu private fun observeDownloadList() { mainActivityViewModel.downloadList.observe(viewLifecycleOwner) { list -> - val appList = viewModel.appListLiveData.value?.toMutableList() - appList?.let { + val appList = viewModel.appListLiveData.value?.first?.toMutableList() ?: emptyList() + appList.let { mainActivityViewModel.updateStatusOfFusedApps(it, list) } - viewModel.appListLiveData.value = appList + viewModel.appListLiveData.apply { + value = Pair(appList, value?.second) + } } } @@ -148,7 +150,7 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu } viewModel.appListLiveData.observe(viewLifecycleOwner) { - listAdapter?.setData(it) + listAdapter?.setData(it.first) if (!isDownloadObserverAdded) { observeDownloadList() isDownloadObserverAdded = true diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index cd710a731..72a518e18 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -26,6 +26,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.utils.enums.Origin +import foundation.e.apps.utils.enums.ResultStatus import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @@ -35,7 +36,7 @@ class ApplicationListViewModel @Inject constructor( private val fusedAPIRepository: FusedAPIRepository ) : ViewModel() { - val appListLiveData: MutableLiveData> = MutableLiveData() + val appListLiveData: MutableLiveData, ResultStatus?>> = MutableLiveData() private var lastBrowseUrl = String() @@ -66,7 +67,7 @@ class ApplicationListViewModel @Inject constructor( * Add existing apps now and add additional apps later. */ val newList = mutableListOf().apply { - appListLiveData.value?.let { addAll(it) } + appListLiveData.value?.first?.let { addAll(it) } } /** @@ -124,7 +125,7 @@ class ApplicationListViewModel @Inject constructor( fusedAPIRepository.getAppsAndNextClusterUrl(nextClusterUrl, authData).run { val existingPackageNames = newList.map { it.package_name } newList.addAll(first.filter { it.package_name !in existingPackageNames }) - appListLiveData.postValue(newList) + appListLiveData.postValue(Pair(newList, third)) nextClusterUrl = second // set the next "clusterNextPageUrl" } } @@ -132,27 +133,40 @@ class ApplicationListViewModel @Inject constructor( } fun getList(category: String, browseUrl: String, authData: AuthData, source: String) { - if (appListLiveData.value?.isNotEmpty() == true) { + if (appListLiveData.value?.first?.isNotEmpty() == true) { return } viewModelScope.launch(Dispatchers.IO) { - val packageNames = fusedAPIRepository.getAppsListBasedOnCategory( + val appsListData = fusedAPIRepository.getAppsListBasedOnCategory( category, browseUrl, authData, source - ).map { it.package_name } + ) - val applicationDetails = if (!source.contentEquals("PWA")) { + if (appsListData.second != ResultStatus.OK) { + appListLiveData.postValue(Pair(listOf(), appsListData.second)) + return@launch + } + + val applicationDetailsWithStatus = if (!source.contentEquals("PWA")) { + /* + * Optimization: packageNames were not used anywhere else, + * hence moved here. + */ + val packageNames = appsListData.first.map { it.package_name } fusedAPIRepository.getApplicationDetails( packageNames, authData, getOrigin(source) ) } else { - fusedAPIRepository.getAppsListBasedOnCategory(category, browseUrl, authData, source) + /* + * Optimization: Old code was same as the one called above. + */ + appsListData } - appListLiveData.postValue(applicationDetails) + appListLiveData.postValue(applicationDetailsWithStatus) } } diff --git a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt index 47443ad08..d09ba24f1 100644 --- a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt +++ b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt @@ -47,7 +47,7 @@ class UpdatesManagerImpl @Inject constructor( authData, Origin.CLEANAPK ) - cleanAPKList.forEach { + cleanAPKList.first.forEach { if (it.package_name in pkgList) pkgList.remove(it.package_name) if (it.status == Status.UPDATABLE) updateList.add(it) } @@ -58,7 +58,7 @@ class UpdatesManagerImpl @Inject constructor( authData, Origin.GPLAY ) - gPlayList.forEach { + gPlayList.first.forEach { if (it.status == Status.UPDATABLE) updateList.add(it) } } -- GitLab From d75bd6d4826960a4c2b0c96ecf5a6b819a10fb30 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 04:35:49 +0530 Subject: [PATCH 25/53] App lounge: (issue_5413) "OK" always visible in timeout dialog for AppsFragment, GamesFragment --- .../java/foundation/e/apps/categories/AppsFragment.kt | 9 +-------- .../java/foundation/e/apps/categories/GamesFragment.kt | 9 +-------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index 1b5578798..f1257cf73 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -86,14 +86,7 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface mainActivityViewModel.displayTimeoutAlertDialog( activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), - positiveButtonText = if (!categoriesViewModel.isCategoriesEmpty()) { - /* - * It may happen that both GPlay and cleanapk is unreachable. - * In that case of user presses OK to dismiss the dialog, - * only blank screen will be shown. - */ - getString(android.R.string.ok) - } else null, + positiveButtonText = getString(android.R.string.ok), positiveButtonBlock = {}, negativeButtonText = getString(R.string.retry), negativeButtonBlock = { diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 5b2805d51..a1f72e1b8 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -81,14 +81,7 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac mainActivityViewModel.displayTimeoutAlertDialog( activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), - positiveButtonText = if (!categoriesViewModel.isCategoriesEmpty()) { - /* - * It may happen that both GPlay and cleanapk is unreachable. - * In that case of user presses OK to dismiss the dialog, - * only blank screen will be shown. - */ - getString(android.R.string.ok) - } else null, + positiveButtonText = getString(android.R.string.ok), positiveButtonBlock = {}, negativeButtonText = getString(R.string.retry), negativeButtonBlock = { -- GitLab From 46f8603029c8fb500c88e611fdcfb369d8648a8a Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 05:13:05 +0530 Subject: [PATCH 26/53] App lounge: (issue_5413) Use new refreshDataOrRefreshToken() in ApplicationListFragment --- .../ApplicationListFragment.kt | 88 ++++++++++++++----- 1 file changed, 64 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index e91c2caf1..678411b45 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -30,6 +30,7 @@ import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.AppInfoFetchViewModel import foundation.e.apps.AppProgressViewModel @@ -43,14 +44,17 @@ import foundation.e.apps.applicationlist.model.ApplicationListRVAdapter import foundation.e.apps.databinding.FragmentApplicationListBinding import foundation.e.apps.manager.download.data.DownloadProgress import foundation.e.apps.manager.pkg.PkgManagerModule +import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User +import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface import foundation.e.apps.utils.modules.PWAManagerModule import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class ApplicationListFragment : Fragment(R.layout.fragment_application_list), FusedAPIInterface { +class ApplicationListFragment : Fragment(R.layout.fragment_application_list), FusedAPIInterface, + TimeoutFragmentInterface { private val args: ApplicationListFragmentArgs by navArgs() @@ -157,37 +161,73 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu } binding.shimmerLayout.visibility = View.GONE recyclerView.visibility = View.VISIBLE + if (it.second != ResultStatus.OK) { + onTimeout() + } } - mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { isInternetConnection -> - mainActivityViewModel.authData.value?.let { authData -> - if (isInternetConnection) { - viewModel.getList( - args.category, - args.browseUrl, - authData, - args.source - ) + /* + * Explanation of double observers in HomeFragment.kt + */ + + mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { + refreshDataOrRefreshToken(mainActivityViewModel) + } + mainActivityViewModel.authData.observe(viewLifecycleOwner) { + refreshDataOrRefreshToken(mainActivityViewModel) + } + } + + override fun onTimeout() { + if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + mainActivityViewModel.displayTimeoutAlertDialog( + activity = requireActivity(), + message = getString(R.string.timeout_desc_cleanapk), + positiveButtonText = getString(R.string.retry), + positiveButtonBlock = { + showLoadingShimmer() + mainActivityViewModel.retryFetchingTokenAfterTimeout() + }, + negativeButtonText = getString(android.R.string.ok), + negativeButtonBlock = {}, + allowCancel = true, + ) + } + } + + override fun refreshData(authData: AuthData) { + /* + * Code moved from onResume() + */ + + viewModel.getList( + args.category, + args.browseUrl, + authData, + args.source + ) - if (args.source != "Open Source" && args.source != "PWA") { - /* - * For Play store apps we try to load more apps on reaching end of list. - * Source: https://stackoverflow.com/a/46342525 - */ - recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - super.onScrollStateChanged(recyclerView, newState) - if (!recyclerView.canScrollVertically(1)) { - viewModel.getPlayStoreAppsOnScroll(args.browseUrl, authData) - } - } - }) + if (args.source != "Open Source" && args.source != "PWA") { + /* + * For Play store apps we try to load more apps on reaching end of list. + * Source: https://stackoverflow.com/a/46342525 + */ + binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (!recyclerView.canScrollVertically(1)) { + viewModel.getPlayStoreAppsOnScroll(args.browseUrl, authData) } } - } + }) } } + private fun showLoadingShimmer() { + binding.shimmerLayout.visibility = View.VISIBLE + binding.recyclerView.visibility = View.GONE + } + private fun updateProgressOfDownloadingItems( recyclerView: RecyclerView, it: DownloadProgress -- GitLab From 30bdc4b9818e0972c7c58d0aae67a17fcc1f9d1e Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 05:28:38 +0530 Subject: [PATCH 27/53] App lounge: (issue_5413) Add ResultStatus in SearchViewModel.searchResult. Load FusedAPIImpl.getSearchResults() in timeout. --- .../e/apps/api/fused/FusedAPIImpl.kt | 38 ++++++++++--------- .../e/apps/api/fused/FusedAPIRepository.kt | 2 +- .../e/apps/search/SearchFragment.kt | 12 +++--- .../e/apps/search/SearchViewModel.kt | 3 +- 4 files changed, 30 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index f3971efb8..6f53a2c64 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -204,28 +204,30 @@ class FusedAPIImpl @Inject constructor( * @param authData [AuthData] * @return A list of nullable [FusedApp] */ - suspend fun getSearchResults(query: String, authData: AuthData): List { + suspend fun getSearchResults(query: String, authData: AuthData): Pair, ResultStatus> { val fusedResponse = mutableListOf() - when (preferenceManagerModule.preferredApplicationType()) { - APP_TYPE_ANY -> { - fusedResponse.addAll(getCleanAPKSearchResults(query)) - fusedResponse.addAll(getGplaySearchResults(query, authData)) - } - APP_TYPE_OPEN -> { - fusedResponse.addAll(getCleanAPKSearchResults(query)) - } - APP_TYPE_PWA -> { - fusedResponse.addAll( - getCleanAPKSearchResults( - query, - CleanAPKInterface.APP_SOURCE_ANY, - CleanAPKInterface.APP_TYPE_PWA + val status = runCodeBlockWithTimeout({ + when (preferenceManagerModule.preferredApplicationType()) { + APP_TYPE_ANY -> { + fusedResponse.addAll(getCleanAPKSearchResults(query)) + fusedResponse.addAll(getGplaySearchResults(query, authData)) + } + APP_TYPE_OPEN -> { + fusedResponse.addAll(getCleanAPKSearchResults(query)) + } + APP_TYPE_PWA -> { + fusedResponse.addAll( + getCleanAPKSearchResults( + query, + CleanAPKInterface.APP_SOURCE_ANY, + CleanAPKInterface.APP_TYPE_PWA + ) ) - ) + } } - } - return fusedResponse.distinctBy { it.package_name } + }) + return Pair(fusedResponse.distinctBy { it.package_name }, status) } suspend fun getSearchSuggestions(query: String, authData: AuthData): List { diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 62195764a..f92123cc0 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -96,7 +96,7 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.fetchAuthData(email, aasToken) } - suspend fun getSearchResults(query: String, authData: AuthData): List { + suspend fun getSearchResults(query: String, authData: AuthData): Pair, ResultStatus> { return fusedAPIImpl.getSearchResults(query, authData) } diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index 3791b0db0..64ef7d4ce 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -172,18 +172,20 @@ class SearchFragment : } mainActivityViewModel.downloadList.observe(viewLifecycleOwner) { list -> - val searchList = searchViewModel.searchResult.value?.toMutableList() - searchList?.let { + val searchList = searchViewModel.searchResult.value?.first?.toMutableList() ?: emptyList() + searchList.let { mainActivityViewModel.updateStatusOfFusedApps(searchList, list) } - searchViewModel.searchResult.value = searchList + searchViewModel.searchResult.apply { + value = Pair(searchList, value?.second) + } } searchViewModel.searchResult.observe(viewLifecycleOwner) { - if (it.isNullOrEmpty()) { + if (it.first.isNullOrEmpty()) { noAppsFoundLayout?.visibility = View.VISIBLE } else { - listAdapter?.setData(it) + listAdapter?.setData(it.first) shimmerLayout?.visibility = View.GONE recyclerView?.visibility = View.VISIBLE noAppsFoundLayout?.visibility = View.GONE diff --git a/app/src/main/java/foundation/e/apps/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/search/SearchViewModel.kt index 8749977ad..76a821549 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchViewModel.kt @@ -26,6 +26,7 @@ import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp +import foundation.e.apps.utils.enums.ResultStatus import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @@ -36,7 +37,7 @@ class SearchViewModel @Inject constructor( ) : ViewModel() { val searchSuggest: MutableLiveData?> = MutableLiveData() - val searchResult: MutableLiveData> = MutableLiveData() + val searchResult: MutableLiveData, ResultStatus?>> = MutableLiveData() fun getSearchSuggestions(query: String, authData: AuthData) { viewModelScope.launch(Dispatchers.IO) { -- GitLab From 5f041bf4d5685a467913480651536ae546d1682d Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 06:27:53 +0530 Subject: [PATCH 28/53] App lounge: (issue_5413) Use new refreshDataOrRefreshToken() in SearchFragment --- .../e/apps/search/SearchFragment.kt | 68 ++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index 64ef7d4ce..c6bd5445c 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -37,6 +37,7 @@ 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 @@ -50,8 +51,10 @@ import foundation.e.apps.application.subFrags.ApplicationDialogFragment import foundation.e.apps.applicationlist.model.ApplicationListRVAdapter import foundation.e.apps.databinding.FragmentSearchBinding import foundation.e.apps.manager.pkg.PkgManagerModule +import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User +import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface import foundation.e.apps.utils.modules.PWAManagerModule import kotlinx.coroutines.launch import javax.inject.Inject @@ -61,7 +64,8 @@ class SearchFragment : Fragment(R.layout.fragment_search), SearchView.OnQueryTextListener, SearchView.OnSuggestionListener, - FusedAPIInterface { + FusedAPIInterface, + TimeoutFragmentInterface { @Inject lateinit var pkgManagerModule: PkgManagerModule @@ -86,6 +90,12 @@ class SearchFragment : private var searchHintLayout: LinearLayout? = null private var noAppsFoundLayout: LinearLayout? = null + /* + * Store the string from onQueryTextSubmit() and access it from refreshData() + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ + private var searchText = "" + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentSearchBinding.bind(view) @@ -181,6 +191,23 @@ class SearchFragment : } } + + /* + * Explanation of double observers in HomeFragment.kt + * Modified to check and search only if searchText in not blank, to prevent blank search. + */ + + mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { + if (searchText.isNotBlank()) { + refreshDataOrRefreshToken(mainActivityViewModel) + } + } + mainActivityViewModel.authData.observe(viewLifecycleOwner) { + if (searchText.isNotBlank()) { + refreshDataOrRefreshToken(mainActivityViewModel) + } + } + searchViewModel.searchResult.observe(viewLifecycleOwner) { if (it.first.isNullOrEmpty()) { noAppsFoundLayout?.visibility = View.VISIBLE @@ -190,9 +217,42 @@ class SearchFragment : recyclerView?.visibility = View.VISIBLE noAppsFoundLayout?.visibility = View.GONE } + if (searchText.isNotBlank() && it.second != ResultStatus.OK) { + /* + * If blank check is not performed then timeout dialog keeps + * popping up whenever search tab is opened. + */ + onTimeout() + } + } + } + + override fun onTimeout() { + if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + mainActivityViewModel.displayTimeoutAlertDialog( + activity = requireActivity(), + message = getString(R.string.timeout_desc_cleanapk), + positiveButtonText = getString(R.string.retry), + positiveButtonBlock = { + showLoadingShimmer() + mainActivityViewModel.retryFetchingTokenAfterTimeout() + }, + negativeButtonText = getString(android.R.string.ok), + negativeButtonBlock = {}, + allowCancel = true, + ) } } + override fun refreshData(authData: AuthData) { + searchViewModel.getSearchResults(searchText, authData) + } + + private fun showLoadingShimmer() { + binding.shimmerLayout.visibility = View.VISIBLE + binding.recyclerView.visibility = View.GONE + } + override fun onResume() { super.onResume() binding.shimmerLayout.startShimmer() @@ -211,7 +271,11 @@ class SearchFragment : shimmerLayout?.visibility = View.VISIBLE recyclerView?.visibility = View.GONE noAppsFoundLayout?.visibility = View.GONE - mainActivityViewModel.authData.value?.let { searchViewModel.getSearchResults(text, it) } + /* + * Set the search text and call for network result. + */ + searchText = text + refreshDataOrRefreshToken(mainActivityViewModel) } return false } -- GitLab From b30c27413f02dc387c931e1e22a14c65cc669e05 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 16:19:19 +0530 Subject: [PATCH 29/53] App lounge: (issue_5413) minor formatting change --- .../e/apps/applicationlist/ApplicationListFragment.kt | 9 ++++++--- .../main/java/foundation/e/apps/search/SearchFragment.kt | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 678411b45..2ce892808 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -96,9 +96,12 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu appList.let { mainActivityViewModel.updateStatusOfFusedApps(it, list) } - viewModel.appListLiveData.apply { - value = Pair(appList, value?.second) - } + + /* + * Done in one line, so that on Ctrl+click on appListLiveData, + * we can see that it is being updated here. + */ + viewModel.appListLiveData.apply { value = Pair(appList, value?.second) } } } diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index c6bd5445c..8c3b5ebaf 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -186,9 +186,12 @@ class SearchFragment : searchList.let { mainActivityViewModel.updateStatusOfFusedApps(searchList, list) } - searchViewModel.searchResult.apply { - value = Pair(searchList, value?.second) - } + + /* + * Done in one line, so that on Ctrl+click on searchResult, + * we can see that it is being updated here. + */ + searchViewModel.searchResult.apply { value = Pair(searchList, value?.second) } } -- GitLab From 242838fa0d53379e3c26b6b2bdc2e54b9c1d18f9 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 16:28:49 +0530 Subject: [PATCH 30/53] App lounge: (issue_5413) add ResultStatus in UpdatesViewModel.updatesList in Pair. Make other relevant changes for the new type. --- .../e/apps/updates/UpdatesFragment.kt | 10 ++++---- .../e/apps/updates/UpdatesViewModel.kt | 10 +++++--- .../updates/manager/UpdatesManagerImpl.kt | 24 ++++++++++++++----- .../manager/UpdatesManagerRepository.kt | 3 ++- .../e/apps/updates/manager/UpdatesWorker.kt | 2 +- 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index eb03b02d9..71aa773e1 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -126,14 +126,14 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { } updatesViewModel.updatesList.observe(viewLifecycleOwner) { - listAdapter?.setData(it) + listAdapter?.setData(it.first) if (!isDownloadObserverAdded) { observeDownloadList() isDownloadObserverAdded = true } binding.progressBar.visibility = View.GONE recyclerView.visibility = View.VISIBLE - if (!it.isNullOrEmpty()) { + if (!it.first.isNullOrEmpty()) { binding.button.isEnabled = true binding.noUpdates.visibility = View.GONE } else { @@ -145,11 +145,11 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { private fun observeDownloadList() { mainActivityViewModel.downloadList.observe(viewLifecycleOwner) { list -> - val appList = updatesViewModel.updatesList.value?.toMutableList() - appList?.let { + val appList = updatesViewModel.updatesList.value?.first?.toMutableList() ?: emptyList() + appList.let { mainActivityViewModel.updateStatusOfFusedApps(appList, list) } - updatesViewModel.updatesList.value = appList + updatesViewModel.updatesList.apply { value = Pair(appList, value?.second) } } } diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt index a440d34ab..ff4e5b19d 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt @@ -25,6 +25,7 @@ import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.updates.manager.UpdatesManagerRepository +import foundation.e.apps.utils.enums.ResultStatus import kotlinx.coroutines.launch import javax.inject.Inject @@ -33,13 +34,16 @@ class UpdatesViewModel @Inject constructor( private val updatesManagerRepository: UpdatesManagerRepository ) : ViewModel() { - val updatesList: MutableLiveData> = MutableLiveData() + val updatesList: MutableLiveData, ResultStatus?>> = MutableLiveData() fun getUpdates(authData: AuthData) { viewModelScope.launch { + val updatesResult = updatesManagerRepository.getUpdates(authData) updatesList.postValue( - updatesManagerRepository.getUpdates(authData) - .filter { !(!it.isFree && authData.isAnonymous) } + Pair( + updatesResult.first.filter { !(!it.isFree && authData.isAnonymous) }, + updatesResult.second + ) ) } } diff --git a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt index d09ba24f1..d82ebbb32 100644 --- a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt +++ b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt @@ -23,6 +23,7 @@ import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.utils.enums.Origin +import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import javax.inject.Inject @@ -33,35 +34,46 @@ class UpdatesManagerImpl @Inject constructor( private val TAG = UpdatesManagerImpl::class.java.simpleName // TODO: MAKE THIS LOGIC MORE SANE - suspend fun getUpdates(authData: AuthData): List { + suspend fun getUpdates(authData: AuthData): Pair, ResultStatus> { val pkgList = mutableListOf() val updateList = mutableListOf() + var status = ResultStatus.OK val userApplications = pkgManagerModule.getAllUserApps() userApplications.forEach { pkgList.add(it.packageName) } if (pkgList.isNotEmpty()) { // Get updates from CleanAPK - val cleanAPKList = fusedAPIRepository.getApplicationDetails( + val cleanAPKResult = fusedAPIRepository.getApplicationDetails( pkgList, authData, Origin.CLEANAPK ) - cleanAPKList.first.forEach { + cleanAPKResult.first.forEach { if (it.package_name in pkgList) pkgList.remove(it.package_name) if (it.status == Status.UPDATABLE) updateList.add(it) } + cleanAPKResult.second.let { + if (it != ResultStatus.OK) { + status = it + } + } // Check for remaining apps from GPlay - val gPlayList = fusedAPIRepository.getApplicationDetails( + val gPlayResult = fusedAPIRepository.getApplicationDetails( pkgList, authData, Origin.GPLAY ) - gPlayList.first.forEach { + gPlayResult.first.forEach { if (it.status == Status.UPDATABLE) updateList.add(it) } + gPlayResult.second.let { + if (it != ResultStatus.OK) { + status = it + } + } } - return updateList + return Pair(updateList, status) } } diff --git a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerRepository.kt b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerRepository.kt index 51850286e..485250a17 100644 --- a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerRepository.kt +++ b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerRepository.kt @@ -20,13 +20,14 @@ package foundation.e.apps.updates.manager import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.api.fused.data.FusedApp +import foundation.e.apps.utils.enums.ResultStatus import javax.inject.Inject class UpdatesManagerRepository @Inject constructor( private val updatesManagerImpl: UpdatesManagerImpl ) { - suspend fun getUpdates(authData: AuthData): List { + suspend fun getUpdates(authData: AuthData): Pair, ResultStatus> { return updatesManagerImpl.getUpdates(authData) } } diff --git a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesWorker.kt b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesWorker.kt index bd2dc6fd3..658244662 100644 --- a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesWorker.kt +++ b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesWorker.kt @@ -60,7 +60,7 @@ class UpdatesWorker @AssistedInject constructor( loadSettings() val authData = getAuthData() val appsNeededToUpdate = updatesManagerRepository.getUpdates(authData) - .filter { !(!it.isFree && authData.isAnonymous) } + .first.filter { !(!it.isFree && authData.isAnonymous) } val isConnectedToUnmeteredNetwork = isConnectedToUnmeteredNetwork(applicationContext) /* * Show notification only if enabled. -- GitLab From 100b888ff933ec1046225eacc93f162ec30f463c Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 16:36:02 +0530 Subject: [PATCH 31/53] App lounge: (issue_5413) add getApplicationCategoryPreference() for updates. --- .../main/java/foundation/e/apps/updates/UpdatesViewModel.kt | 4 ++++ .../foundation/e/apps/updates/manager/UpdatesManagerImpl.kt | 4 ++++ .../e/apps/updates/manager/UpdatesManagerRepository.kt | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt index ff4e5b19d..214538eae 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt @@ -47,4 +47,8 @@ class UpdatesViewModel @Inject constructor( ) } } + + fun getApplicationCategoryPreference(): String { + return updatesManagerRepository.getApplicationCategoryPreference() + } } diff --git a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt index d82ebbb32..03e485126 100644 --- a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt +++ b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt @@ -76,4 +76,8 @@ class UpdatesManagerImpl @Inject constructor( } return Pair(updateList, status) } + + fun getApplicationCategoryPreference(): String { + return fusedAPIRepository.getApplicationCategoryPreference() + } } diff --git a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerRepository.kt b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerRepository.kt index 485250a17..649c82261 100644 --- a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerRepository.kt +++ b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerRepository.kt @@ -30,4 +30,8 @@ class UpdatesManagerRepository @Inject constructor( suspend fun getUpdates(authData: AuthData): Pair, ResultStatus> { return updatesManagerImpl.getUpdates(authData) } + + fun getApplicationCategoryPreference(): String { + return updatesManagerImpl.getApplicationCategoryPreference() + } } -- GitLab From d6800e733c31557dae966e7270421f0a52518d9d Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 17:58:44 +0530 Subject: [PATCH 32/53] App lounge: (issue_5413) implement timeout dialog mechanism in UpdatesFragment --- .../e/apps/updates/UpdatesFragment.kt | 75 ++++++++++++++++--- .../res/navigation/navigation_resource.xml | 3 + 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index 71aa773e1..46c59f4ae 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -25,15 +25,18 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope +import androidx.navigation.findNavController import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.AppInfoFetchViewModel import foundation.e.apps.AppProgressViewModel import foundation.e.apps.MainActivityViewModel import foundation.e.apps.PrivacyInfoViewModel import foundation.e.apps.R +import foundation.e.apps.api.fused.FusedAPIImpl import foundation.e.apps.api.fused.FusedAPIInterface import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.application.subFrags.ApplicationDialogFragment @@ -42,14 +45,17 @@ import foundation.e.apps.databinding.FragmentUpdatesBinding import foundation.e.apps.manager.download.data.DownloadProgress import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.updates.manager.UpdatesWorkManager +import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User +import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.modules.CommonUtilsModule.safeNavigate import foundation.e.apps.utils.modules.PWAManagerModule import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { +class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, TimeoutFragmentInterface { private var _binding: FragmentUpdatesBinding? = null private val binding get() = _binding!! @@ -74,15 +80,15 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { binding.button.isEnabled = false - mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { hasInternet -> - mainActivityViewModel.authData.observe(viewLifecycleOwner) { data -> - if (hasInternet) { - updatesViewModel.getUpdates(data) - binding.button.setOnClickListener { - UpdatesWorkManager.startUpdateAllWork(requireContext().applicationContext) - } - } - } + /* + * Explanation of double observers in HomeFragment.kt + */ + + mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { + refreshDataOrRefreshToken(mainActivityViewModel) + } + mainActivityViewModel.authData.observe(viewLifecycleOwner) { + refreshDataOrRefreshToken(mainActivityViewModel) } val recyclerView = binding.recyclerView @@ -140,9 +146,53 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { binding.noUpdates.visibility = View.VISIBLE binding.button.isEnabled = false } + if (it.second != ResultStatus.OK) { + onTimeout() + } + } + } + + override fun onTimeout() { + if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + mainActivityViewModel.displayTimeoutAlertDialog( + activity = requireActivity(), + message = + if (updatesViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { + getString(R.string.timeout_desc_gplay) + } else { + getString(R.string.timeout_desc_cleanapk) + }, + positiveButtonText = getString(R.string.retry), + positiveButtonBlock = { + showLoadingProgressBar() + mainActivityViewModel.retryFetchingTokenAfterTimeout() + }, + negativeButtonText = + if (updatesViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { + getString(R.string.open_settings) + } else null, + negativeButtonBlock = { + openSettings() + }, + allowCancel = true, + ) } } + override fun refreshData(authData: AuthData) { + updatesViewModel.getUpdates(authData) + binding.button.setOnClickListener { + UpdatesWorkManager.startUpdateAllWork(requireContext().applicationContext) + } + } + + private fun showLoadingProgressBar() { + binding.button.isEnabled = false + binding.noUpdates.visibility = View.GONE + binding.progressBar.visibility = View.VISIBLE + binding.recyclerView.visibility = View.INVISIBLE + } + private fun observeDownloadList() { mainActivityViewModel.downloadList.observe(viewLifecycleOwner) { list -> val appList = updatesViewModel.updatesList.value?.first?.toMutableList() ?: emptyList() @@ -189,4 +239,9 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { override fun cancelDownload(app: FusedApp) { mainActivityViewModel.cancelDownload(app) } + + private fun openSettings() { + view?.findNavController() + ?.safeNavigate(R.id.updatesFragment, R.id.action_updatesFragment_to_SettingsFragment) + } } diff --git a/app/src/main/res/navigation/navigation_resource.xml b/app/src/main/res/navigation/navigation_resource.xml index 5fd2fa9ca..2bacd70dc 100644 --- a/app/src/main/res/navigation/navigation_resource.xml +++ b/app/src/main/res/navigation/navigation_resource.xml @@ -75,6 +75,9 @@ + Date: Mon, 16 May 2022 18:00:14 +0530 Subject: [PATCH 33/53] App lounge: (issue_5413) minor documentation --- .../e/apps/utils/interfaces/TimeoutFragmentInterface.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt index fd4aef2e5..ece1631fb 100644 --- a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt +++ b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt @@ -3,6 +3,11 @@ package foundation.e.apps.utils.interfaces import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.MainActivityViewModel +/* + * Interface for fragments which can display a timeout dialog + * for network calls exceeding timeout limit. + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ interface TimeoutFragmentInterface { fun onTimeout() -- GitLab From 668cb6c0e70ad8138f7576ff2a5414ed921435b8 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 18:25:57 +0530 Subject: [PATCH 34/53] App lounge: (issue_5413) modify timeout dialog for HomeFragment --- .../main/java/foundation/e/apps/home/HomeFragment.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index 601142256..eeaa9bca0 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -171,7 +171,8 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou if (homeViewModel.isFusedHomesEmpty() && !mainActivityViewModel.isTimeoutDialogDisplayed()) { mainActivityViewModel.displayTimeoutAlertDialog( activity = requireActivity(), - message = if (homeViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { + message = + if (homeViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { getString(R.string.timeout_desc_gplay) } else { getString(R.string.timeout_desc_cleanapk) @@ -181,11 +182,14 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou showLoadingShimmer() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, - negativeButtonText = getString(R.string.open_settings), + negativeButtonText = + if (homeViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { + getString(R.string.open_settings) + } else null, negativeButtonBlock = { openSettings() }, - allowCancel = false, + allowCancel = true, ) } } -- GitLab From 82626adcf536d5e7eed573a185a5633efd53aadb Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 19:29:47 +0530 Subject: [PATCH 35/53] App lounge: (issue_5413) make separate methods for getting bulk app details from cleanapk and gplay. for cleanapk, check timeout for individual package query. --- .../e/apps/api/fused/FusedAPIImpl.kt | 123 ++++++++++++------ 1 file changed, 82 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 6f53a2c64..aac88fa67 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -357,56 +357,97 @@ class FusedAPIImpl @Inject constructor( ): Pair, ResultStatus> { val list = mutableListOf() - val status = runCodeBlockWithTimeout({ + val response: Pair, ResultStatus> = + if (origin == Origin.CLEANAPK) { + getAppDetailsListFromCleanapk(packageNameList) + } else { + getAppDetailsListFromGPlay(packageNameList, authData) + } - /* - * Code is just moved inside timeout block. - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 - */ + response.first.forEach { + if (it.package_name.isNotBlank()) { + it.updateStatus() + it.updateType() + list.add(it) + } + } - val response = if (origin == Origin.CLEANAPK) { - val pkgList = mutableListOf() - packageNameList.forEach { - val result = cleanAPKRepository.searchApps( - keyword = it, - by = "package_name" - ).body() - if (result?.apps?.isNotEmpty() == true && result.numberOfResults == 1) { - pkgList.add(result.apps[0]) - } - } - pkgList - } else { - gPlayAPIRepository.getAppDetails(packageNameList, authData).map { app -> - /* - * Some apps are restricted to locations. Example "com.skype.m2". - * For restricted apps, check if it is possible to get their specific app info. - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5174 - */ - if (app.restriction != Constants.Restriction.NOT_RESTRICTED) { - try { - gPlayAPIRepository.getAppDetails(app.packageName, authData) - ?.transformToFusedApp() ?: FusedApp() - } catch (e: Exception) { - FusedApp() - } - } else { - app.transformToFusedApp() + return Pair(list, response.second) + } + + /* + * Get app details of a list of apps from cleanapk. + * Returns list of FusedApp and ResultStatus - which will reflect timeout if even one app fails. + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ + private suspend fun getAppDetailsListFromCleanapk( + packageNameList: List, + ): Pair, ResultStatus> { + var status = ResultStatus.OK + val fusedAppList = mutableListOf() + + /* + * Fetch result of each cleanapk search with separate timeout, + * i.e. check timeout for individual package query. + */ + for (packageName in packageNameList) { + status = runCodeBlockWithTimeout({ + cleanAPKRepository.searchApps( + keyword = packageName, + by = "package_name" + ).body()?.run { + if (apps.isNotEmpty() && numberOfResults == 1) { + fusedAppList.add(apps[0]) } } + }) + + /* + * If status is not ok, immediately return. + */ + if (status != ResultStatus.OK) { + return Pair(fusedAppList, status) } - response.forEach { - if (it.package_name.isNotBlank()) { - it.updateStatus() - it.updateType() - list.add(it) + } + + return Pair(fusedAppList, status) + } + + /* + * Get app details of a list of apps from Google Play store. + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ + private suspend fun getAppDetailsListFromGPlay( + packageNameList: List, + authData: AuthData, + ): Pair, ResultStatus> { + var fusedAppList = listOf() + + /* + * Old code moved from getApplicationDetails() + */ + val status = runCodeBlockWithTimeout({ + fusedAppList = gPlayAPIRepository.getAppDetails(packageNameList, authData).map { app -> + /* + * Some apps are restricted to locations. Example "com.skype.m2". + * For restricted apps, check if it is possible to get their specific app info. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5174 + */ + if (app.restriction != Constants.Restriction.NOT_RESTRICTED) { + try { + gPlayAPIRepository.getAppDetails(app.packageName, authData) + ?.transformToFusedApp() ?: FusedApp() + } catch (e: Exception) { + FusedApp() + } + } else { + app.transformToFusedApp() } } - }) - return Pair(list, status) + return Pair(fusedAppList, status) } suspend fun getApplicationDetails( -- GitLab From d74c17dc0ba32481cf4e79ddd0ae9c80127f3ec0 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 16 May 2022 19:31:10 +0530 Subject: [PATCH 36/53] App lounge: (issue_5413) replace "close" with "ok" in timeout dialog for ApplicationFragment.kt --- .../java/foundation/e/apps/application/ApplicationFragment.kt | 2 +- app/src/main/res/values/strings.xml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index 103408b04..e81e28869 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -294,7 +294,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag showLoadingProgressBar() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, - negativeButtonText = getString(R.string.close), + negativeButtonText = getString(android.R.string.ok), negativeButtonBlock = { requireActivity().onBackPressed() }, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ae75f6ddf..7e0eb34c5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -161,7 +161,6 @@ Some network issue is preventing fetching all applications. Open Settings - Close %1$s]]> -- GitLab From 217088aac4368610b439c401c3dadace47709882 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 02:38:30 +0530 Subject: [PATCH 37/53] App lounge: (issue_5413) rename showLoadingShimmer() and showLoadingProgressBar() to showLoadingUI() in all the classes. --- .../java/foundation/e/apps/application/ApplicationFragment.kt | 4 ++-- .../e/apps/applicationlist/ApplicationListFragment.kt | 4 ++-- .../main/java/foundation/e/apps/categories/AppsFragment.kt | 4 ++-- .../main/java/foundation/e/apps/categories/GamesFragment.kt | 4 ++-- app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 4 ++-- app/src/main/java/foundation/e/apps/search/SearchFragment.kt | 4 ++-- .../main/java/foundation/e/apps/updates/UpdatesFragment.kt | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index e81e28869..c5f743f5b 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -291,7 +291,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag message = getString(R.string.timeout_desc_cleanapk), positiveButtonText = getString(R.string.retry), positiveButtonBlock = { - showLoadingProgressBar() + showLoadingUI() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, negativeButtonText = getString(android.R.string.ok), @@ -584,7 +584,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag } } - private fun showLoadingProgressBar() { + private fun showLoadingUI() { binding.applicationLayout.visibility = View.GONE binding.progressBar.visibility = View.VISIBLE } diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 2ce892808..83767c080 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -188,7 +188,7 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu message = getString(R.string.timeout_desc_cleanapk), positiveButtonText = getString(R.string.retry), positiveButtonBlock = { - showLoadingShimmer() + showLoadingUI() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, negativeButtonText = getString(android.R.string.ok), @@ -226,7 +226,7 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu } } - private fun showLoadingShimmer() { + private fun showLoadingUI() { binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE } diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index f1257cf73..02c76b2c3 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -90,7 +90,7 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface positiveButtonBlock = {}, negativeButtonText = getString(R.string.retry), negativeButtonBlock = { - showLoadingShimmer() + showLoadingUI() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, allowCancel = !categoriesViewModel.isCategoriesEmpty(), @@ -105,7 +105,7 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface ) } - private fun showLoadingShimmer() { + private fun showLoadingUI() { binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE } diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index a1f72e1b8..1d9884da6 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -85,7 +85,7 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac positiveButtonBlock = {}, negativeButtonText = getString(R.string.retry), negativeButtonBlock = { - showLoadingShimmer() + showLoadingUI() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, allowCancel = !categoriesViewModel.isCategoriesEmpty(), @@ -100,7 +100,7 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac ) } - private fun showLoadingShimmer() { + private fun showLoadingUI() { binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE } diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index eeaa9bca0..a212efcc5 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -179,7 +179,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou }, positiveButtonText = getString(R.string.retry), positiveButtonBlock = { - showLoadingShimmer() + showLoadingUI() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, negativeButtonText = @@ -198,7 +198,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou homeViewModel.getHomeScreenData(authData) } - private fun showLoadingShimmer() { + private fun showLoadingUI() { binding.shimmerLayout.visibility = View.VISIBLE binding.parentRV.visibility = View.GONE } diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index 8c3b5ebaf..47743f2e5 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -237,7 +237,7 @@ class SearchFragment : message = getString(R.string.timeout_desc_cleanapk), positiveButtonText = getString(R.string.retry), positiveButtonBlock = { - showLoadingShimmer() + showLoadingUI() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, negativeButtonText = getString(android.R.string.ok), @@ -251,7 +251,7 @@ class SearchFragment : searchViewModel.getSearchResults(searchText, authData) } - private fun showLoadingShimmer() { + private fun showLoadingUI() { binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE } diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index 46c59f4ae..a0661426a 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -164,7 +164,7 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, }, positiveButtonText = getString(R.string.retry), positiveButtonBlock = { - showLoadingProgressBar() + showLoadingUI() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, negativeButtonText = @@ -186,7 +186,7 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, } } - private fun showLoadingProgressBar() { + private fun showLoadingUI() { binding.button.isEnabled = false binding.noUpdates.visibility = View.GONE binding.progressBar.visibility = View.VISIBLE -- GitLab From 8f8f9afc2a215bf083c0fa7dae7505bc8febe0e5 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 02:53:17 +0530 Subject: [PATCH 38/53] App lounge: (issue_5413) create method stopLoadingUI() in all classes --- .../foundation/e/apps/application/ApplicationFragment.kt | 8 ++++++-- .../java/foundation/e/apps/categories/AppsFragment.kt | 8 ++++++-- .../java/foundation/e/apps/categories/GamesFragment.kt | 8 ++++++-- app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 8 ++++++-- .../main/java/foundation/e/apps/search/SearchFragment.kt | 8 ++++++-- .../java/foundation/e/apps/updates/UpdatesFragment.kt | 8 ++++++-- 6 files changed, 36 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index c5f743f5b..85a549a4c 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -579,8 +579,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag private fun fetchAppTracker(fusedApp: FusedApp) { privacyInfoViewModel.getAppPrivacyInfoLiveData(fusedApp).observe(viewLifecycleOwner) { updatePrivacyScore() - binding.applicationLayout.visibility = View.VISIBLE - binding.progressBar.visibility = View.GONE + stopLoadingUI() } } @@ -589,6 +588,11 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag binding.progressBar.visibility = View.VISIBLE } + private fun stopLoadingUI() { + binding.applicationLayout.visibility = View.VISIBLE + binding.progressBar.visibility = View.GONE + } + private fun updatePrivacyScore() { val privacyScore = privacyInfoViewModel.getPrivacyScore(applicationViewModel.fusedApp.value?.first) if (privacyScore != -1) { diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index 02c76b2c3..da70aa532 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -72,9 +72,8 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface } categoriesViewModel.categoriesList.observe(viewLifecycleOwner) { + stopLoadingUI() categoriesRVAdapter.setData(it.first) - binding.shimmerLayout.visibility = View.GONE - recyclerView.visibility = View.VISIBLE if (it.third != ResultStatus.OK) { onTimeout() } @@ -110,6 +109,11 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface binding.recyclerView.visibility = View.GONE } + private fun stopLoadingUI() { + binding.shimmerLayout.visibility = View.GONE + binding.recyclerView.visibility = View.VISIBLE + } + override fun onResume() { super.onResume() binding.shimmerLayout.startShimmer() diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 1d9884da6..851977fec 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -67,9 +67,8 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac } categoriesViewModel.categoriesList.observe(viewLifecycleOwner) { + stopLoadingUI() categoriesRVAdapter.setData(it.first) - binding.shimmerLayout.visibility = View.GONE - recyclerView.visibility = View.VISIBLE if (it.third != ResultStatus.OK) { onTimeout() } @@ -105,6 +104,11 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac binding.recyclerView.visibility = View.GONE } + private fun stopLoadingUI() { + binding.shimmerLayout.visibility = View.GONE + binding.recyclerView.visibility = View.VISIBLE + } + override fun onResume() { super.onResume() binding.shimmerLayout.startShimmer() diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index a212efcc5..054c68879 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -152,8 +152,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou } homeViewModel.homeScreenData.observe(viewLifecycleOwner) { - binding.shimmerLayout.visibility = View.GONE - binding.parentRV.visibility = View.VISIBLE + stopLoadingUI() if (it.second == ResultStatus.OK) { mainActivityViewModel.dismissTimeoutDialog() homeParentRVAdapter.setData(it.first) @@ -203,6 +202,11 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou binding.parentRV.visibility = View.GONE } + private fun stopLoadingUI() { + binding.shimmerLayout.visibility = View.GONE + binding.parentRV.visibility = View.VISIBLE + } + private fun updateProgressOfDownloadingAppItemViews( homeParentRVAdapter: HomeParentRVAdapter, downloadProgress: DownloadProgress diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index 47743f2e5..ecb1af89c 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -216,8 +216,7 @@ class SearchFragment : noAppsFoundLayout?.visibility = View.VISIBLE } else { listAdapter?.setData(it.first) - shimmerLayout?.visibility = View.GONE - recyclerView?.visibility = View.VISIBLE + stopLoadingUI() noAppsFoundLayout?.visibility = View.GONE } if (searchText.isNotBlank() && it.second != ResultStatus.OK) { @@ -256,6 +255,11 @@ class SearchFragment : binding.recyclerView.visibility = View.GONE } + private fun stopLoadingUI() { + binding.shimmerLayout.visibility = View.GONE + binding.recyclerView.visibility = View.VISIBLE + } + override fun onResume() { super.onResume() binding.shimmerLayout.startShimmer() diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index a0661426a..91fa84681 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -137,8 +137,7 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, observeDownloadList() isDownloadObserverAdded = true } - binding.progressBar.visibility = View.GONE - recyclerView.visibility = View.VISIBLE + stopLoadingUI() if (!it.first.isNullOrEmpty()) { binding.button.isEnabled = true binding.noUpdates.visibility = View.GONE @@ -193,6 +192,11 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, binding.recyclerView.visibility = View.INVISIBLE } + private fun stopLoadingUI() { + binding.progressBar.visibility = View.GONE + binding.recyclerView.visibility = View.VISIBLE + } + private fun observeDownloadList() { mainActivityViewModel.downloadList.observe(viewLifecycleOwner) { list -> val appList = updatesViewModel.updatesList.value?.first?.toMutableList() ?: emptyList() -- GitLab From 86ba793432d01552727dba7b3394bbde02d327f6 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 03:13:50 +0530 Subject: [PATCH 39/53] App lounge: (issue_5413) start and stop shimmer animation in startLoadingUI() and stopLoadingUI() --- app/src/main/java/foundation/e/apps/categories/AppsFragment.kt | 2 ++ app/src/main/java/foundation/e/apps/categories/GamesFragment.kt | 2 ++ app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 2 ++ app/src/main/java/foundation/e/apps/search/SearchFragment.kt | 2 ++ 4 files changed, 8 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index da70aa532..563dca341 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -105,11 +105,13 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface } private fun showLoadingUI() { + binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE } private fun stopLoadingUI() { + binding.shimmerLayout.stopShimmer() binding.shimmerLayout.visibility = View.GONE binding.recyclerView.visibility = View.VISIBLE } diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 851977fec..c76c824e6 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -100,11 +100,13 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac } private fun showLoadingUI() { + binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE } private fun stopLoadingUI() { + binding.shimmerLayout.stopShimmer() binding.shimmerLayout.visibility = View.GONE binding.recyclerView.visibility = View.VISIBLE } diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index 054c68879..cb363fcb5 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -198,11 +198,13 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou } private fun showLoadingUI() { + binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE binding.parentRV.visibility = View.GONE } private fun stopLoadingUI() { + binding.shimmerLayout.stopShimmer() binding.shimmerLayout.visibility = View.GONE binding.parentRV.visibility = View.VISIBLE } diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index ecb1af89c..f907f7dfd 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -251,11 +251,13 @@ class SearchFragment : } private fun showLoadingUI() { + binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE } private fun stopLoadingUI() { + binding.shimmerLayout.stopShimmer() binding.shimmerLayout.visibility = View.GONE binding.recyclerView.visibility = View.VISIBLE } -- GitLab From 1fffc9d2bbf9a74c904d5bcf03384d73b601e65c Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 03:25:36 +0530 Subject: [PATCH 40/53] App lounge: (issue_5413) allow timeout dialog to be cancelled in AppsFragment.kt and GamesFragment.kt --- app/src/main/java/foundation/e/apps/categories/AppsFragment.kt | 2 +- app/src/main/java/foundation/e/apps/categories/GamesFragment.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index 563dca341..5cb63c445 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -92,7 +92,7 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface showLoadingUI() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, - allowCancel = !categoriesViewModel.isCategoriesEmpty(), + allowCancel = true, ) } } diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index c76c824e6..828da3c44 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -87,7 +87,7 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac showLoadingUI() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, - allowCancel = !categoriesViewModel.isCategoriesEmpty(), + allowCancel = true, ) } } -- GitLab From cc4d9e78ca52b072e34de8be327552e99b24bea6 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 03:26:09 +0530 Subject: [PATCH 41/53] App lounge: (issue_5413) do not allow timeout dialog to be cancelled in HomeFragment.kt --- app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index cb363fcb5..c3c73db1e 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -188,7 +188,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou negativeButtonBlock = { openSettings() }, - allowCancel = true, + allowCancel = false, ) } } -- GitLab From fcc1e94e90a109e3ac314b02aa45648f2d961db9 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 18:42:31 +0530 Subject: [PATCH 42/53] App lounge: (issue_5413) create a prototype JobResult class for merging API requests and other timeout related data return scenarios. --- .../java/foundation/e/apps/api/JobResult.kt | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 app/src/main/java/foundation/e/apps/api/JobResult.kt diff --git a/app/src/main/java/foundation/e/apps/api/JobResult.kt b/app/src/main/java/foundation/e/apps/api/JobResult.kt new file mode 100644 index 000000000..9a57011d2 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/api/JobResult.kt @@ -0,0 +1,78 @@ +package foundation.e.apps.api + +import foundation.e.apps.utils.enums.ResultStatus + +/** + * Currently defunct, not being used anywhere. + * Prototype to merge API request and also get rid of Pair, Triple for timeout related cases. + */ +open class JobResult private constructor(val status: ResultStatus) { + + /* + * Classes for returning multiple data from a function along with a status + * in the form of ResultStatus. + * Use the static overloaded create methods (in companion object) to for easy creation. + * + * If needed to just pass a single data element with status for API requests, + * see the static methods success(), error(), loading() (in companion object). + */ + class of1 (val data1: A, status: ResultStatus): JobResult(status) + class of2 (val data1: A, val data2: B, status: ResultStatus): JobResult(status) + class of3 (val data1: A, val data2: B, val data3: C, status: ResultStatus): JobResult(status) + + var message = "" + + /* + * This is the primary data, mainly for API requests which might send null data. + * Other data (type B, C ...) are secondary/optional data. + * + * For non-null return type, directly use of1, of2, of3 ... classes + * and directly access data1, data2, data3 ... + */ + val data: T? get() = when(this) { + is of1 -> this.data1 + is of2 -> this.data1 + is of3 -> this.data1 + else -> null + } + + fun isSuccess(): Boolean { + return status == ResultStatus.OK + } + + companion object { + fun create(data1: A, status: ResultStatus, message: String? = null): of1 { + return of1(data1, status).apply { + message?.let { this.message = message } + } + } + fun create(data1: A, data2: B, status: ResultStatus, message: String? = null): of2 { + return of2(data1, data2, status).apply { + message?.let { this.message = message } + } + } + fun create(data1: A, data2: B, data3: C, status: ResultStatus, message: String? = null): of3 { + return of3(data1, data2, data3, status).apply { + message?.let { this.message = message } + } + } + + /* + * Methods for API + */ + fun success(data: T): JobResult { + return of1(data, ResultStatus.OK) + } + fun error(message: String, data: T? = null): JobResult { + val result = if (data == null) JobResult(ResultStatus.UNKNOWN) + else of1(data, ResultStatus.UNKNOWN) + return result.apply { + this.message = message + } + } + /*fun loading(data: T?): JobResult { + return if (data == null) JobResult(ResultStatus.LOADING) + else JobResult.of1(data, ResultStatus.LOADING) + }*/ + } +} \ No newline at end of file -- GitLab From 4a588bc59b02e4f4ce77366b3473e366b8ac505e Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 20:30:13 +0530 Subject: [PATCH 43/53] App lounge: (issue_5413) create mission stopLoadingUI() for ApplicationFragment --- .../e/apps/applicationlist/ApplicationListFragment.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 83767c080..545b19e1e 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -162,8 +162,7 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu observeDownloadList() isDownloadObserverAdded = true } - binding.shimmerLayout.visibility = View.GONE - recyclerView.visibility = View.VISIBLE + stopLoadingUI() if (it.second != ResultStatus.OK) { onTimeout() } @@ -227,10 +226,17 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu } private fun showLoadingUI() { + binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE } + private fun stopLoadingUI() { + binding.shimmerLayout.stopShimmer() + binding.shimmerLayout.visibility = View.GONE + binding.recyclerView.visibility = View.VISIBLE + } + private fun updateProgressOfDownloadingItems( recyclerView: RecyclerView, it: DownloadProgress -- GitLab From 9798e3e9050bb27914c10ce3afdb6cb09ea290a8 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 20:34:24 +0530 Subject: [PATCH 44/53] App lounge: (issue_5413) stopLoadingUI when timeout dialog is shown --- .../java/foundation/e/apps/application/ApplicationFragment.kt | 1 + .../foundation/e/apps/applicationlist/ApplicationListFragment.kt | 1 + app/src/main/java/foundation/e/apps/categories/AppsFragment.kt | 1 + app/src/main/java/foundation/e/apps/categories/GamesFragment.kt | 1 + app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 1 + app/src/main/java/foundation/e/apps/search/SearchFragment.kt | 1 + app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt | 1 + 7 files changed, 7 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index 85a549a4c..03fb2039f 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -286,6 +286,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag override fun onTimeout() { if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + stopLoadingUI() mainActivityViewModel.displayTimeoutAlertDialog( activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 545b19e1e..5eb85f9fb 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -182,6 +182,7 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu override fun onTimeout() { if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + stopLoadingUI() mainActivityViewModel.displayTimeoutAlertDialog( activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index 5cb63c445..7abe98bdf 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -82,6 +82,7 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface override fun onTimeout() { if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + stopLoadingUI() mainActivityViewModel.displayTimeoutAlertDialog( activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 828da3c44..01f5bc0ea 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -77,6 +77,7 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac override fun onTimeout() { if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + stopLoadingUI() mainActivityViewModel.displayTimeoutAlertDialog( activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index c3c73db1e..39a169619 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -168,6 +168,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou override fun onTimeout() { if (homeViewModel.isFusedHomesEmpty() && !mainActivityViewModel.isTimeoutDialogDisplayed()) { + stopLoadingUI() mainActivityViewModel.displayTimeoutAlertDialog( activity = requireActivity(), message = diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index f907f7dfd..d98b3f5cd 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -231,6 +231,7 @@ class SearchFragment : override fun onTimeout() { if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + stopLoadingUI() mainActivityViewModel.displayTimeoutAlertDialog( activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index 91fa84681..085e95467 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -153,6 +153,7 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, override fun onTimeout() { if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + stopLoadingUI() mainActivityViewModel.displayTimeoutAlertDialog( activity = requireActivity(), message = -- GitLab From bf4bca9ddf5292432e0a46fc474705169adbb734 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 20:47:36 +0530 Subject: [PATCH 45/53] App lounge: (issue_5413) move timeout dialog code to a separate module from MainActivityViewModel.kt --- .../e/apps/MainActivityViewModel.kt | 115 +++------------ .../e/apps/utils/modules/TimeoutModule.kt | 135 ++++++++++++++++++ 2 files changed, 152 insertions(+), 98 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index 7bca05c7d..2ccd6a469 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -53,6 +53,7 @@ import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.enums.User import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.DataStoreModule +import foundation.e.apps.utils.modules.TimeoutModule import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ru.beryukhov.reactivenetwork.ReactiveNetwork @@ -65,7 +66,8 @@ class MainActivityViewModel @Inject constructor( private val dataStoreModule: DataStoreModule, private val fusedAPIRepository: FusedAPIRepository, private val fusedManagerRepository: FusedManagerRepository, - private val pkgManagerModule: PkgManagerModule + private val pkgManagerModule: PkgManagerModule, + private val timeoutModule: TimeoutModule, ) : ViewModel() { val authDataJson: LiveData = dataStoreModule.authData.asLiveData() @@ -90,26 +92,7 @@ class MainActivityViewModel @Inject constructor( var firstAuthDataFetchTime = 0L /* - * Alert dialog to show to user if App Lounge times out. - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 - */ - private var timeoutAlertDialog: AlertDialog? = null - - /** - * Display timeout alert dialog. - * - * @param activity Activity class. Basically the MainActivity. - * @param message Alert dialog body. - * @param positiveButtonText Positive button text. Example "Retry" - * @param positiveButtonBlock Code block when [positiveButtonText] is pressed. - * @param negativeButtonText Negative button text. Example "Retry" - * @param negativeButtonBlock Code block when [negativeButtonText] is pressed. - * @param positiveButtonText Positive button text. Example "Retry" - * @param positiveButtonBlock Code block when [positiveButtonText] is pressed. - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + * Moved to TimeoutModule class */ fun displayTimeoutAlertDialog( activity: Activity, @@ -122,89 +105,25 @@ class MainActivityViewModel @Inject constructor( neutralButtonBlock: (() -> Unit)? = null, allowCancel: Boolean = true, ) { - val timeoutAlertDialogBuilder = AlertDialog.Builder(activity).apply { - - /* - * Set title. - */ - setTitle(R.string.timeout_title) - - if (!allowCancel) { - /* - * Prevent dismissing the dialog from pressing outside as it will only - * show a blank screen below the dialog. - */ - setCancelable(false) - /* - * If user presses back button to close the dialog without selecting anything, - * close App Lounge. - */ - setOnKeyListener { dialog, keyCode, _ -> - if (keyCode == KeyEvent.KEYCODE_BACK) { - dialog.dismiss() - activity.finish() - } - true - } - } else { - setCancelable(true) - } - - /* - * Set message - */ - setMessage(message) - - /* - * Set buttons. - */ - positiveButtonText?.let { - setPositiveButton(it) {_, _ -> - positiveButtonBlock?.invoke() - } - } - negativeButtonText?.let { - setNegativeButton(it) {_, _ -> - negativeButtonBlock?.invoke() - } - } - neutralButtonText?.let { - setNeutralButton(it) {_, _ -> - neutralButtonBlock?.invoke() - } - } - } - - /* - * Dismiss alert dialog if already being shown - */ - try { - timeoutAlertDialog?.dismiss() - } catch (_: Exception) {} - - timeoutAlertDialog = timeoutAlertDialogBuilder.create() - timeoutAlertDialog?.show() + timeoutModule.displayTimeoutAlertDialog( + activity, + message, + positiveButtonText, + positiveButtonBlock, + negativeButtonText, + negativeButtonBlock, + neutralButtonText, + neutralButtonBlock, + allowCancel + ) } - /** - * Returns true if [timeoutAlertDialog] is displaying. - * Returs false if it is not initialised. - */ fun isTimeoutDialogDisplayed(): Boolean { - return timeoutAlertDialog?.isShowing == true + return timeoutModule.isTimeoutDialogDisplayed() } - /** - * Dismisses the [timeoutAlertDialog] if it is being displayed. - * Does nothing if it is not being displayed. - * Caller need not check if the dialog is being displayed. - */ fun dismissTimeoutDialog() { - if (isTimeoutDialogDisplayed()) { - try { - timeoutAlertDialog?.dismiss() - } catch (_: Exception) {} - } + timeoutModule.dismissTimeoutDialog() } // Downloads diff --git a/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt b/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt new file mode 100644 index 000000000..c850164a5 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt @@ -0,0 +1,135 @@ +package foundation.e.apps.utils.modules + +import android.app.Activity +import android.content.Context +import android.view.KeyEvent +import androidx.appcompat.app.AlertDialog +import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.R +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TimeoutModule @Inject constructor( + @ApplicationContext private val context: Context, +) { + + /* + * Alert dialog to show to user if App Lounge times out. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 + */ + private var timeoutAlertDialog: AlertDialog? = null + + /** + * Display timeout alert dialog. + * + * @param activity Activity class. Basically the MainActivity. + * @param message Alert dialog body. + * @param positiveButtonText Positive button text. Example "Retry" + * @param positiveButtonBlock Code block when [positiveButtonText] is pressed. + * @param negativeButtonText Negative button text. Example "Retry" + * @param negativeButtonBlock Code block when [negativeButtonText] is pressed. + * @param positiveButtonText Positive button text. Example "Retry" + * @param positiveButtonBlock Code block when [positiveButtonText] is pressed. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ + fun displayTimeoutAlertDialog( + activity: Activity, + message: String, + positiveButtonText: String? = null, + positiveButtonBlock: (() -> Unit)? = null, + negativeButtonText: String? = null, + negativeButtonBlock: (() -> Unit)? = null, + neutralButtonText: String? = null, + neutralButtonBlock: (() -> Unit)? = null, + allowCancel: Boolean = true, + ) { + val timeoutAlertDialogBuilder = AlertDialog.Builder(activity).apply { + + /* + * Set title. + */ + setTitle(R.string.timeout_title) + + if (!allowCancel) { + /* + * Prevent dismissing the dialog from pressing outside as it will only + * show a blank screen below the dialog. + */ + setCancelable(false) + /* + * If user presses back button to close the dialog without selecting anything, + * close App Lounge. + */ + setOnKeyListener { dialog, keyCode, _ -> + if (keyCode == KeyEvent.KEYCODE_BACK) { + dialog.dismiss() + activity.finish() + } + true + } + } else { + setCancelable(true) + } + + /* + * Set message + */ + setMessage(message) + + /* + * Set buttons. + */ + positiveButtonText?.let { + setPositiveButton(it) {_, _ -> + positiveButtonBlock?.invoke() + } + } + negativeButtonText?.let { + setNegativeButton(it) {_, _ -> + negativeButtonBlock?.invoke() + } + } + neutralButtonText?.let { + setNeutralButton(it) {_, _ -> + neutralButtonBlock?.invoke() + } + } + } + + /* + * Dismiss alert dialog if already being shown + */ + try { + timeoutAlertDialog?.dismiss() + } catch (_: Exception) {} + + timeoutAlertDialog = timeoutAlertDialogBuilder.create() + timeoutAlertDialog?.show() + } + + /** + * Returns true if [timeoutAlertDialog] is displaying. + * Returs false if it is not initialised. + */ + fun isTimeoutDialogDisplayed(): Boolean { + return timeoutAlertDialog?.isShowing == true + } + + /** + * Dismisses the [timeoutAlertDialog] if it is being displayed. + * Does nothing if it is not being displayed. + * Caller need not check if the dialog is being displayed. + */ + fun dismissTimeoutDialog() { + if (isTimeoutDialogDisplayed()) { + try { + timeoutAlertDialog?.dismiss() + } catch (_: Exception) {} + } + } + +} \ No newline at end of file -- GitLab From 3aaaf3cc0317ed7c86cd12a83ed25c57824edb6a Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 20:59:49 +0530 Subject: [PATCH 46/53] App lounge: (issue_5413) showLoadingUI in refreshData() --- .../java/foundation/e/apps/application/ApplicationFragment.kt | 1 + .../e/apps/applicationlist/ApplicationListFragment.kt | 2 ++ app/src/main/java/foundation/e/apps/categories/AppsFragment.kt | 1 + app/src/main/java/foundation/e/apps/categories/GamesFragment.kt | 1 + app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 1 + app/src/main/java/foundation/e/apps/search/SearchFragment.kt | 1 + app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt | 1 + 7 files changed, 8 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index 03fb2039f..efbdd934e 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -305,6 +305,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag } override fun refreshData(authData: AuthData) { + showLoadingUI() applicationViewModel.getApplicationDetails( args.id, args.packageName, diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 5eb85f9fb..5faf926e5 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -199,6 +199,8 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu } override fun refreshData(authData: AuthData) { + showLoadingUI() + /* * Code moved from onResume() */ diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index 7abe98bdf..4ee389d84 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -99,6 +99,7 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface } override fun refreshData(authData: AuthData) { + showLoadingUI() categoriesViewModel.getCategoriesList( Category.Type.APPLICATION, authData diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 01f5bc0ea..870cdc92d 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -94,6 +94,7 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac } override fun refreshData(authData: AuthData) { + showLoadingUI() categoriesViewModel.getCategoriesList( Category.Type.GAME, authData diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index 39a169619..7991e787c 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -195,6 +195,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou } override fun refreshData(authData: AuthData) { + showLoadingUI() homeViewModel.getHomeScreenData(authData) } diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index d98b3f5cd..fe543cbfd 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -248,6 +248,7 @@ class SearchFragment : } override fun refreshData(authData: AuthData) { + showLoadingUI() searchViewModel.getSearchResults(searchText, authData) } diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index 085e95467..cf381dad0 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -180,6 +180,7 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, } override fun refreshData(authData: AuthData) { + showLoadingUI() updatesViewModel.getUpdates(authData) binding.button.setOnClickListener { UpdatesWorkManager.startUpdateAllWork(requireContext().applicationContext) -- GitLab From 952dab8875ef7efcd1b3add6e38177649eac9770 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 23:30:22 +0530 Subject: [PATCH 47/53] App lounge: (issue_5413) [WIP] define TimeoutFragmentInterface.timeoutDialogShownLock and update from TimeoutModule --- .../interfaces/TimeoutFragmentInterface.kt | 19 +++++++++++++++++++ .../e/apps/utils/modules/TimeoutModule.kt | 15 +++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt index ece1631fb..12212175c 100644 --- a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt +++ b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt @@ -11,6 +11,25 @@ import foundation.e.apps.MainActivityViewModel interface TimeoutFragmentInterface { fun onTimeout() + /* + * Override as false! + * + * Set this to true when timeout dialog is once shown. + * Set to false if user clicks "Retry". + * Use this to prevent repeatedly showing timeout dialog. + * + * Setting the value to true is automatically done from TimeoutModule.displayTimeoutAlertDialog(). + * To set it as false, call resetTimeoutDialogLock() from the fragment. + * + * Timeout dialog maybe shown multiple times from MainActivity authData observer, + * MainActivityViewModel.downloadList observer, or simply from timing out while + * fetch the information for the fragment. + */ + var timeoutDialogShownLock: Boolean + fun resetTimeoutDialogLock() { + timeoutDialogShownLock = false + } + /* * Recommended to put code to refresh data inside this block. * But call refreshDataOrRefreshToken() to execute the refresh. diff --git a/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt b/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt index c850164a5..3362d2f75 100644 --- a/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt +++ b/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt @@ -6,6 +6,7 @@ import android.view.KeyEvent import androidx.appcompat.app.AlertDialog import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R +import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface import javax.inject.Inject import javax.inject.Singleton @@ -37,6 +38,7 @@ class TimeoutModule @Inject constructor( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ fun displayTimeoutAlertDialog( + timeoutFragment: TimeoutFragmentInterface, activity: Activity, message: String, positiveButtonText: String? = null, @@ -47,6 +49,14 @@ class TimeoutModule @Inject constructor( neutralButtonBlock: (() -> Unit)? = null, allowCancel: Boolean = true, ) { + + /* + * If timeout dialog is already shown, don't proceed. + */ + if (timeoutFragment.timeoutDialogShownLock) { + return + } + val timeoutAlertDialogBuilder = AlertDialog.Builder(activity).apply { /* @@ -109,6 +119,11 @@ class TimeoutModule @Inject constructor( timeoutAlertDialog = timeoutAlertDialogBuilder.create() timeoutAlertDialog?.show() + + /* + * Mark timeout dialog is already shown. + */ + timeoutFragment.timeoutDialogShownLock = true } /** -- GitLab From 3d788f09f882b6007c1933db5e6b46773f8aee12 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 17 May 2022 23:33:04 +0530 Subject: [PATCH 48/53] App lounge: (issue_5413) implement TimeoutFragmentInterface.timeoutDialogShownLock, update displayTimeoutAlertDialog() signature --- app/src/main/java/foundation/e/apps/MainActivityViewModel.kt | 3 +++ .../java/foundation/e/apps/application/ApplicationFragment.kt | 4 ++++ .../e/apps/applicationlist/ApplicationListFragment.kt | 4 ++++ .../main/java/foundation/e/apps/categories/AppsFragment.kt | 4 ++++ .../java/foundation/e/apps/categories/CategoriesFragment.kt | 2 ++ .../main/java/foundation/e/apps/categories/GamesFragment.kt | 4 ++++ app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 4 ++++ app/src/main/java/foundation/e/apps/search/SearchFragment.kt | 4 ++++ .../main/java/foundation/e/apps/updates/UpdatesFragment.kt | 4 ++++ 9 files changed, 33 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index 2ccd6a469..806498782 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -51,6 +51,7 @@ import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.enums.User +import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.DataStoreModule import foundation.e.apps.utils.modules.TimeoutModule @@ -95,6 +96,7 @@ class MainActivityViewModel @Inject constructor( * Moved to TimeoutModule class */ fun displayTimeoutAlertDialog( + timeoutFragment: TimeoutFragmentInterface, activity: Activity, message: String, positiveButtonText: String? = null, @@ -106,6 +108,7 @@ class MainActivityViewModel @Inject constructor( allowCancel: Boolean = true, ) { timeoutModule.displayTimeoutAlertDialog( + timeoutFragment, activity, message, positiveButtonText, diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index efbdd934e..ebfffc077 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -90,6 +90,8 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag private var applicationIcon: ImageView? = null + override var timeoutDialogShownLock: Boolean = false + companion object { private const val PRIVACY_SCORE_SOURCE_CODE_URL = "https://gitlab.e.foundation/e/os/apps/-/blob/main/app/src/main/java/foundation/e/apps/PrivacyInfoViewModel.kt#L78" @@ -288,11 +290,13 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { stopLoadingUI() mainActivityViewModel.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), diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 5faf926e5..51d47a9df 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -74,6 +74,8 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu private val binding get() = _binding!! private var isDownloadObserverAdded = false + override var timeoutDialogShownLock: Boolean = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } @@ -184,11 +186,13 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { stopLoadingUI() mainActivityViewModel.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), diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index 4ee389d84..776754032 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -42,6 +42,8 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface private val categoriesViewModel: CategoriesViewModel by viewModels() private val mainActivityViewModel: MainActivityViewModel by activityViewModels() + override var timeoutDialogShownLock: Boolean = false + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentAppsBinding.bind(view) @@ -84,6 +86,7 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { stopLoadingUI() mainActivityViewModel.displayTimeoutAlertDialog( + timeoutFragment = this, activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), positiveButtonText = getString(android.R.string.ok), @@ -91,6 +94,7 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface negativeButtonText = getString(R.string.retry), negativeButtonBlock = { showLoadingUI() + resetTimeoutDialogLock() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, allowCancel = true, diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt index 1ad0ec11d..f0a7e0d44 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt @@ -36,6 +36,8 @@ class CategoriesFragment : Fragment(R.layout.fragment_categories), TimeoutFragme private val TAG = CategoriesFragment::class.java.simpleName + override var timeoutDialogShownLock: Boolean = false + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentCategoriesBinding.bind(view) diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 870cdc92d..6ebd4e491 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -42,6 +42,8 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac private val categoriesViewModel: CategoriesViewModel by viewModels() private val mainActivityViewModel: MainActivityViewModel by activityViewModels() + override var timeoutDialogShownLock: Boolean = false + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentGamesBinding.bind(view) @@ -79,6 +81,7 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { stopLoadingUI() mainActivityViewModel.displayTimeoutAlertDialog( + timeoutFragment = this, activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), positiveButtonText = getString(android.R.string.ok), @@ -86,6 +89,7 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac negativeButtonText = getString(R.string.retry), negativeButtonBlock = { showLoadingUI() + resetTimeoutDialogLock() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, allowCancel = true, diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index 7991e787c..10e0682ff 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -69,6 +69,8 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou @Inject lateinit var pwaManagerModule: PWAManagerModule + override var timeoutDialogShownLock: Boolean = false + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentHomeBinding.bind(view) @@ -170,6 +172,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou if (homeViewModel.isFusedHomesEmpty() && !mainActivityViewModel.isTimeoutDialogDisplayed()) { stopLoadingUI() mainActivityViewModel.displayTimeoutAlertDialog( + timeoutFragment = this, activity = requireActivity(), message = if (homeViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { @@ -180,6 +183,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou positiveButtonText = getString(R.string.retry), positiveButtonBlock = { showLoadingUI() + resetTimeoutDialogLock() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, negativeButtonText = diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index fe543cbfd..75237d9ab 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -96,6 +96,8 @@ class SearchFragment : */ private var searchText = "" + override var timeoutDialogShownLock: Boolean = false + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentSearchBinding.bind(view) @@ -233,11 +235,13 @@ class SearchFragment : if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { stopLoadingUI() mainActivityViewModel.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), diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index cf381dad0..8afec48c3 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -74,6 +74,8 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, private var isDownloadObserverAdded = false + override var timeoutDialogShownLock: Boolean = false + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentUpdatesBinding.bind(view) @@ -155,6 +157,7 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { stopLoadingUI() mainActivityViewModel.displayTimeoutAlertDialog( + timeoutFragment = this, activity = requireActivity(), message = if (updatesViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { @@ -165,6 +168,7 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, positiveButtonText = getString(R.string.retry), positiveButtonBlock = { showLoadingUI() + resetTimeoutDialogLock() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, negativeButtonText = -- GitLab From 8916adaff4fc8e5194bbd2eb038ec52e96cd9a4a Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 18 May 2022 02:04:00 +0530 Subject: [PATCH 49/53] App lounge: (issue_5413) call resetTimeoutDialogLock() in onResume() --- .../foundation/e/apps/categories/AppsFragment.kt | 1 + .../e/apps/categories/CategoriesFragment.kt | 12 +++++++++++- .../foundation/e/apps/categories/GamesFragment.kt | 1 + .../main/java/foundation/e/apps/home/HomeFragment.kt | 1 + .../java/foundation/e/apps/search/SearchFragment.kt | 1 + .../foundation/e/apps/updates/UpdatesFragment.kt | 5 +++++ .../utils/interfaces/TimeoutFragmentInterface.kt | 5 +++++ 7 files changed, 25 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index 776754032..3879c8350 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -124,6 +124,7 @@ class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface override fun onResume() { super.onResume() + resetTimeoutDialogLock() binding.shimmerLayout.startShimmer() } diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt index f0a7e0d44..9dfd64d2d 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt @@ -59,7 +59,17 @@ class CategoriesFragment : Fragment(R.layout.fragment_categories), TimeoutFragme } override fun onTimeout() { - childFragmentManager.fragments[0]?.let { + val position = binding.viewPager.currentItem + + val fragment = childFragmentManager.fragments.find { + when (position) { + 0 -> it is AppsFragment + 1 -> it is GamesFragment + else -> false + } + } + + fragment?.let { if (it is TimeoutFragmentInterface) { Log.d(TAG, "Showing timeout on Categories fragment: " + it::class.java.name) it.onTimeout() diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 6ebd4e491..bcb138c1d 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -119,6 +119,7 @@ class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterfac override fun onResume() { super.onResume() + resetTimeoutDialogLock() binding.shimmerLayout.startShimmer() } diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index 10e0682ff..703ffdc43 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -258,6 +258,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou override fun onResume() { super.onResume() + resetTimeoutDialogLock() binding.shimmerLayout.startShimmer() } diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index 75237d9ab..73b52f692 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -270,6 +270,7 @@ class SearchFragment : override fun onResume() { super.onResume() + resetTimeoutDialogLock() binding.shimmerLayout.startShimmer() } diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index 8afec48c3..78964cb5a 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -254,4 +254,9 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, view?.findNavController() ?.safeNavigate(R.id.updatesFragment, R.id.action_updatesFragment_to_SettingsFragment) } + + override fun onResume() { + super.onResume() + resetTimeoutDialogLock() + } } diff --git a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt index 12212175c..64f8c6020 100644 --- a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt +++ b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt @@ -26,6 +26,11 @@ interface TimeoutFragmentInterface { * fetch the information for the fragment. */ var timeoutDialogShownLock: Boolean + + /* + * Do call this in the "Retry" button block of timeout dialog. + * Also call this in onResume(), otherwise after screen off, the timeout dialog may not appear. + */ fun resetTimeoutDialogLock() { timeoutDialogShownLock = false } -- GitLab From 1208e33715d29f34b710a25adfe7b75b06be7f79 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 18 May 2022 02:27:05 +0530 Subject: [PATCH 50/53] App lounge: (issue_5413) add license --- .../interfaces/TimeoutFragmentInterface.kt | 17 +++++++++++++++++ .../e/apps/utils/modules/TimeoutModule.kt | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt index 64f8c6020..c203c25fa 100644 --- a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt +++ b/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2022 ECORP + * + * 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 . + */ + package foundation.e.apps.utils.interfaces import com.aurora.gplayapi.data.models.AuthData diff --git a/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt b/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt index 3362d2f75..8e2c130e4 100644 --- a/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt +++ b/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2022 ECORP + * + * 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 . + */ + package foundation.e.apps.utils.modules import android.app.Activity -- GitLab From 37a2d3b3059441958a40a5479db3c79ac7575f98 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 19 May 2022 15:25:44 +0530 Subject: [PATCH 51/53] App lounge: (issue_5413) convert TimeoutFragmentInterface to abstract class TimeoutFragment --- app/src/main/java/foundation/e/apps/MainActivity.kt | 4 ++-- .../java/foundation/e/apps/MainActivityViewModel.kt | 6 ++---- .../e/apps/application/ApplicationFragment.kt | 7 ++----- .../apps/applicationlist/ApplicationListFragment.kt | 8 ++------ .../foundation/e/apps/categories/AppsFragment.kt | 7 ++----- .../e/apps/categories/CategoriesFragment.kt | 12 ++++++------ .../foundation/e/apps/categories/GamesFragment.kt | 7 ++----- .../main/java/foundation/e/apps/home/HomeFragment.kt | 7 ++----- .../java/foundation/e/apps/search/SearchFragment.kt | 10 +++------- .../foundation/e/apps/updates/UpdatesFragment.kt | 7 ++----- .../foundation/e/apps/utils/modules/TimeoutModule.kt | 4 ++-- .../TimeoutFragment.kt} | 12 +++++++----- 12 files changed, 34 insertions(+), 57 deletions(-) rename app/src/main/java/foundation/e/apps/utils/{interfaces/TimeoutFragmentInterface.kt => parentFragment/TimeoutFragment.kt} (89%) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 8de3a6b44..5ee0f9a35 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -45,7 +45,7 @@ import foundation.e.apps.setup.signin.SignInViewModel import foundation.e.apps.updates.UpdatesNotifier import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule import kotlinx.coroutines.launch import java.io.File @@ -142,7 +142,7 @@ class MainActivity : AppCompatActivity() { } else { Log.d(TAG, "Timeout validating auth data!") val lastFragment = navHostFragment.childFragmentManager.fragments[0] - if (lastFragment is TimeoutFragmentInterface) { + if (lastFragment is TimeoutFragment) { Log.d(TAG, "Displaying timeout from MainActivity on fragment: " + lastFragment::class.java.name) lastFragment.onTimeout() diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index 806498782..13999d344 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -20,13 +20,11 @@ package foundation.e.apps import android.app.Activity import android.content.Context -import android.content.DialogInterface import android.graphics.Bitmap import android.os.Build import android.os.SystemClock import android.util.Base64 import android.util.Log -import android.view.KeyEvent import android.widget.ImageView import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog @@ -51,7 +49,7 @@ import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.DataStoreModule import foundation.e.apps.utils.modules.TimeoutModule @@ -96,7 +94,7 @@ class MainActivityViewModel @Inject constructor( * Moved to TimeoutModule class */ fun displayTimeoutAlertDialog( - timeoutFragment: TimeoutFragmentInterface, + timeoutFragment: TimeoutFragment, activity: Activity, message: String, positiveButtonText: String? = null, diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index ebfffc077..cf1244d18 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -31,7 +31,6 @@ import android.widget.RelativeLayout import androidx.core.content.ContextCompat import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeCompat -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope @@ -61,7 +60,7 @@ import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule.LIST_OF_NULL import foundation.e.apps.utils.modules.PWAManagerModule import kotlinx.coroutines.Dispatchers @@ -69,7 +68,7 @@ import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFragmentInterface { +class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { private val args: ApplicationFragmentArgs by navArgs() private val TAG = ApplicationFragment::class.java.simpleName @@ -90,8 +89,6 @@ class ApplicationFragment : Fragment(R.layout.fragment_application), TimeoutFrag private var applicationIcon: ImageView? = null - override var timeoutDialogShownLock: Boolean = false - companion object { private const val PRIVACY_SCORE_SOURCE_CODE_URL = "https://gitlab.e.foundation/e/os/apps/-/blob/main/app/src/main/java/foundation/e/apps/PrivacyInfoViewModel.kt#L78" diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 51d47a9df..5898f9196 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -21,7 +21,6 @@ package foundation.e.apps.applicationlist import android.os.Bundle import android.view.View import android.widget.ImageView -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope @@ -47,14 +46,13 @@ import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.PWAManagerModule import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class ApplicationListFragment : Fragment(R.layout.fragment_application_list), FusedAPIInterface, - TimeoutFragmentInterface { +class ApplicationListFragment : TimeoutFragment(R.layout.fragment_application_list), FusedAPIInterface { private val args: ApplicationListFragmentArgs by navArgs() @@ -74,8 +72,6 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu private val binding get() = _binding!! private var isDownloadObserverAdded = false - override var timeoutDialogShownLock: Boolean = false - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index 3879c8350..e4b2001e3 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -20,7 +20,6 @@ package foundation.e.apps.categories import android.os.Bundle import android.view.View -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager @@ -32,18 +31,16 @@ import foundation.e.apps.R import foundation.e.apps.categories.model.CategoriesRVAdapter import foundation.e.apps.databinding.FragmentAppsBinding import foundation.e.apps.utils.enums.ResultStatus -import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.parentFragment.TimeoutFragment @AndroidEntryPoint -class AppsFragment : Fragment(R.layout.fragment_apps), TimeoutFragmentInterface { +class AppsFragment : TimeoutFragment(R.layout.fragment_apps) { private var _binding: FragmentAppsBinding? = null private val binding get() = _binding!! private val categoriesViewModel: CategoriesViewModel by viewModels() private val mainActivityViewModel: MainActivityViewModel by activityViewModels() - override var timeoutDialogShownLock: Boolean = false - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentAppsBinding.bind(view) diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt index 9dfd64d2d..2fb4fc7c9 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt @@ -21,23 +21,21 @@ package foundation.e.apps.categories import android.os.Bundle import android.util.Log import android.view.View -import androidx.fragment.app.Fragment +import com.aurora.gplayapi.data.models.AuthData import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.categories.model.CategoriesVPAdapter import foundation.e.apps.databinding.FragmentCategoriesBinding -import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.parentFragment.TimeoutFragment @AndroidEntryPoint -class CategoriesFragment : Fragment(R.layout.fragment_categories), TimeoutFragmentInterface { +class CategoriesFragment : TimeoutFragment(R.layout.fragment_categories) { private var _binding: FragmentCategoriesBinding? = null private val binding get() = _binding!! private val TAG = CategoriesFragment::class.java.simpleName - override var timeoutDialogShownLock: Boolean = false - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentCategoriesBinding.bind(view) @@ -70,10 +68,12 @@ class CategoriesFragment : Fragment(R.layout.fragment_categories), TimeoutFragme } fragment?.let { - if (it is TimeoutFragmentInterface) { + if (it is TimeoutFragment) { Log.d(TAG, "Showing timeout on Categories fragment: " + it::class.java.name) it.onTimeout() } } } + + override fun refreshData(authData: AuthData) {} } diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index bcb138c1d..314a6e7c3 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -20,7 +20,6 @@ package foundation.e.apps.categories import android.os.Bundle import android.view.View -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager @@ -32,18 +31,16 @@ import foundation.e.apps.R import foundation.e.apps.categories.model.CategoriesRVAdapter import foundation.e.apps.databinding.FragmentGamesBinding import foundation.e.apps.utils.enums.ResultStatus -import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.parentFragment.TimeoutFragment @AndroidEntryPoint -class GamesFragment : Fragment(R.layout.fragment_games), TimeoutFragmentInterface { +class GamesFragment : TimeoutFragment(R.layout.fragment_games) { private var _binding: FragmentGamesBinding? = null private val binding get() = _binding!! private val categoriesViewModel: CategoriesViewModel by viewModels() private val mainActivityViewModel: MainActivityViewModel by activityViewModels() - override var timeoutDialogShownLock: Boolean = false - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentGamesBinding.bind(view) diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index 703ffdc43..e38793178 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -21,7 +21,6 @@ package foundation.e.apps.home import android.os.Bundle import android.view.View import android.widget.ImageView -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope @@ -46,14 +45,14 @@ import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule.safeNavigate import foundation.e.apps.utils.modules.PWAManagerModule import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, TimeoutFragmentInterface { +class HomeFragment : TimeoutFragment(R.layout.fragment_home), FusedAPIInterface { private var _binding: FragmentHomeBinding? = null private val binding get() = _binding!! @@ -69,8 +68,6 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface, Timeou @Inject lateinit var pwaManagerModule: PWAManagerModule - override var timeoutDialogShownLock: Boolean = false - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentHomeBinding.bind(view) diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index 73b52f692..4abbd0c25 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -29,7 +29,6 @@ import android.widget.LinearLayout 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 @@ -54,18 +53,17 @@ import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.PWAManagerModule import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint class SearchFragment : - Fragment(R.layout.fragment_search), + TimeoutFragment(R.layout.fragment_search), SearchView.OnQueryTextListener, SearchView.OnSuggestionListener, - FusedAPIInterface, - TimeoutFragmentInterface { + FusedAPIInterface { @Inject lateinit var pkgManagerModule: PkgManagerModule @@ -96,8 +94,6 @@ class SearchFragment : */ private var searchText = "" - override var timeoutDialogShownLock: Boolean = false - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentSearchBinding.bind(view) diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index 78964cb5a..cd0436d41 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -21,7 +21,6 @@ package foundation.e.apps.updates import android.os.Bundle import android.view.View import android.widget.ImageView -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope @@ -48,14 +47,14 @@ import foundation.e.apps.updates.manager.UpdatesWorkManager import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule.safeNavigate import foundation.e.apps.utils.modules.PWAManagerModule import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, TimeoutFragmentInterface { +class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInterface { private var _binding: FragmentUpdatesBinding? = null private val binding get() = _binding!! @@ -74,8 +73,6 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface, private var isDownloadObserverAdded = false - override var timeoutDialogShownLock: Boolean = false - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentUpdatesBinding.bind(view) diff --git a/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt b/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt index 8e2c130e4..9c6e7d8c4 100644 --- a/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt +++ b/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt @@ -23,7 +23,7 @@ import android.view.KeyEvent import androidx.appcompat.app.AlertDialog import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R -import foundation.e.apps.utils.interfaces.TimeoutFragmentInterface +import foundation.e.apps.utils.parentFragment.TimeoutFragment import javax.inject.Inject import javax.inject.Singleton @@ -55,7 +55,7 @@ class TimeoutModule @Inject constructor( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ fun displayTimeoutAlertDialog( - timeoutFragment: TimeoutFragmentInterface, + timeoutFragment: TimeoutFragment, activity: Activity, message: String, positiveButtonText: String? = null, diff --git a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt b/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt similarity index 89% rename from app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt rename to app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt index c203c25fa..48ece7d98 100644 --- a/app/src/main/java/foundation/e/apps/utils/interfaces/TimeoutFragmentInterface.kt +++ b/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt @@ -15,8 +15,10 @@ * along with this program. If not, see . */ -package foundation.e.apps.utils.interfaces +package foundation.e.apps.utils.parentFragment +import androidx.annotation.LayoutRes +import androidx.fragment.app.Fragment import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.MainActivityViewModel @@ -25,8 +27,8 @@ import foundation.e.apps.MainActivityViewModel * for network calls exceeding timeout limit. * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ -interface TimeoutFragmentInterface { - fun onTimeout() +abstract class TimeoutFragment(@LayoutRes layoutId: Int): Fragment(layoutId) { + abstract fun onTimeout() /* * Override as false! @@ -42,7 +44,7 @@ interface TimeoutFragmentInterface { * MainActivityViewModel.downloadList observer, or simply from timing out while * fetch the information for the fragment. */ - var timeoutDialogShownLock: Boolean + var timeoutDialogShownLock: Boolean = false /* * Do call this in the "Retry" button block of timeout dialog. @@ -56,7 +58,7 @@ interface TimeoutFragmentInterface { * Recommended to put code to refresh data inside this block. * But call refreshDataOrRefreshToken() to execute the refresh. */ - fun refreshData(authData: AuthData) {} + abstract fun refreshData(authData: AuthData) /* * Checks if network connectivity is present. -- GitLab From 0edc200f4a3dc22eb658ca6a611a72aaffeb7aee Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 19 May 2022 16:41:11 +0530 Subject: [PATCH 52/53] App lounge: (issue_5413) minor documentation avoid --- .../e/apps/utils/parentFragment/TimeoutFragment.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt b/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt index 48ece7d98..4cf962be9 100644 --- a/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt +++ b/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt @@ -23,22 +23,21 @@ import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.MainActivityViewModel /* - * Interface for fragments which can display a timeout dialog + * Parent class (extending fragment) for fragments which can display a timeout dialog * for network calls exceeding timeout limit. * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ abstract class TimeoutFragment(@LayoutRes layoutId: Int): Fragment(layoutId) { + abstract fun onTimeout() /* - * Override as false! - * * Set this to true when timeout dialog is once shown. * Set to false if user clicks "Retry". * Use this to prevent repeatedly showing timeout dialog. * * Setting the value to true is automatically done from TimeoutModule.displayTimeoutAlertDialog(). - * To set it as false, call resetTimeoutDialogLock() from the fragment. + * To set it as false, call resetTimeoutDialogLock(). * * Timeout dialog maybe shown multiple times from MainActivity authData observer, * MainActivityViewModel.downloadList observer, or simply from timing out while -- GitLab From 8340670bb86fdc960388c72ea58b40981b9d48cf Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 19 May 2022 17:11:17 +0530 Subject: [PATCH 53/53] App lounge: (issue_5413) Move code from TimeoutModule to TimeoutFragment and delete TimeoutModule.kt --- .../e/apps/MainActivityViewModel.kt | 40 ----- .../e/apps/application/ApplicationFragment.kt | 6 +- .../ApplicationListFragment.kt | 4 +- .../e/apps/categories/AppsFragment.kt | 4 +- .../e/apps/categories/GamesFragment.kt | 4 +- .../foundation/e/apps/home/HomeFragment.kt | 6 +- .../e/apps/search/SearchFragment.kt | 4 +- .../e/apps/updates/UpdatesFragment.kt | 4 +- .../e/apps/utils/modules/TimeoutModule.kt | 167 ------------------ .../utils/parentFragment/TimeoutFragment.kt | 142 ++++++++++++++- 10 files changed, 155 insertions(+), 226 deletions(-) delete mode 100644 app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index 13999d344..4ca7a3bec 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -49,10 +49,8 @@ import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.DataStoreModule -import foundation.e.apps.utils.modules.TimeoutModule import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ru.beryukhov.reactivenetwork.ReactiveNetwork @@ -66,7 +64,6 @@ class MainActivityViewModel @Inject constructor( private val fusedAPIRepository: FusedAPIRepository, private val fusedManagerRepository: FusedManagerRepository, private val pkgManagerModule: PkgManagerModule, - private val timeoutModule: TimeoutModule, ) : ViewModel() { val authDataJson: LiveData = dataStoreModule.authData.asLiveData() @@ -90,43 +87,6 @@ class MainActivityViewModel @Inject constructor( */ var firstAuthDataFetchTime = 0L - /* - * Moved to TimeoutModule class - */ - fun displayTimeoutAlertDialog( - timeoutFragment: TimeoutFragment, - activity: Activity, - message: String, - positiveButtonText: String? = null, - positiveButtonBlock: (() -> Unit)? = null, - negativeButtonText: String? = null, - negativeButtonBlock: (() -> Unit)? = null, - neutralButtonText: String? = null, - neutralButtonBlock: (() -> Unit)? = null, - allowCancel: Boolean = true, - ) { - timeoutModule.displayTimeoutAlertDialog( - timeoutFragment, - activity, - message, - positiveButtonText, - positiveButtonBlock, - negativeButtonText, - negativeButtonBlock, - neutralButtonText, - neutralButtonBlock, - allowCancel - ) - } - - fun isTimeoutDialogDisplayed(): Boolean { - return timeoutModule.isTimeoutDialogDisplayed() - } - - fun dismissTimeoutDialog() { - timeoutModule.dismissTimeoutDialog() - } - // Downloads val downloadList = fusedManagerRepository.getDownloadLiveList() var installInProgress = false diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index cf1244d18..6a261ba0f 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -154,7 +154,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { */ val it = resultPair.first - mainActivityViewModel.dismissTimeoutDialog() + dismissTimeoutDialog() if (applicationViewModel.appStatus.value == null) { applicationViewModel.appStatus.value = it.status @@ -284,9 +284,9 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { } override fun onTimeout() { - if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + if (!isTimeoutDialogDisplayed()) { stopLoadingUI() - mainActivityViewModel.displayTimeoutAlertDialog( + displayTimeoutAlertDialog( timeoutFragment = this, activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 5898f9196..16513628e 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -179,9 +179,9 @@ class ApplicationListFragment : TimeoutFragment(R.layout.fragment_application_li } override fun onTimeout() { - if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + if (!isTimeoutDialogDisplayed()) { stopLoadingUI() - mainActivityViewModel.displayTimeoutAlertDialog( + displayTimeoutAlertDialog( timeoutFragment = this, activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), diff --git a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt index e4b2001e3..0335553b1 100644 --- a/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/AppsFragment.kt @@ -80,9 +80,9 @@ class AppsFragment : TimeoutFragment(R.layout.fragment_apps) { } override fun onTimeout() { - if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + if (!isTimeoutDialogDisplayed()) { stopLoadingUI() - mainActivityViewModel.displayTimeoutAlertDialog( + displayTimeoutAlertDialog( timeoutFragment = this, activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 314a6e7c3..109451974 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -75,9 +75,9 @@ class GamesFragment : TimeoutFragment(R.layout.fragment_games) { } override fun onTimeout() { - if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + if (!isTimeoutDialogDisplayed()) { stopLoadingUI() - mainActivityViewModel.displayTimeoutAlertDialog( + displayTimeoutAlertDialog( timeoutFragment = this, activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index e38793178..c8239acc8 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -153,7 +153,7 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), FusedAPIInterface homeViewModel.homeScreenData.observe(viewLifecycleOwner) { stopLoadingUI() if (it.second == ResultStatus.OK) { - mainActivityViewModel.dismissTimeoutDialog() + dismissTimeoutDialog() homeParentRVAdapter.setData(it.first) } else { onTimeout() @@ -166,9 +166,9 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), FusedAPIInterface } override fun onTimeout() { - if (homeViewModel.isFusedHomesEmpty() && !mainActivityViewModel.isTimeoutDialogDisplayed()) { + if (homeViewModel.isFusedHomesEmpty() && !isTimeoutDialogDisplayed()) { stopLoadingUI() - mainActivityViewModel.displayTimeoutAlertDialog( + displayTimeoutAlertDialog( timeoutFragment = this, activity = requireActivity(), message = diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index 4abbd0c25..deb08675c 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -228,9 +228,9 @@ class SearchFragment : } override fun onTimeout() { - if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + if (!isTimeoutDialogDisplayed()) { stopLoadingUI() - mainActivityViewModel.displayTimeoutAlertDialog( + displayTimeoutAlertDialog( timeoutFragment = this, activity = requireActivity(), message = getString(R.string.timeout_desc_cleanapk), diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index cd0436d41..6a4550ec1 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -151,9 +151,9 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte } override fun onTimeout() { - if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + if (!isTimeoutDialogDisplayed()) { stopLoadingUI() - mainActivityViewModel.displayTimeoutAlertDialog( + displayTimeoutAlertDialog( timeoutFragment = this, activity = requireActivity(), message = diff --git a/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt b/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt deleted file mode 100644 index 9c6e7d8c4..000000000 --- a/app/src/main/java/foundation/e/apps/utils/modules/TimeoutModule.kt +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2022 ECORP - * - * 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 . - */ - -package foundation.e.apps.utils.modules - -import android.app.Activity -import android.content.Context -import android.view.KeyEvent -import androidx.appcompat.app.AlertDialog -import dagger.hilt.android.qualifiers.ApplicationContext -import foundation.e.apps.R -import foundation.e.apps.utils.parentFragment.TimeoutFragment -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TimeoutModule @Inject constructor( - @ApplicationContext private val context: Context, -) { - - /* - * Alert dialog to show to user if App Lounge times out. - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 - */ - private var timeoutAlertDialog: AlertDialog? = null - - /** - * Display timeout alert dialog. - * - * @param activity Activity class. Basically the MainActivity. - * @param message Alert dialog body. - * @param positiveButtonText Positive button text. Example "Retry" - * @param positiveButtonBlock Code block when [positiveButtonText] is pressed. - * @param negativeButtonText Negative button text. Example "Retry" - * @param negativeButtonBlock Code block when [negativeButtonText] is pressed. - * @param positiveButtonText Positive button text. Example "Retry" - * @param positiveButtonBlock Code block when [positiveButtonText] is pressed. - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 - */ - fun displayTimeoutAlertDialog( - timeoutFragment: TimeoutFragment, - activity: Activity, - message: String, - positiveButtonText: String? = null, - positiveButtonBlock: (() -> Unit)? = null, - negativeButtonText: String? = null, - negativeButtonBlock: (() -> Unit)? = null, - neutralButtonText: String? = null, - neutralButtonBlock: (() -> Unit)? = null, - allowCancel: Boolean = true, - ) { - - /* - * If timeout dialog is already shown, don't proceed. - */ - if (timeoutFragment.timeoutDialogShownLock) { - return - } - - val timeoutAlertDialogBuilder = AlertDialog.Builder(activity).apply { - - /* - * Set title. - */ - setTitle(R.string.timeout_title) - - if (!allowCancel) { - /* - * Prevent dismissing the dialog from pressing outside as it will only - * show a blank screen below the dialog. - */ - setCancelable(false) - /* - * If user presses back button to close the dialog without selecting anything, - * close App Lounge. - */ - setOnKeyListener { dialog, keyCode, _ -> - if (keyCode == KeyEvent.KEYCODE_BACK) { - dialog.dismiss() - activity.finish() - } - true - } - } else { - setCancelable(true) - } - - /* - * Set message - */ - setMessage(message) - - /* - * Set buttons. - */ - positiveButtonText?.let { - setPositiveButton(it) {_, _ -> - positiveButtonBlock?.invoke() - } - } - negativeButtonText?.let { - setNegativeButton(it) {_, _ -> - negativeButtonBlock?.invoke() - } - } - neutralButtonText?.let { - setNeutralButton(it) {_, _ -> - neutralButtonBlock?.invoke() - } - } - } - - /* - * Dismiss alert dialog if already being shown - */ - try { - timeoutAlertDialog?.dismiss() - } catch (_: Exception) {} - - timeoutAlertDialog = timeoutAlertDialogBuilder.create() - timeoutAlertDialog?.show() - - /* - * Mark timeout dialog is already shown. - */ - timeoutFragment.timeoutDialogShownLock = true - } - - /** - * Returns true if [timeoutAlertDialog] is displaying. - * Returs false if it is not initialised. - */ - fun isTimeoutDialogDisplayed(): Boolean { - return timeoutAlertDialog?.isShowing == true - } - - /** - * Dismisses the [timeoutAlertDialog] if it is being displayed. - * Does nothing if it is not being displayed. - * Caller need not check if the dialog is being displayed. - */ - fun dismissTimeoutDialog() { - if (isTimeoutDialogDisplayed()) { - try { - timeoutAlertDialog?.dismiss() - } catch (_: Exception) {} - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt b/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt index 4cf962be9..2aa3d485f 100644 --- a/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt +++ b/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt @@ -17,10 +17,14 @@ package foundation.e.apps.utils.parentFragment +import android.app.Activity +import android.view.KeyEvent import androidx.annotation.LayoutRes +import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.MainActivityViewModel +import foundation.e.apps.R /* * Parent class (extending fragment) for fragments which can display a timeout dialog @@ -29,6 +33,13 @@ import foundation.e.apps.MainActivityViewModel */ abstract class TimeoutFragment(@LayoutRes layoutId: Int): Fragment(layoutId) { + /* + * Alert dialog to show to user if App Lounge times out. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 + */ + private var timeoutAlertDialog: AlertDialog? = null + abstract fun onTimeout() /* @@ -36,14 +47,14 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int): Fragment(layoutId) { * Set to false if user clicks "Retry". * Use this to prevent repeatedly showing timeout dialog. * - * Setting the value to true is automatically done from TimeoutModule.displayTimeoutAlertDialog(). + * Setting the value to true is automatically done from displayTimeoutAlertDialog(). * To set it as false, call resetTimeoutDialogLock(). * * Timeout dialog maybe shown multiple times from MainActivity authData observer, * MainActivityViewModel.downloadList observer, or simply from timing out while * fetch the information for the fragment. */ - var timeoutDialogShownLock: Boolean = false + private var timeoutDialogShownLock: Boolean = false /* * Do call this in the "Retry" button block of timeout dialog. @@ -68,11 +79,136 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int): Fragment(layoutId) { fun refreshDataOrRefreshToken(mainActivityViewModel: MainActivityViewModel) { if (mainActivityViewModel.internetConnection.value == true) { mainActivityViewModel.authData.value?.let { authData -> - mainActivityViewModel.dismissTimeoutDialog() + dismissTimeoutDialog() refreshData(authData) } ?: run { mainActivityViewModel.retryFetchingTokenAfterTimeout() } } } + + /** + * Display timeout alert dialog. + * + * @param activity Activity class. Basically the MainActivity. + * @param message Alert dialog body. + * @param positiveButtonText Positive button text. Example "Retry" + * @param positiveButtonBlock Code block when [positiveButtonText] is pressed. + * @param negativeButtonText Negative button text. Example "Retry" + * @param negativeButtonBlock Code block when [negativeButtonText] is pressed. + * @param positiveButtonText Positive button text. Example "Retry" + * @param positiveButtonBlock Code block when [positiveButtonText] is pressed. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 + */ + fun displayTimeoutAlertDialog( + timeoutFragment: TimeoutFragment, + activity: Activity, + message: String, + positiveButtonText: String? = null, + positiveButtonBlock: (() -> Unit)? = null, + negativeButtonText: String? = null, + negativeButtonBlock: (() -> Unit)? = null, + neutralButtonText: String? = null, + neutralButtonBlock: (() -> Unit)? = null, + allowCancel: Boolean = true, + ) { + + /* + * If timeout dialog is already shown, don't proceed. + */ + if (timeoutFragment.timeoutDialogShownLock) { + return + } + + val timeoutAlertDialogBuilder = AlertDialog.Builder(activity).apply { + + /* + * Set title. + */ + setTitle(R.string.timeout_title) + + if (!allowCancel) { + /* + * Prevent dismissing the dialog from pressing outside as it will only + * show a blank screen below the dialog. + */ + setCancelable(false) + /* + * If user presses back button to close the dialog without selecting anything, + * close App Lounge. + */ + setOnKeyListener { dialog, keyCode, _ -> + if (keyCode == KeyEvent.KEYCODE_BACK) { + dialog.dismiss() + activity.finish() + } + true + } + } else { + setCancelable(true) + } + + /* + * Set message + */ + setMessage(message) + + /* + * Set buttons. + */ + positiveButtonText?.let { + setPositiveButton(it) {_, _ -> + positiveButtonBlock?.invoke() + } + } + negativeButtonText?.let { + setNegativeButton(it) {_, _ -> + negativeButtonBlock?.invoke() + } + } + neutralButtonText?.let { + setNeutralButton(it) {_, _ -> + neutralButtonBlock?.invoke() + } + } + } + + /* + * Dismiss alert dialog if already being shown + */ + try { + timeoutAlertDialog?.dismiss() + } catch (_: Exception) {} + + timeoutAlertDialog = timeoutAlertDialogBuilder.create() + timeoutAlertDialog?.show() + + /* + * Mark timeout dialog is already shown. + */ + timeoutFragment.timeoutDialogShownLock = true + } + + /** + * Returns true if [timeoutAlertDialog] is displaying. + * Returs false if it is not initialised. + */ + fun isTimeoutDialogDisplayed(): Boolean { + return timeoutAlertDialog?.isShowing == true + } + + /** + * Dismisses the [timeoutAlertDialog] if it is being displayed. + * Does nothing if it is not being displayed. + * Caller need not check if the dialog is being displayed. + */ + fun dismissTimeoutDialog() { + if (isTimeoutDialogDisplayed()) { + try { + timeoutAlertDialog?.dismiss() + } catch (_: Exception) {} + } + } } \ No newline at end of file -- GitLab