Loading app/src/main/java/foundation/e/apps/MainActivity.kt +37 −3 Original line number Diff line number Diff line Loading @@ -40,8 +40,10 @@ import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.login.LoginViewModel import foundation.e.apps.data.login.exceptions.GPlayValidationException import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.databinding.ActivityMainBinding import foundation.e.apps.install.updates.UpdatesNotifier import foundation.e.apps.ui.MainActivityViewModel Loading @@ -52,10 +54,12 @@ import foundation.e.apps.ui.setup.signin.SignInViewModel import foundation.e.apps.utils.SystemInfoProvider import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import javax.inject.Inject import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import timber.log.Timber @AndroidEntryPoint class MainActivity : AppCompatActivity() { Loading @@ -65,6 +69,9 @@ class MainActivity : AppCompatActivity() { private val TAG = MainActivity::class.java.simpleName private lateinit var viewModel: MainActivityViewModel @Inject lateinit var preferenceManagerModule: PreferenceManagerModule override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Loading Loading @@ -130,6 +137,13 @@ class MainActivity : AppCompatActivity() { email, SystemInfoProvider.getAppBuildInfo() ) } else if (exception != null) { Timber.e(exception, "Login failed! message: ${exception?.localizedMessage}") ApplicationDialogFragment( title = getString(R.string.sign_in_failed_title), message = getString(R.string.sign_in_failed_desc), positiveButtonText = getString(R.string.ok) ).show(supportFragmentManager, TAG) } } } Loading Loading @@ -207,6 +221,10 @@ class MainActivity : AppCompatActivity() { observeInvalidAuth() } launch { observeTooManyRequests() } launch { observeSignatureMissMatchError() } Loading Loading @@ -277,11 +295,27 @@ class MainActivity : AppCompatActivity() { }.distinctUntilChanged { old, new -> ((old.data is String) && (new.data is String) && old.data == new.data) }.collectLatest { val data = it.data as String validatedAuthObject(it) } } private fun validatedAuthObject(appEvent: AppEvent) { val data = appEvent.data as String if (data.isNotBlank()) { loginViewModel.markInvalidAuthObject(data) } } private suspend fun observeTooManyRequests() { EventBus.events.filter { appEvent -> appEvent is AppEvent.TooManyRequests }.collectLatest { binding.sessionErrorLayout.visibility = View.VISIBLE binding.retrySessionButton.setOnClickListener { binding.sessionErrorLayout.visibility = View.GONE loginViewModel.startLoginFlow(listOf(LoginSourceGPlay::class.java.simpleName)) } } } private fun setupBottomNavItemSelectedListener( Loading app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +1 −2 Original line number Diff line number Diff line Loading @@ -280,7 +280,6 @@ class FusedApiImpl @Inject constructor( packageSpecificResults ) } return finalSearchResult } Loading Loading @@ -427,7 +426,7 @@ class FusedApiImpl @Inject constructor( ): FusedApp? { try { getApplicationDetails(query, query, authData, Origin.GPLAY).let { if (it.second == ResultStatus.OK) { if (it.second == ResultStatus.OK && it.first.package_name.isNotEmpty()) { return it.first } } Loading app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt +22 −4 Original line number Diff line number Diff line Loading @@ -45,7 +45,7 @@ import java.util.concurrent.TimeUnit import javax.inject.Inject class GPlayHttpClient @Inject constructor( cache: Cache, private val cache: Cache, ) : IHttpClient { private val POST = "POST" Loading @@ -55,6 +55,9 @@ class GPlayHttpClient @Inject constructor( private const val TAG = "GPlayHttpClient" private const val HTTP_TIMEOUT_IN_SECOND = 10L private const val SEARCH = "search" private const val SEARCH_SUGGEST = "searchSuggest" private const val STATUS_CODE_UNAUTHORIZED = 401 private const val STATUS_CODE_TOO_MANY_REQUESTS = 429 } private val okHttpClient = OkHttpClient().newBuilder() Loading Loading @@ -155,9 +158,11 @@ class GPlayHttpClient @Inject constructor( } private fun processRequest(request: Request): PlayResponse { var response: Response? = null return try { val call = okHttpClient.newCall(request) buildPlayResponse(call.execute()) response = call.execute() buildPlayResponse(response) } catch (e: Exception) { // TODO: exception will be thrown for all apis when all gplay api implementation // will handle the exceptions. this will be done in following issue. Loading @@ -171,6 +176,8 @@ class GPlayHttpClient @Inject constructor( is SocketTimeoutException -> handleExceptionOnGooglePlayRequest(e) else -> handleExceptionOnGooglePlayRequest(e) } } finally { response?.close() } } Loading @@ -195,12 +202,23 @@ class GPlayHttpClient @Inject constructor( code = response.code Timber.d("$TAG: Url: ${response.request.url}\nStatus: $code") if (code == 401) { MainScope().launch { when (code) { STATUS_CODE_UNAUTHORIZED -> MainScope().launch { EventBus.invokeEvent( AppEvent.InvalidAuthEvent(AuthObject.GPlayAuth::class.java.simpleName) ) } STATUS_CODE_TOO_MANY_REQUESTS -> MainScope().launch { cache.evictAll() if (response.request.url.toString().contains(SEARCH_SUGGEST)) { return@launch } EventBus.invokeEvent( AppEvent.TooManyRequests() ) } } // TODO: exception will be thrown for all apis when all gplay api implementation Loading app/src/main/java/foundation/e/apps/data/login/LoginViewModel.kt +4 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import foundation.e.apps.data.enums.User import foundation.e.apps.ui.parentFragment.LoadingViewModel import kotlinx.coroutines.launch import javax.inject.Inject import okhttp3.Cache /** * ViewModel to handle all login related operations. Loading @@ -33,6 +34,7 @@ import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( private val loginSourceRepository: LoginSourceRepository, private val cache: Cache, ) : ViewModel() { /** Loading Loading @@ -120,6 +122,7 @@ class LoginViewModel @Inject constructor( } authObjects.postValue(authObjectsLocal) cache.evictAll() } /** Loading @@ -127,6 +130,7 @@ class LoginViewModel @Inject constructor( */ fun logout() { viewModelScope.launch { cache.evictAll() loginSourceRepository.logout() authObjects.postValue(listOf()) } Loading app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt +2 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,8 @@ class PreferenceManagerModule @Inject constructor( fun isPWASelected() = preferenceManager.getBoolean(PREFERENCE_SHOW_PWA, true) fun isGplaySelected() = preferenceManager.getBoolean(PREFERENCE_SHOW_GPLAY, true) fun disableGplay() = preferenceManager.edit().putBoolean(PREFERENCE_SHOW_GPLAY, false).apply() fun autoUpdatePreferred(): Boolean { return preferenceManager.getBoolean("updateInstallAuto", false) } Loading Loading
app/src/main/java/foundation/e/apps/MainActivity.kt +37 −3 Original line number Diff line number Diff line Loading @@ -40,8 +40,10 @@ import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.login.LoginViewModel import foundation.e.apps.data.login.exceptions.GPlayValidationException import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.databinding.ActivityMainBinding import foundation.e.apps.install.updates.UpdatesNotifier import foundation.e.apps.ui.MainActivityViewModel Loading @@ -52,10 +54,12 @@ import foundation.e.apps.ui.setup.signin.SignInViewModel import foundation.e.apps.utils.SystemInfoProvider import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import javax.inject.Inject import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import timber.log.Timber @AndroidEntryPoint class MainActivity : AppCompatActivity() { Loading @@ -65,6 +69,9 @@ class MainActivity : AppCompatActivity() { private val TAG = MainActivity::class.java.simpleName private lateinit var viewModel: MainActivityViewModel @Inject lateinit var preferenceManagerModule: PreferenceManagerModule override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Loading Loading @@ -130,6 +137,13 @@ class MainActivity : AppCompatActivity() { email, SystemInfoProvider.getAppBuildInfo() ) } else if (exception != null) { Timber.e(exception, "Login failed! message: ${exception?.localizedMessage}") ApplicationDialogFragment( title = getString(R.string.sign_in_failed_title), message = getString(R.string.sign_in_failed_desc), positiveButtonText = getString(R.string.ok) ).show(supportFragmentManager, TAG) } } } Loading Loading @@ -207,6 +221,10 @@ class MainActivity : AppCompatActivity() { observeInvalidAuth() } launch { observeTooManyRequests() } launch { observeSignatureMissMatchError() } Loading Loading @@ -277,11 +295,27 @@ class MainActivity : AppCompatActivity() { }.distinctUntilChanged { old, new -> ((old.data is String) && (new.data is String) && old.data == new.data) }.collectLatest { val data = it.data as String validatedAuthObject(it) } } private fun validatedAuthObject(appEvent: AppEvent) { val data = appEvent.data as String if (data.isNotBlank()) { loginViewModel.markInvalidAuthObject(data) } } private suspend fun observeTooManyRequests() { EventBus.events.filter { appEvent -> appEvent is AppEvent.TooManyRequests }.collectLatest { binding.sessionErrorLayout.visibility = View.VISIBLE binding.retrySessionButton.setOnClickListener { binding.sessionErrorLayout.visibility = View.GONE loginViewModel.startLoginFlow(listOf(LoginSourceGPlay::class.java.simpleName)) } } } private fun setupBottomNavItemSelectedListener( Loading
app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +1 −2 Original line number Diff line number Diff line Loading @@ -280,7 +280,6 @@ class FusedApiImpl @Inject constructor( packageSpecificResults ) } return finalSearchResult } Loading Loading @@ -427,7 +426,7 @@ class FusedApiImpl @Inject constructor( ): FusedApp? { try { getApplicationDetails(query, query, authData, Origin.GPLAY).let { if (it.second == ResultStatus.OK) { if (it.second == ResultStatus.OK && it.first.package_name.isNotEmpty()) { return it.first } } Loading
app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt +22 −4 Original line number Diff line number Diff line Loading @@ -45,7 +45,7 @@ import java.util.concurrent.TimeUnit import javax.inject.Inject class GPlayHttpClient @Inject constructor( cache: Cache, private val cache: Cache, ) : IHttpClient { private val POST = "POST" Loading @@ -55,6 +55,9 @@ class GPlayHttpClient @Inject constructor( private const val TAG = "GPlayHttpClient" private const val HTTP_TIMEOUT_IN_SECOND = 10L private const val SEARCH = "search" private const val SEARCH_SUGGEST = "searchSuggest" private const val STATUS_CODE_UNAUTHORIZED = 401 private const val STATUS_CODE_TOO_MANY_REQUESTS = 429 } private val okHttpClient = OkHttpClient().newBuilder() Loading Loading @@ -155,9 +158,11 @@ class GPlayHttpClient @Inject constructor( } private fun processRequest(request: Request): PlayResponse { var response: Response? = null return try { val call = okHttpClient.newCall(request) buildPlayResponse(call.execute()) response = call.execute() buildPlayResponse(response) } catch (e: Exception) { // TODO: exception will be thrown for all apis when all gplay api implementation // will handle the exceptions. this will be done in following issue. Loading @@ -171,6 +176,8 @@ class GPlayHttpClient @Inject constructor( is SocketTimeoutException -> handleExceptionOnGooglePlayRequest(e) else -> handleExceptionOnGooglePlayRequest(e) } } finally { response?.close() } } Loading @@ -195,12 +202,23 @@ class GPlayHttpClient @Inject constructor( code = response.code Timber.d("$TAG: Url: ${response.request.url}\nStatus: $code") if (code == 401) { MainScope().launch { when (code) { STATUS_CODE_UNAUTHORIZED -> MainScope().launch { EventBus.invokeEvent( AppEvent.InvalidAuthEvent(AuthObject.GPlayAuth::class.java.simpleName) ) } STATUS_CODE_TOO_MANY_REQUESTS -> MainScope().launch { cache.evictAll() if (response.request.url.toString().contains(SEARCH_SUGGEST)) { return@launch } EventBus.invokeEvent( AppEvent.TooManyRequests() ) } } // TODO: exception will be thrown for all apis when all gplay api implementation Loading
app/src/main/java/foundation/e/apps/data/login/LoginViewModel.kt +4 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import foundation.e.apps.data.enums.User import foundation.e.apps.ui.parentFragment.LoadingViewModel import kotlinx.coroutines.launch import javax.inject.Inject import okhttp3.Cache /** * ViewModel to handle all login related operations. Loading @@ -33,6 +34,7 @@ import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( private val loginSourceRepository: LoginSourceRepository, private val cache: Cache, ) : ViewModel() { /** Loading Loading @@ -120,6 +122,7 @@ class LoginViewModel @Inject constructor( } authObjects.postValue(authObjectsLocal) cache.evictAll() } /** Loading @@ -127,6 +130,7 @@ class LoginViewModel @Inject constructor( */ fun logout() { viewModelScope.launch { cache.evictAll() loginSourceRepository.logout() authObjects.postValue(listOf()) } Loading
app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt +2 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,8 @@ class PreferenceManagerModule @Inject constructor( fun isPWASelected() = preferenceManager.getBoolean(PREFERENCE_SHOW_PWA, true) fun isGplaySelected() = preferenceManager.getBoolean(PREFERENCE_SHOW_GPLAY, true) fun disableGplay() = preferenceManager.edit().putBoolean(PREFERENCE_SHOW_GPLAY, false).apply() fun autoUpdatePreferred(): Boolean { return preferenceManager.getBoolean("updateInstallAuto", false) } Loading