From ca636ee2a6f848a3228188e5796957d8aa8a89ef Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 10 May 2022 16:13:19 +0530 Subject: [PATCH 01/21] App lounge: (issue_5404) offload fetching home data to a different method getHomeScreenDataBasedOnApplicationType() --- .../e/apps/api/fused/FusedAPIImpl.kt | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 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 2e54a4183..d9f91fa16 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 @@ -73,11 +73,23 @@ class FusedAPIImpl @Inject constructor( private var TAG = FusedAPIImpl::class.java.simpleName suspend fun getHomeScreenData(authData: AuthData): List { - val list = mutableListOf() val preferredApplicationType = preferenceManagerModule.preferredApplicationType() + return getHomeScreenDataBasedOnApplicationType(authData, preferredApplicationType) + } - if (preferredApplicationType != APP_TYPE_ANY) { - val response = if (preferredApplicationType == APP_TYPE_OPEN) { + /* + * Offload fetching application to a different method to dynamically fallback to a different + * app source if the user selected app source times out. + * + * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 + */ + private suspend fun getHomeScreenDataBasedOnApplicationType( + authData: AuthData, + applicationType: String + ): List { + val list = mutableListOf() + if (applicationType != APP_TYPE_ANY) { + val response = if (applicationType == APP_TYPE_OPEN) { cleanAPKRepository.getHomeScreenData( CleanAPKInterface.APP_TYPE_ANY, CleanAPKInterface.APP_SOURCE_FOSS @@ -89,7 +101,7 @@ class FusedAPIImpl @Inject constructor( ).body() } response?.home?.let { - list.addAll(generateCleanAPKHome(it, preferredApplicationType)) + list.addAll(generateCleanAPKHome(it, applicationType)) } } else { list.addAll(fetchGPlayHome(authData)) -- GitLab From 04a11d820907760ab2bc43275f824138a2ff8e9c Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 10 May 2022 16:26:43 +0530 Subject: [PATCH 02/21] App lounge: (issue_5404) return application source type along with application list as a Pair --- .../foundation/e/apps/api/fused/FusedAPIImpl.kt | 14 +++++++++++--- .../e/apps/api/fused/FusedAPIRepository.kt | 2 +- .../java/foundation/e/apps/home/HomeFragment.kt | 2 +- .../java/foundation/e/apps/home/HomeViewModel.kt | 8 +++++++- 4 files changed, 20 insertions(+), 6 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 d9f91fa16..4e9c06ecc 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 @@ -72,7 +72,15 @@ class FusedAPIImpl @Inject constructor( private var TAG = FusedAPIImpl::class.java.simpleName - suspend fun getHomeScreenData(authData: AuthData): List { + /** + * Pass application source type along with list of apps. + * Application source type may change in case of timeout of GPlay/cleanapk api. + * + * 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/os/apps/-/issues/142 + */ + suspend fun getHomeScreenData(authData: AuthData): Pair, String> { val preferredApplicationType = preferenceManagerModule.preferredApplicationType() return getHomeScreenDataBasedOnApplicationType(authData, preferredApplicationType) } @@ -86,7 +94,7 @@ class FusedAPIImpl @Inject constructor( private suspend fun getHomeScreenDataBasedOnApplicationType( authData: AuthData, applicationType: String - ): List { + ): Pair, String> { val list = mutableListOf() if (applicationType != APP_TYPE_ANY) { val response = if (applicationType == APP_TYPE_OPEN) { @@ -106,7 +114,7 @@ class FusedAPIImpl @Inject constructor( } else { list.addAll(fetchGPlayHome(authData)) } - return list + return Pair(list, applicationType) } 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 f5f2ea17f..c6848b0cf 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 @@ -34,7 +34,7 @@ import javax.inject.Singleton class FusedAPIRepository @Inject constructor( private val fusedAPIImpl: FusedAPIImpl ) { - suspend fun getHomeScreenData(authData: AuthData): List { + suspend fun getHomeScreenData(authData: AuthData): Pair, String> { 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 d4f0dfb88..c95e671d9 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -113,7 +113,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { } homeViewModel.homeScreenData.observe(viewLifecycleOwner) { - homeParentRVAdapter.setData(it) + homeParentRVAdapter.setData(it.first) binding.shimmerLayout.visibility = View.GONE binding.parentRV.visibility = View.VISIBLE } 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 29b2514c0..a9ea1e7aa 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt @@ -33,7 +33,13 @@ class HomeViewModel @Inject constructor( private val fusedAPIRepository: FusedAPIRepository ) : ViewModel() { - var homeScreenData: MutableLiveData> = MutableLiveData() + /* + * Hold list of applications, as well as application source type. + * Source type may change from user selected preference in case of timeout. + * + * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 + */ + var homeScreenData: MutableLiveData, String>> = MutableLiveData() fun getHomeScreenData(authData: AuthData) { viewModelScope.launch { -- GitLab From b30e4067cac14ebee92603784102da4ff1c46a55 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 15:10:51 +0530 Subject: [PATCH 03/21] App lounge: (issue_5404) define timeout as a constant in CommonUtilsModule --- .../foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt | 7 ++++--- .../foundation/e/apps/utils/modules/CommonUtilsModule.kt | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt b/app/src/main/java/foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt index 10a084239..27148c396 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt @@ -23,6 +23,7 @@ import android.util.Log import com.aurora.gplayapi.data.models.PlayResponse import com.aurora.gplayapi.network.IHttpClient import foundation.e.apps.BuildConfig +import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import okhttp3.Cache import okhttp3.Headers.Companion.toHeaders import okhttp3.HttpUrl @@ -51,9 +52,9 @@ class GPlayHttpClient @Inject constructor( } private val okHttpClient = OkHttpClient().newBuilder() - .connectTimeout(25, TimeUnit.SECONDS) - .readTimeout(25, TimeUnit.SECONDS) - .writeTimeout(25, TimeUnit.SECONDS) + .connectTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) + .readTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) + .writeTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) .retryOnConnectionFailure(true) .followRedirects(true) .followSslRedirects(true) diff --git a/app/src/main/java/foundation/e/apps/utils/modules/CommonUtilsModule.kt b/app/src/main/java/foundation/e/apps/utils/modules/CommonUtilsModule.kt index 12af86294..e00439dbb 100644 --- a/app/src/main/java/foundation/e/apps/utils/modules/CommonUtilsModule.kt +++ b/app/src/main/java/foundation/e/apps/utils/modules/CommonUtilsModule.kt @@ -44,6 +44,7 @@ import javax.inject.Singleton object CommonUtilsModule { val LIST_OF_NULL = listOf("null") + const val timeoutDurationInMillis: Long = 25000 /** * Check supported ABIs by device -- GitLab From 85709c601a5e7afdce711b2f024987142f5ce4dc Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 16:40:31 +0530 Subject: [PATCH 04/21] App lounge: (issue_5404) GPlayHttpClient - prevent retry, add callTimeout --- .../java/foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt b/app/src/main/java/foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt index 27148c396..6411d870f 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt @@ -55,7 +55,8 @@ class GPlayHttpClient @Inject constructor( .connectTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) .readTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) .writeTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) - .retryOnConnectionFailure(true) + .callTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) + .retryOnConnectionFailure(false) .followRedirects(true) .followSslRedirects(true) .cache(cache) -- GitLab From c7c7ea6d1eb05df105d6acce14a22b7e27850c29 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 16:43:28 +0530 Subject: [PATCH 05/21] App lounge: (issue_5404) surround body of getHomeScreenDataBasedOnApplicationType() using withTimeout() --- .../e/apps/api/fused/FusedAPIImpl.kt | 43 ++++++++++++------- 1 file changed, 27 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 4e9c06ecc..343cb623f 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 @@ -20,6 +20,7 @@ package foundation.e.apps.api.fused import android.content.Context import android.text.format.Formatter +import android.util.Log import com.aurora.gplayapi.Constants import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App @@ -46,8 +47,11 @@ 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.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.PWAManagerModule import foundation.e.apps.utils.modules.PreferenceManagerModule +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.withTimeout import javax.inject.Inject import javax.inject.Singleton @@ -96,23 +100,30 @@ class FusedAPIImpl @Inject constructor( applicationType: String ): Pair, String> { val list = mutableListOf() - if (applicationType != APP_TYPE_ANY) { - val response = if (applicationType == APP_TYPE_OPEN) { - cleanAPKRepository.getHomeScreenData( - CleanAPKInterface.APP_TYPE_ANY, - CleanAPKInterface.APP_SOURCE_FOSS - ).body() - } else { - cleanAPKRepository.getHomeScreenData( - CleanAPKInterface.APP_TYPE_PWA, - CleanAPKInterface.APP_SOURCE_ANY - ).body() - } - response?.home?.let { - list.addAll(generateCleanAPKHome(it, applicationType)) + try { + withTimeout(timeoutDurationInMillis) { + if (applicationType != APP_TYPE_ANY) { + val response = if (applicationType == APP_TYPE_OPEN) { + cleanAPKRepository.getHomeScreenData( + CleanAPKInterface.APP_TYPE_ANY, + CleanAPKInterface.APP_SOURCE_FOSS + ).body() + } else { + cleanAPKRepository.getHomeScreenData( + CleanAPKInterface.APP_TYPE_PWA, + CleanAPKInterface.APP_SOURCE_ANY + ).body() + } + response?.home?.let { + list.addAll(generateCleanAPKHome(it, applicationType)) + } + } else { + list.addAll(fetchGPlayHome(authData)) + } } - } else { - list.addAll(fetchGPlayHome(authData)) + } + catch (e: TimeoutCancellationException) { + Log.d(TAG, "Timed out fetching home data for type: $applicationType") } return Pair(list, applicationType) } -- GitLab From 620614a2de413f196bc2155f88712f220c837179 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 17:02:10 +0530 Subject: [PATCH 06/21] App lounge: (issue_5404) record first auth data fetch time and prevent regeneration on timeout --- .../java/foundation/e/apps/MainActivity.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 3c6d100cd..b11922010 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -23,6 +23,7 @@ import android.os.Build import android.os.Bundle import android.os.Environment import android.os.StatFs +import android.os.SystemClock import android.os.storage.StorageManager import android.util.Log import android.view.View @@ -46,6 +47,7 @@ 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.modules.CommonUtilsModule +import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import kotlinx.coroutines.launch import java.io.File import java.util.UUID @@ -84,12 +86,21 @@ class MainActivity : AppCompatActivity() { } } + /* + * Store the time when auth data is fetched for the first time. + * If we try to fetch auth data after timeout, then don't allow it. + * + * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 + */ + var firstAuthDataFetchTime = 0L + fun generateAuthDataBasedOnUserType(user: String) { if (user.isNotBlank() && viewModel.tocStatus.value == true) { when (User.valueOf(user)) { User.ANONYMOUS -> { if (viewModel.authDataJson.value.isNullOrEmpty() && !viewModel.authRequestRunning) { Log.d(TAG, "Fetching new authentication data") + firstAuthDataFetchTime = SystemClock.uptimeMillis() viewModel.getAuthData() } } @@ -99,6 +110,7 @@ class MainActivity : AppCompatActivity() { User.GOOGLE -> { if (viewModel.authData.value == null && !viewModel.authRequestRunning) { Log.d(TAG, "Fetching new authentication data") + firstAuthDataFetchTime = SystemClock.uptimeMillis() signInViewModel.fetchAuthData() } } @@ -134,7 +146,11 @@ class MainActivity : AppCompatActivity() { if (it != true) { Log.d(TAG, "Authentication data validation failed!") viewModel.destroyCredentials { user -> - generateAuthDataBasedOnUserType(user) + if (SystemClock.uptimeMillis() - firstAuthDataFetchTime <= timeoutDurationInMillis) { + generateAuthDataBasedOnUserType(user) + } else { + Log.d(TAG, "Timeout validating auth data!") + } } } else { Log.d(TAG, "Authentication data is valid!") -- GitLab From dc1734831ccfbdce9eae05632c751dc8997c696c Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 17:12:25 +0530 Subject: [PATCH 07/21] App lounge: (issue_5404) add some documentation for getHomeScreenDataBasedOnApplicationType(), catch all other type of exceptions. --- .../foundation/e/apps/api/fused/FusedAPIImpl.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 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 343cb623f..7b42ce6c9 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 @@ -101,6 +101,13 @@ class FusedAPIImpl @Inject constructor( ): Pair, String> { val list = mutableListOf() try { + /* + * Each category of home apps (example "Top Free Apps") will have its own timeout. + * Fetching 6 such categories will have a total timeout to 2 mins 30 seconds + * (considering each category having 25 seconds timeout). + * + * To prevent waiting so long and fail early, use withTimeout{}. + */ withTimeout(timeoutDurationInMillis) { if (applicationType != APP_TYPE_ANY) { val response = if (applicationType == APP_TYPE_OPEN) { @@ -121,9 +128,11 @@ class FusedAPIImpl @Inject constructor( list.addAll(fetchGPlayHome(authData)) } } - } - catch (e: TimeoutCancellationException) { + } catch (e: TimeoutCancellationException) { + e.printStackTrace() Log.d(TAG, "Timed out fetching home data for type: $applicationType") + } catch (e: Exception) { + e.printStackTrace() } return Pair(list, applicationType) } -- GitLab From b6e4434e76c79e19b3a65dea26040041e76d0ca1 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 17:29:50 +0530 Subject: [PATCH 08/21] App lounge: (issue_5404) create method isFusedHomesEmpty in FusedAPIImpl --- .../foundation/e/apps/api/fused/FusedAPIImpl.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) 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 7b42ce6c9..6c9a94485 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 @@ -86,7 +86,19 @@ class FusedAPIImpl @Inject constructor( */ suspend fun getHomeScreenData(authData: AuthData): Pair, String> { val preferredApplicationType = preferenceManagerModule.preferredApplicationType() - return getHomeScreenDataBasedOnApplicationType(authData, preferredApplicationType) + val initialData = getHomeScreenDataBasedOnApplicationType(authData, preferredApplicationType) + if (isFusedHomesEmpty(initialData.first)) { + Log.d(TAG, "Received empty home data.") + } + return initialData + } + + fun isFusedHomesEmpty(fusedHomes: List): Boolean { + var count = 0 + fusedHomes.forEach { + count += it.list.size + } + return count == 0 } /* -- GitLab From 544f1581a0dd0976b679b6b2944109dd9bdb61af Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 17:31:17 +0530 Subject: [PATCH 09/21] App lounge: (issue_5404) move token time record code to MainActivityViewModel --- .../java/foundation/e/apps/MainActivity.kt | 16 +++------------- .../foundation/e/apps/MainActivityViewModel.kt | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index b11922010..39874ab76 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -23,7 +23,6 @@ import android.os.Build import android.os.Bundle import android.os.Environment import android.os.StatFs -import android.os.SystemClock import android.os.storage.StorageManager import android.util.Log import android.view.View @@ -47,7 +46,6 @@ 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.modules.CommonUtilsModule -import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import kotlinx.coroutines.launch import java.io.File import java.util.UUID @@ -86,21 +84,13 @@ class MainActivity : AppCompatActivity() { } } - /* - * Store the time when auth data is fetched for the first time. - * If we try to fetch auth data after timeout, then don't allow it. - * - * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 - */ - var firstAuthDataFetchTime = 0L - fun generateAuthDataBasedOnUserType(user: String) { if (user.isNotBlank() && viewModel.tocStatus.value == true) { when (User.valueOf(user)) { User.ANONYMOUS -> { if (viewModel.authDataJson.value.isNullOrEmpty() && !viewModel.authRequestRunning) { Log.d(TAG, "Fetching new authentication data") - firstAuthDataFetchTime = SystemClock.uptimeMillis() + viewModel.setFirstTokenFetchTime() viewModel.getAuthData() } } @@ -110,7 +100,7 @@ class MainActivity : AppCompatActivity() { User.GOOGLE -> { if (viewModel.authData.value == null && !viewModel.authRequestRunning) { Log.d(TAG, "Fetching new authentication data") - firstAuthDataFetchTime = SystemClock.uptimeMillis() + viewModel.setFirstTokenFetchTime() signInViewModel.fetchAuthData() } } @@ -146,7 +136,7 @@ class MainActivity : AppCompatActivity() { if (it != true) { Log.d(TAG, "Authentication data validation failed!") viewModel.destroyCredentials { user -> - if (SystemClock.uptimeMillis() - firstAuthDataFetchTime <= timeoutDurationInMillis) { + if (viewModel.isTimeEligibleForTokenRefresh()) { generateAuthDataBasedOnUserType(user) } else { Log.d(TAG, "Timeout validating auth data!") diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index 19d8bbc30..b702dee15 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -22,6 +22,7 @@ import android.app.AlertDialog import android.content.Context import android.graphics.Bitmap import android.os.Build +import android.os.SystemClock import android.util.Base64 import android.util.Log import android.widget.ImageView @@ -47,6 +48,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.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.DataStoreModule import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -76,6 +78,14 @@ class MainActivityViewModel @Inject constructor( val purchaseDeclined: MutableLiveData = MutableLiveData() var authRequestRunning = false + /* + * Store the time when auth data is fetched for the first time. + * If we try to fetch auth data after timeout, then don't allow it. + * + * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 + */ + var firstAuthDataFetchTime = 0L + // Downloads val downloadList = fusedManagerRepository.getDownloadLiveList() var installInProgress = false @@ -92,6 +102,14 @@ class MainActivityViewModel @Inject constructor( private const val TAG = "MainActivityViewModel" } + fun setFirstTokenFetchTime() { + firstAuthDataFetchTime = SystemClock.uptimeMillis() + } + + fun isTimeEligibleForTokenRefresh(): Boolean { + return (SystemClock.uptimeMillis() - firstAuthDataFetchTime) <= timeoutDurationInMillis + } + fun getAuthData() { if (!authRequestRunning) { authRequestRunning = true -- GitLab From bfa9e51f31f3bf3483399e1101b4b82480f81439 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 17:52:51 +0530 Subject: [PATCH 10/21] App lounge: (issue_5404) create retryFetchingTokenAfterTimeout in MainActivityViewModel --- .../java/foundation/e/apps/MainActivityViewModel.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index b702dee15..55ae4c140 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -110,6 +110,18 @@ class MainActivityViewModel @Inject constructor( return (SystemClock.uptimeMillis() - firstAuthDataFetchTime) <= timeoutDurationInMillis } + /* + * This method resets the last recorded token fetch time. + * Then it posts authValidity as false. This causes the observer in MainActivity to destroyCredentials + * and fetch new token. + * + * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 + */ + fun retryFetchingTokenAfterTimeout() { + setFirstTokenFetchTime() + authValidity.postValue(false) + } + fun getAuthData() { if (!authRequestRunning) { authRequestRunning = true -- GitLab From bbb0b10ea38bfbd0b5b19b5039d52633f45f6c8d Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 17:57:19 +0530 Subject: [PATCH 11/21] App lounge: (issue_5404) propagate isFusedHomesEmpty() from FusedAPIImpl to HomeFragment --- .../java/foundation/e/apps/api/fused/FusedAPIRepository.kt | 4 ++++ app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 6 +++++- app/src/main/java/foundation/e/apps/home/HomeViewModel.kt | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) 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 c6848b0cf..745dd0773 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 @@ -38,6 +38,10 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.getHomeScreenData(authData) } + fun isFusedHomesEmpty(fusedHomes: List): Boolean { + return fusedAPIImpl.isFusedHomesEmpty(fusedHomes) + } + suspend fun validateAuthData(authData: AuthData): Boolean { return fusedAPIImpl.validateAuthData(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 c95e671d9..351176560 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -113,7 +113,11 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { } homeViewModel.homeScreenData.observe(viewLifecycleOwner) { - homeParentRVAdapter.setData(it.first) + if (!homeViewModel.isFusedHomesEmpty(it.first)) { + homeParentRVAdapter.setData(it.first) + } else { + // TODO: show alert dialog or whatever + } binding.shimmerLayout.visibility = View.GONE binding.parentRV.visibility = View.VISIBLE } 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 a9ea1e7aa..47b0b4df1 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt @@ -46,4 +46,8 @@ class HomeViewModel @Inject constructor( homeScreenData.postValue(fusedAPIRepository.getHomeScreenData(authData)) } } + + fun isFusedHomesEmpty(fusedHomes: List): Boolean { + return fusedAPIRepository.isFusedHomesEmpty(fusedHomes) + } } -- GitLab From 352558260945b544ef8dc2a0c11c968546aa5eac Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 20:33:05 +0530 Subject: [PATCH 12/21] App lounge: (issue_5404) add strings for timeout alert dialog --- app/src/main/res/values/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fb2863aae..4496e1752 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -152,6 +152,16 @@ Can\'t connect! Please check your internet connection and try again + + + Timeout fetching applications! + Some network issue is preventing fetching applications.\n\nFor now, + you can Retry, + or open Settings to choose Open source apps or PWAs. + + Refresh token + Open Settings + Please wait %1$s]]> -- GitLab From 6e0742ec36d054a43ab280d821c84d69c2b5ada7 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 20:33:48 +0530 Subject: [PATCH 13/21] App lounge: (issue_5404) create navigation route to open Settings from Home --- app/src/main/res/navigation/navigation_resource.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/res/navigation/navigation_resource.xml b/app/src/main/res/navigation/navigation_resource.xml index 72834907f..5fd2fa9ca 100644 --- a/app/src/main/res/navigation/navigation_resource.xml +++ b/app/src/main/res/navigation/navigation_resource.xml @@ -42,6 +42,12 @@ app:launchSingleTop="true" app:popUpTo="@+id/navigation_resource" app:popUpToInclusive="true" /> + Date: Wed, 11 May 2022 20:36:14 +0530 Subject: [PATCH 14/21] App lounge: (issue_5404) offload observing authData and refreshing Home to a separate method refreshHomeData() --- .../foundation/e/apps/home/HomeFragment.kt | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 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 351176560..11260b8fd 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -77,13 +77,9 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { } } - mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { hasInternet -> - mainActivityViewModel.authData.observe(viewLifecycleOwner) { authData -> - if (hasInternet) { - authData?.let { - homeViewModel.getHomeScreenData(authData) - } - } + mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { + mainActivityViewModel.authData.observe(viewLifecycleOwner) { + refreshHomeData() } } @@ -127,6 +123,19 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { } } + /* + * Offload loading home data to a different function, to allow retrying mechanism. + */ + private fun refreshHomeData() { + if (mainActivityViewModel.internetConnection.value == true) { + mainActivityViewModel.authData.value?.let { authData -> + binding.shimmerLayout.visibility = View.VISIBLE + binding.parentRV.visibility = View.GONE + homeViewModel.getHomeScreenData(authData) + } + } + } + private fun updateProgressOfDownloadingAppItemViews( homeParentRVAdapter: HomeParentRVAdapter, downloadProgress: DownloadProgress -- GitLab From 7662cebe562dae02b3ec6be6d57423c22eab83be Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 20:37:25 +0530 Subject: [PATCH 15/21] App lounge: (issue_5404) create AlertDialog to show to user in case of a timeout. --- .../foundation/e/apps/home/HomeFragment.kt | 72 +++++++++++++++++-- 1 file changed, 68 insertions(+), 4 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 11260b8fd..c40a71115 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -19,8 +19,11 @@ package foundation.e.apps.home import android.os.Bundle +import android.view.KeyEvent import android.view.View import android.widget.ImageView +import android.widget.Toast +import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels @@ -65,10 +68,59 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { @Inject lateinit var pwaManagerModule: PWAManagerModule + private lateinit var timeoutAlertDialog: AlertDialog + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentHomeBinding.bind(view) + /* + * Alert dialog to show to user if App Lounge times out. + * + * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 + */ + timeoutAlertDialog = AlertDialog.Builder(requireActivity()).apply { + setTitle(R.string.timeout_title) + setMessage(R.string.timeout_desc) + setPositiveButton(R.string.retry) {_, _ -> + /* + * Retry fetching data without refreshing token. + */ + refreshHomeData() + } + setNegativeButton(R.string.refresh_token) {_, _ -> + /* + * Refresh token. + * This will automatically cause the mainActivityViewModel.authData observer + * to attempt to refresh the data. + */ + Toast.makeText(requireContext(), R.string.please_wait, Toast.LENGTH_SHORT).show() + mainActivityViewModel.retryFetchingTokenAfterTimeout() + } + setNeutralButton(R.string.open_settings) {_, _ -> + /* + * Open App Lounge settings so that user can select Open Source apps or PWAs. + */ + openSettings() + } + /* + * 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() + mainActivityViewModel.userType.observe(viewLifecycleOwner) { user -> if (user.isNullOrEmpty() || User.valueOf(user) == User.UNAVAILABLE) { mainActivityViewModel.tocStatus.observe(viewLifecycleOwner) { tosAccepted -> @@ -109,13 +161,13 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { } homeViewModel.homeScreenData.observe(viewLifecycleOwner) { + binding.shimmerLayout.visibility = View.GONE + binding.parentRV.visibility = View.VISIBLE if (!homeViewModel.isFusedHomesEmpty(it.first)) { homeParentRVAdapter.setData(it.first) - } else { - // TODO: show alert dialog or whatever + } else if (!timeoutAlertDialog.isShowing) { + timeoutAlertDialog.show() } - binding.shimmerLayout.visibility = View.GONE - binding.parentRV.visibility = View.VISIBLE } appProgressViewModel.downloadProgress.observe(viewLifecycleOwner) { @@ -129,6 +181,13 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { private fun refreshHomeData() { if (mainActivityViewModel.internetConnection.value == true) { mainActivityViewModel.authData.value?.let { authData -> + try { + timeoutAlertDialog.run { + if (isShowing) { + dismiss() + } + } + } catch (_: Exception) {} binding.shimmerLayout.visibility = View.VISIBLE binding.parentRV.visibility = View.GONE homeViewModel.getHomeScreenData(authData) @@ -213,4 +272,9 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { ?.safeNavigate(R.id.homeFragment, R.id.action_homeFragment_to_signInFragment) } } + + private fun openSettings() { + view?.findNavController() + ?.safeNavigate(R.id.homeFragment, R.id.action_homeFragment_to_SettingsFragment) + } } -- GitLab From 52a70e50c43921b47fbd9011a6944d711baf35b4 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 11 May 2022 23:08:11 +0530 Subject: [PATCH 16/21] App lounge: (issue_5404) update issue links from 142 to 5404 --- app/src/main/java/foundation/e/apps/MainActivityViewModel.kt | 4 ++-- app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt | 4 ++-- app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 2 +- app/src/main/java/foundation/e/apps/home/HomeViewModel.kt | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index 55ae4c140..cac85e3e9 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -82,7 +82,7 @@ class MainActivityViewModel @Inject constructor( * Store the time when auth data is fetched for the first time. * If we try to fetch auth data after timeout, then don't allow it. * - * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ var firstAuthDataFetchTime = 0L @@ -115,7 +115,7 @@ class MainActivityViewModel @Inject constructor( * Then it posts authValidity as false. This causes the observer in MainActivity to destroyCredentials * and fetch new token. * - * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ fun retryFetchingTokenAfterTimeout() { setFirstTokenFetchTime() 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 6c9a94485..e0cbce84e 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 @@ -82,7 +82,7 @@ class FusedAPIImpl @Inject constructor( * * 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/os/apps/-/issues/142 + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ suspend fun getHomeScreenData(authData: AuthData): Pair, String> { val preferredApplicationType = preferenceManagerModule.preferredApplicationType() @@ -105,7 +105,7 @@ class FusedAPIImpl @Inject constructor( * Offload fetching application to a different method to dynamically fallback to a different * app source if the user selected app source times out. * - * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ private suspend fun getHomeScreenDataBasedOnApplicationType( authData: 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 c40a71115..b9109f705 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -77,7 +77,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { /* * Alert dialog to show to user if App Lounge times out. * - * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ timeoutAlertDialog = AlertDialog.Builder(requireActivity()).apply { setTitle(R.string.timeout_title) 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 47b0b4df1..48e020bee 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt @@ -37,7 +37,7 @@ class HomeViewModel @Inject constructor( * Hold list of applications, as well as application source type. * Source type may change from user selected preference in case of timeout. * - * Issue: https://gitlab.e.foundation/e/os/apps/-/issues/142 + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ var homeScreenData: MutableLiveData, String>> = MutableLiveData() -- GitLab From e8ef15dad9d2595b4f868dda3f9947ae458c313c Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 12 May 2022 06:32:44 +0530 Subject: [PATCH 17/21] App lounge: (issue_5404) fix blinking - show loading shimmer via a separate method. remove toast for token refresh --- .../main/java/foundation/e/apps/home/HomeFragment.kt | 11 +++++++---- app/src/main/res/values/strings.xml | 1 - 2 files changed, 7 insertions(+), 5 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 b9109f705..274df110f 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -22,7 +22,6 @@ import android.os.Bundle import android.view.KeyEvent import android.view.View import android.widget.ImageView -import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels @@ -86,6 +85,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { /* * Retry fetching data without refreshing token. */ + showLoadingShimmer() refreshHomeData() } setNegativeButton(R.string.refresh_token) {_, _ -> @@ -94,7 +94,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { * This will automatically cause the mainActivityViewModel.authData observer * to attempt to refresh the data. */ - Toast.makeText(requireContext(), R.string.please_wait, Toast.LENGTH_SHORT).show() + showLoadingShimmer() mainActivityViewModel.retryFetchingTokenAfterTimeout() } setNeutralButton(R.string.open_settings) {_, _ -> @@ -188,13 +188,16 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { } } } catch (_: Exception) {} - binding.shimmerLayout.visibility = View.VISIBLE - binding.parentRV.visibility = View.GONE homeViewModel.getHomeScreenData(authData) } } } + private fun showLoadingShimmer() { + binding.shimmerLayout.visibility = View.VISIBLE + binding.parentRV.visibility = View.GONE + } + private fun updateProgressOfDownloadingAppItemViews( homeParentRVAdapter: HomeParentRVAdapter, downloadProgress: DownloadProgress diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4496e1752..cfeb42c2e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -161,7 +161,6 @@ Refresh token Open Settings - Please wait %1$s]]> -- GitLab From b1186b40ccbc7ca169050d76824e10406580f94f Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 12 May 2022 14:45:59 +0530 Subject: [PATCH 18/21] App lounge: (issue_5404) update strings as per issue description --- app/src/main/java/foundation/e/apps/home/HomeFragment.kt | 2 +- app/src/main/res/values/strings.xml | 6 +++--- 2 files changed, 4 insertions(+), 4 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 274df110f..0314888ea 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -80,7 +80,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { */ timeoutAlertDialog = AlertDialog.Builder(requireActivity()).apply { setTitle(R.string.timeout_title) - setMessage(R.string.timeout_desc) + setMessage(R.string.timeout_desc_gplay) setPositiveButton(R.string.retry) {_, _ -> /* * Retry fetching data without refreshing token. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cfeb42c2e..293bb4c04 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -155,10 +155,10 @@ Timeout fetching applications! - Some network issue is preventing fetching applications.\n\nFor now, - you can Retry, - or open Settings to choose Open source apps or PWAs. + Some network issue is preventing fetching all applications. + \n\nOpen settings to look for Open source apps or PWAs only. + Some network issue is preventing fetching all applications. Refresh token Open Settings -- GitLab From baab76cb29ea9863c183a564f40afa6ec8eba3aa Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 12 May 2022 14:48:55 +0530 Subject: [PATCH 19/21] App lounge: (issue_5404) Replace android.app.AlertDialog with appcompat AlertDialog in MainActivityViewModel --- app/src/main/java/foundation/e/apps/MainActivityViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 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 cac85e3e9..ac6c7c7f1 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -18,7 +18,6 @@ package foundation.e.apps -import android.app.AlertDialog import android.content.Context import android.graphics.Bitmap import android.os.Build @@ -27,6 +26,7 @@ import android.util.Base64 import android.util.Log import android.widget.ImageView import androidx.annotation.RequiresApi +import androidx.appcompat.app.AlertDialog import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -- GitLab From 4236ceeaa0203c374c4df0b2752e565129916910 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 12 May 2022 16:16:49 +0530 Subject: [PATCH 20/21] App lounge: (issue_5404) move timeoutAlertDialog to MainActivityViewModel, with new specifications from issue. --- .../e/apps/MainActivityViewModel.kt | 107 ++++++++++++++++++ .../e/apps/api/fused/FusedAPIImpl.kt | 12 +- .../foundation/e/apps/home/HomeFragment.kt | 69 ++--------- app/src/main/res/values/strings.xml | 1 - 4 files changed, 124 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index ac6c7c7f1..d3023bd80 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -18,12 +18,15 @@ 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 @@ -38,12 +41,14 @@ 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 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 @@ -86,6 +91,108 @@ 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 lateinit var timeoutAlertDialog: AlertDialog + + /** + * 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] + */ + fun displayTimeoutAlertDialog( + activity: Activity, + positiveButtonBlock: () -> Unit, + openSettings: () -> Unit, + applicationTypeFromPreferences: String, + ) { + 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() + } + true + } + }.create() + } + + timeoutAlertDialog.apply { + /* + * Set retry button. + */ + setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.retry)) {_, _ -> + positiveButtonBlock() + } + /* + * 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() + } + } + } + + timeoutAlertDialog.show() + } + + /** + * Returns true if [timeoutAlertDialog] is displaying. + * Returs false if it is not initialised. + */ + fun isTimeoutDialogDisplayed(): Boolean { + return if (this::timeoutAlertDialog.isInitialized) { + timeoutAlertDialog.isShowing + } else false + } + + /** + * 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) {} + } + } + // Downloads val downloadList = fusedManagerRepository.getDownloadLiveList() var installInProgress = false 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 e0cbce84e..e3d16c174 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 @@ -67,9 +67,15 @@ class FusedAPIImpl @Inject constructor( companion object { private const val CATEGORY_TITLE_REPLACEABLE_CONJUNCTION = "&" - private const val APP_TYPE_ANY = "any" - private const val APP_TYPE_OPEN = "open" - private const val APP_TYPE_PWA = "pwa" + /* + * Removing "private" access specifier to allow access in + * MainActivityViewModel.timeoutAlertDialog + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 + */ + const val APP_TYPE_ANY = "any" + const val APP_TYPE_OPEN = "open" + const val APP_TYPE_PWA = "pwa" private const val CATEGORY_OPEN_GAMES_ID = "game_open_games" private const val CATEGORY_OPEN_GAMES_TITLE = "Open games" } 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 0314888ea..4ece61dde 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -19,10 +19,8 @@ package foundation.e.apps.home import android.os.Bundle -import android.view.KeyEvent import android.view.View import android.widget.ImageView -import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels @@ -67,60 +65,10 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { @Inject lateinit var pwaManagerModule: PWAManagerModule - private lateinit var timeoutAlertDialog: AlertDialog - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentHomeBinding.bind(view) - /* - * Alert dialog to show to user if App Lounge times out. - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 - */ - timeoutAlertDialog = AlertDialog.Builder(requireActivity()).apply { - setTitle(R.string.timeout_title) - setMessage(R.string.timeout_desc_gplay) - setPositiveButton(R.string.retry) {_, _ -> - /* - * Retry fetching data without refreshing token. - */ - showLoadingShimmer() - refreshHomeData() - } - setNegativeButton(R.string.refresh_token) {_, _ -> - /* - * Refresh token. - * This will automatically cause the mainActivityViewModel.authData observer - * to attempt to refresh the data. - */ - showLoadingShimmer() - mainActivityViewModel.retryFetchingTokenAfterTimeout() - } - setNeutralButton(R.string.open_settings) {_, _ -> - /* - * Open App Lounge settings so that user can select Open Source apps or PWAs. - */ - openSettings() - } - /* - * 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() - mainActivityViewModel.userType.observe(viewLifecycleOwner) { user -> if (user.isNullOrEmpty() || User.valueOf(user) == User.UNAVAILABLE) { mainActivityViewModel.tocStatus.observe(viewLifecycleOwner) { tosAccepted -> @@ -165,8 +113,13 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { binding.parentRV.visibility = View.VISIBLE if (!homeViewModel.isFusedHomesEmpty(it.first)) { homeParentRVAdapter.setData(it.first) - } else if (!timeoutAlertDialog.isShowing) { - timeoutAlertDialog.show() + } else if (!mainActivityViewModel.isTimeoutDialogDisplayed()) { + mainActivityViewModel.displayTimeoutAlertDialog(requireActivity(), { + showLoadingShimmer() + mainActivityViewModel.retryFetchingTokenAfterTimeout() + }, { + openSettings() + }, it.second) } } @@ -181,13 +134,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { private fun refreshHomeData() { if (mainActivityViewModel.internetConnection.value == true) { mainActivityViewModel.authData.value?.let { authData -> - try { - timeoutAlertDialog.run { - if (isShowing) { - dismiss() - } - } - } catch (_: Exception) {} + mainActivityViewModel.dismissTimeoutDialog() homeViewModel.getHomeScreenData(authData) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 293bb4c04..3cbd255f6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -159,7 +159,6 @@ \n\nOpen settings to look for Open source apps or PWAs only. Some network issue is preventing fetching all applications. - Refresh token Open Settings -- GitLab From 5f3fbcf72d4a38ad91f4d849ebc0553fc527b0a8 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 12 May 2022 17:19:11 +0530 Subject: [PATCH 21/21] App lounge: (issue_5404) improve isFusedHomesEmpty() --- .../java/foundation/e/apps/api/fused/FusedAPIImpl.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 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 e3d16c174..8130bf9ae 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 @@ -99,12 +99,16 @@ class FusedAPIImpl @Inject constructor( return initialData } + /** + * Check if list in all the FusedHome is empty. + * If any list is not empty, send false. + * Else (if all lists are empty) send true. + */ fun isFusedHomesEmpty(fusedHomes: List): Boolean { - var count = 0 fusedHomes.forEach { - count += it.list.size + if (it.list.isNotEmpty()) return false } - return count == 0 + return true } /* -- GitLab