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 35d71d0889653d5b6fa8f9a1b7e2442817225e7a..ca2557035db1292f34a5bd1a34cd2c87e9f4f12b 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 @@ -107,6 +107,13 @@ class FusedAPIImpl @Inject constructor( return true } + fun isCategoriesEmpty(fusedCategories: List): Boolean { + fusedCategories.forEach { + if (it.title.isNotEmpty()) return false + } + return true + } + fun getApplicationCategoryPreference(): String { return preferenceManagerModule.preferredApplicationType() } @@ -168,7 +175,11 @@ class FusedAPIImpl @Inject constructor( setHomeErrorMessage(apiStatus, source) - scope.emit(ResultSupreme.create(apiStatus, priorList)) + scope.emit( + ResultSupreme.create(apiStatus, priorList.toList()).apply { + otherPayload = source.name + } + ) } private fun setHomeErrorMessage(apiStatus: ResultStatus, source: Source) { @@ -1314,7 +1325,7 @@ class FusedAPIImpl @Inject constructor( updateFilterLevel(authData) } } - list.add(FusedHome(it.key, result)) + list.add(FusedHome(it.key, result, source = APP_TYPE_ANY)) } return 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 270f65a9e98f77da1f7df45965295f70f544f347..94bc0f732ec078e61472b06da812cc69c66abd26 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 @@ -86,6 +86,10 @@ class FusedAPIRepository @Inject constructor(private val fusedAPIImpl: FusedAPII return fusedAPIImpl.isFusedHomesEmpty(fusedHomes) } + fun isCategoriesEmpty(fusedCategories: List): Boolean { + return fusedAPIImpl.isCategoriesEmpty(fusedCategories) + } + fun getApplicationCategoryPreference(): String { return fusedAPIImpl.getApplicationCategoryPreference() } 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 d505cf1367da6f13cf485360680564db58f9f9c1..d9db1e40bc4e5fe039b883ba69ba9f452cd12d86 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesViewModel.kt @@ -26,9 +26,12 @@ 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.login.AuthObject +import foundation.e.apps.utils.enums.AppTag import foundation.e.apps.utils.enums.ResultStatus +import foundation.e.apps.utils.enums.User import foundation.e.apps.utils.exceptions.CleanApkException import foundation.e.apps.utils.exceptions.GPlayException +import foundation.e.apps.utils.exceptions.GPlayLoginException import foundation.e.apps.utils.parentFragment.LoadingViewModel import kotlinx.coroutines.launch import javax.inject.Inject @@ -49,43 +52,50 @@ class CategoriesViewModel @Inject constructor( super.onLoadData(authObjectList, { successAuthList, _ -> successAuthList.find { it is AuthObject.GPlayAuth }?.run { - getCategoriesList(type, result.data!! as AuthData) + getCategoriesList(type, result.data!! as AuthData, this.user) return@onLoadData } successAuthList.find { it is AuthObject.CleanApk }?.run { - getCategoriesList(type, AuthData("", "")) + getCategoriesList(type, AuthData("", ""), this.user) return@onLoadData } }, retryBlock) } - fun getCategoriesList(type: Category.Type, authData: AuthData) { + fun getCategoriesList(type: Category.Type, authData: AuthData, user: User) { viewModelScope.launch { val categoriesData = fusedAPIRepository.getCategoriesList(type, authData) categoriesList.postValue(categoriesData) val status = categoriesData.third - if (status != ResultStatus.OK) { - val exception = - if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) - GPlayException( - categoriesData.third == ResultStatus.TIMEOUT, - status.message.ifBlank { "Data load error" } - ) - else CleanApkException( + val categoriesList = categoriesData.first + + if (status == ResultStatus.OK) { + if (categoriesList.all { it.tag is AppTag.GPlay } && isCategoriesEmpty(categoriesList)) { + exceptionsList.add(GPlayLoginException(false, "Received empty Categories", user)) + } + return@launch + } + + val exception = + if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) + GPlayException( categoriesData.third == ResultStatus.TIMEOUT, status.message.ifBlank { "Data load error" } ) + else CleanApkException( + categoriesData.third == ResultStatus.TIMEOUT, + status.message.ifBlank { "Data load error" } + ) - exceptionsList.add(exception) - exceptionsLiveData.postValue(exceptionsList) - } + exceptionsList.add(exception) + exceptionsLiveData.postValue(exceptionsList) } } - fun isCategoriesEmpty(): Boolean { - return categoriesList.value?.first?.isEmpty() ?: true + fun isCategoriesEmpty(fusedCategories: List): Boolean { + return fusedAPIRepository.isCategoriesEmpty(fusedCategories) } } 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 d42e4dce0ab5f36682f81464d75bc36a2f9eb37d..0f5b72614ceeae7a4aca7c138e6e1efe60062d4a 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeViewModel.kt @@ -28,8 +28,11 @@ import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.fused.data.FusedHome import foundation.e.apps.login.AuthObject +import foundation.e.apps.utils.enums.Source +import foundation.e.apps.utils.enums.User import foundation.e.apps.utils.exceptions.CleanApkException import foundation.e.apps.utils.exceptions.GPlayException +import foundation.e.apps.utils.exceptions.GPlayLoginException import foundation.e.apps.utils.parentFragment.LoadingViewModel import kotlinx.coroutines.launch import javax.inject.Inject @@ -55,12 +58,12 @@ class HomeViewModel @Inject constructor( super.onLoadData(authObjectList, { successAuthList, _ -> successAuthList.find { it is AuthObject.GPlayAuth }?.run { - getHomeScreenData(result.data!! as AuthData, lifecycleOwner) + getHomeScreenData(result.data!! as AuthData, this.user, lifecycleOwner) return@onLoadData } successAuthList.find { it is AuthObject.CleanApk }?.run { - getHomeScreenData(AuthData("", ""), lifecycleOwner) + getHomeScreenData(AuthData("", ""), this.user, lifecycleOwner) return@onLoadData } }, retryBlock) @@ -68,13 +71,22 @@ class HomeViewModel @Inject constructor( fun getHomeScreenData( authData: AuthData, + user: User, lifecycleOwner: LifecycleOwner, ) { viewModelScope.launch { fusedAPIRepository.getHomeScreenData(authData).observe(lifecycleOwner) { homeScreenData.postValue(it) - if (it.isSuccess()) return@observe + val homeList = it.data ?: emptyList() + val source = it.otherPayload?.toString() ?: "" + + if (it.isSuccess()) { + if (homeList.all { source == Source.GPLAY.name } && isFusedHomesEmpty(homeList)) { + exceptionsList.add(GPlayLoginException(false, "Received empty Home", user)) + } + return@observe + } val exception = if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) @@ -97,8 +109,8 @@ class HomeViewModel @Inject constructor( return fusedAPIRepository.getApplicationCategoryPreference() } - fun isFusedHomesEmpty(): Boolean { - return homeScreenData.value?.data?.let { + fun isFusedHomesEmpty(fusedHomes: List): Boolean { + return fusedHomes.let { fusedAPIRepository.isFusedHomesEmpty(it) } ?: true } diff --git a/app/src/main/java/foundation/e/apps/login/AuthObject.kt b/app/src/main/java/foundation/e/apps/login/AuthObject.kt index 84567b197a7be1664ecb387c257164b8079c30e5..8f4943a470a2d498393acb1bc7ca94068853f701 100644 --- a/app/src/main/java/foundation/e/apps/login/AuthObject.kt +++ b/app/src/main/java/foundation/e/apps/login/AuthObject.kt @@ -35,8 +35,9 @@ import foundation.e.apps.utils.enums.User sealed class AuthObject { abstract val result: ResultSupreme<*> + abstract val user: User - class GPlayAuth(override val result: ResultSupreme, val user: User) : AuthObject() - class CleanApk(override val result: ResultSupreme, val user: User) : AuthObject() + class GPlayAuth(override val result: ResultSupreme, override val user: User) : AuthObject() + class CleanApk(override val result: ResultSupreme, override val user: User) : AuthObject() // Add more auth types here } diff --git a/app/src/main/java/foundation/e/apps/login/LoginSourceGPlay.kt b/app/src/main/java/foundation/e/apps/login/LoginSourceGPlay.kt index 03e159d2569fe6722b2e6516a132b783daf4fa69..c79ccc6784c2cc6ba07123d5723ffa71d6b155fd 100644 --- a/app/src/main/java/foundation/e/apps/login/LoginSourceGPlay.kt +++ b/app/src/main/java/foundation/e/apps/login/LoginSourceGPlay.kt @@ -27,7 +27,6 @@ import foundation.e.apps.login.api.GPlayLoginInterface import foundation.e.apps.login.api.GoogleLoginApi import foundation.e.apps.login.api.LoginApiRepository import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.exceptions.GPlayValidationException import java.util.Locale import javax.inject.Inject import javax.inject.Singleton @@ -75,23 +74,15 @@ class LoginSourceGPlay @Inject constructor( */ override suspend fun getAuthObject(): AuthObject.GPlayAuth { val savedAuth = getSavedAuthData() - - val authData = ( - savedAuth ?: run { - // if no saved data, then generate new auth data. - generateAuthData().let { - if (it.isSuccess()) it.data!! - else return AuthObject.GPlayAuth(it, user) - } - } - ) - - // validate authData and save it if nothing is saved (first time use.) - validateAuthData(authData).run { - if (isSuccess() && savedAuth == null) { - saveAuthData(authData) + return if (savedAuth != null) { + AuthObject.GPlayAuth(ResultSupreme.Success(savedAuth), user) + } else { + generateAuthData().let { + if (it.isSuccess()) { + saveAuthData(it.data!!) + AuthObject.GPlayAuth(it, user) + } else AuthObject.GPlayAuth(it, user) } - return AuthObject.GPlayAuth(this, user) } } @@ -199,46 +190,9 @@ class LoginSourceGPlay @Inject constructor( * Finally save the aasToken and create auth data. */ loginDataStore.saveAasToken(aasTokenFetched) - return loginApiRepository.fetchAuthData(email, aasTokenFetched, locale) - } - - /** - * Check if a given [AuthData] from Google login or Anonymous login is valid or not. - * If valid, return the AuthData wrapped in [ResultSupreme], else return null, - * with error message. - */ - private suspend fun validateAuthData( - authData: AuthData, - ): ResultSupreme { - - val formattedAuthData = formattedAuthData(authData) - formattedAuthData.locale = locale - - val validityResponse = loginApiRepository.login(formattedAuthData) - - /* - * Send the email as payload. This is sent to ecloud in case of failure. - * See MainActivityViewModel.uploadFaultyTokenToEcloud. - */ - validityResponse.otherPayload = formattedAuthData.email - - val playResponse = validityResponse.data - return if (validityResponse.isSuccess() && playResponse?.code == 200 && playResponse.isSuccessful) { - ResultSupreme.Success(formattedAuthData) - } else { - val message = - "Validating AuthData failed.\n" + - "Network code: ${playResponse?.code}\n" + - "Success: ${playResponse?.isSuccessful}" + - playResponse?.errorString?.run { - if (isNotBlank()) "\nError message: $this" - else "" - } - - ResultSupreme.Error( - message, - GPlayValidationException(message, user, playResponse?.code ?: -1) - ) + return loginApiRepository.fetchAuthData(email, aasTokenFetched, locale).run { + if (isSuccess()) ResultSupreme.Success(formattedAuthData(this.data!!)) + else this } } } diff --git a/app/src/main/java/foundation/e/apps/login/api/AnonymousLoginApi.kt b/app/src/main/java/foundation/e/apps/login/api/AnonymousLoginApi.kt index b75a6bdfd2a5decee936f00adc27d8d72ca07aa9..a8ebc96a4107deb0d4968036aa97765c0b3d346f 100644 --- a/app/src/main/java/foundation/e/apps/login/api/AnonymousLoginApi.kt +++ b/app/src/main/java/foundation/e/apps/login/api/AnonymousLoginApi.kt @@ -18,10 +18,8 @@ package foundation.e.apps.login.api import com.aurora.gplayapi.data.models.AuthData -import com.aurora.gplayapi.data.models.PlayResponse import com.google.gson.Gson import foundation.e.apps.BuildConfig -import foundation.e.apps.api.gplay.utils.CustomAuthValidator import foundation.e.apps.api.gplay.utils.GPlayHttpClient import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -68,22 +66,4 @@ class AnonymousLoginApi( } return authData } - - /** - * Check if an AuthData is valid. Returns a [PlayResponse]. - * Check [PlayResponse.isSuccessful] to see if the validation was successful. - */ - override suspend fun login(authData: AuthData): PlayResponse { - var result = PlayResponse() - withContext(Dispatchers.IO) { - try { - val authValidator = CustomAuthValidator(authData).using(gPlayHttpClient) - result = authValidator.getValidityResponse() - } catch (e: Exception) { - e.printStackTrace() - throw e - } - } - return result - } } diff --git a/app/src/main/java/foundation/e/apps/login/api/GPlayLoginInterface.kt b/app/src/main/java/foundation/e/apps/login/api/GPlayLoginInterface.kt index 24b212bbcb7aa20a5c33554668eedf039b3ded91..8789a8e0a76794df5d837b78295362e2bb9513ed 100644 --- a/app/src/main/java/foundation/e/apps/login/api/GPlayLoginInterface.kt +++ b/app/src/main/java/foundation/e/apps/login/api/GPlayLoginInterface.kt @@ -18,9 +18,7 @@ package foundation.e.apps.login.api import com.aurora.gplayapi.data.models.AuthData -import com.aurora.gplayapi.data.models.PlayResponse interface GPlayLoginInterface { - suspend fun login(authData: AuthData): PlayResponse suspend fun fetchAuthData(email: String, aasToken: String): AuthData? } diff --git a/app/src/main/java/foundation/e/apps/login/api/GoogleLoginApi.kt b/app/src/main/java/foundation/e/apps/login/api/GoogleLoginApi.kt index 47c7bb19adc31fc3f572dd97c4f11b88e38cbcf3..edb0004aff44272b88f0f7311c33a34061727b85 100644 --- a/app/src/main/java/foundation/e/apps/login/api/GoogleLoginApi.kt +++ b/app/src/main/java/foundation/e/apps/login/api/GoogleLoginApi.kt @@ -21,7 +21,6 @@ import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.PlayResponse import com.aurora.gplayapi.helpers.AuthHelper import foundation.e.apps.api.gplay.utils.AC2DMTask -import foundation.e.apps.api.gplay.utils.CustomAuthValidator import foundation.e.apps.api.gplay.utils.GPlayHttpClient import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -61,22 +60,4 @@ class GoogleLoginApi( } return authData } - - /** - * Check if an AuthData is valid. Returns a [PlayResponse]. - * Check [PlayResponse.isSuccessful] to see if the validation was successful. - */ - override suspend fun login(authData: AuthData): PlayResponse { - var result = PlayResponse() - withContext(Dispatchers.IO) { - try { - val authValidator = CustomAuthValidator(authData).using(gPlayHttpClient) - result = authValidator.getValidityResponse() - } catch (e: Exception) { - e.printStackTrace() - throw e - } - } - return result - } } diff --git a/app/src/main/java/foundation/e/apps/login/api/LoginApiRepository.kt b/app/src/main/java/foundation/e/apps/login/api/LoginApiRepository.kt index fa8602834c93a3df03245bee9a51d1f1589a5917..ea1ce47c9588ab3251177b60401fc4e299762034 100644 --- a/app/src/main/java/foundation/e/apps/login/api/LoginApiRepository.kt +++ b/app/src/main/java/foundation/e/apps/login/api/LoginApiRepository.kt @@ -18,7 +18,6 @@ package foundation.e.apps.login.api import com.aurora.gplayapi.data.models.AuthData -import com.aurora.gplayapi.data.models.PlayResponse import foundation.e.apps.api.ResultSupreme import foundation.e.apps.api.gplay.utils.AC2DMUtil import foundation.e.apps.utils.Constants.timeoutDurationInMillis @@ -65,33 +64,6 @@ class LoginApiRepository constructor( } } - /** - * Get AuthData validity of in the form of PlayResponse. - * Advantage of not using a simple boolean is we get error message and - * network code of the request inside PlayResponse object. - * - * Applicable for both Google and Anonymous login. - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5680 - */ - suspend fun login(authData: AuthData): ResultSupreme { - var response = PlayResponse() - val result = runCodeBlockWithTimeout({ - response = gPlayLoginInterface.login(authData) - if (response.code != 200) { - throw Exception("Validation network code: ${response.code}") - } - response - }) - return ResultSupreme.replicate(result, response).apply { - this.exception = when (result) { - is ResultSupreme.Timeout -> GPlayLoginException(true, "GPlay API timeout", user) - is ResultSupreme.Error -> GPlayLoginException(false, result.message, user) - else -> null - } - } - } - /** * Gets email and oauthToken from Google login, finds the AASToken from AC2DM response * and returns it. This token is then used to fetch AuthData from [fetchAuthData]. diff --git a/app/src/main/java/foundation/e/apps/utils/parentFragment/LoadingViewModel.kt b/app/src/main/java/foundation/e/apps/utils/parentFragment/LoadingViewModel.kt index cbd0ab493ca7bef005ff9db5b96acf7ed4ace54a..a25a61dd29bfc1ba574790050700264842f4e9f2 100644 --- a/app/src/main/java/foundation/e/apps/utils/parentFragment/LoadingViewModel.kt +++ b/app/src/main/java/foundation/e/apps/utils/parentFragment/LoadingViewModel.kt @@ -56,6 +56,8 @@ abstract class LoadingViewModel : ViewModel() { exceptionsList.add(it.result.exception ?: UnknownSourceException()) } + loadingBlock(successAuthList, failedAuthList) + exceptionsList.find { it is GPlayValidationException }?.run { @@ -66,8 +68,6 @@ abstract class LoadingViewModel : ViewModel() { } } - loadingBlock(successAuthList, failedAuthList) - if (successAuthList.isEmpty() && exceptionsList.isNotEmpty()) { /* * As no authentication is successful, nothing can be loaded,