Notify user when they are ratelimited

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2023-04-23 16:21:40 +05:30
parent 2daa434dea
commit 2e024150de
6 changed files with 105 additions and 7 deletions

View File

@@ -28,7 +28,10 @@ import android.view.KeyEvent
import android.view.View import android.view.View
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.TextView import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.aurora.Constants import com.aurora.Constants
import com.aurora.extensions.close import com.aurora.extensions.close
import com.aurora.extensions.open import com.aurora.extensions.open
@@ -48,6 +51,7 @@ import com.aurora.store.view.ui.downloads.DownloadActivity
import com.aurora.store.view.ui.sheets.FilterSheet import com.aurora.store.view.ui.sheets.FilterSheet
import com.aurora.store.viewmodel.search.SearchResultViewModel import com.aurora.store.viewmodel.search.SearchResultViewModel
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import kotlinx.coroutines.launch
class SearchResultsActivity : BaseActivity(), OnSharedPreferenceChangeListener { class SearchResultsActivity : BaseActivity(), OnSharedPreferenceChangeListener {
@@ -101,6 +105,18 @@ class SearchResultsActivity : BaseActivity(), OnSharedPreferenceChangeListener {
query?.let { query?.let {
updateQuery(it) updateQuery(it)
} }
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
VM.responseCode.collect {
when (it) {
429 -> attachErrorLayout(getString(R.string.rate_limited))
else -> detachErrorLayout()
}
}
}
}
} }
override fun onDestroy() { override fun onDestroy() {
@@ -110,6 +126,19 @@ class SearchResultsActivity : BaseActivity(), OnSharedPreferenceChangeListener {
super.onDestroy() super.onDestroy()
} }
private fun attachErrorLayout(message: String) {
B.recycler.visibility = View.GONE
B.filterFab.visibility = View.GONE
B.errorLayout.visibility = View.VISIBLE
B.errorMessage.text = message
}
private fun detachErrorLayout() {
B.recycler.visibility = View.VISIBLE
B.filterFab.visibility = View.VISIBLE
B.errorLayout.visibility = View.GONE
}
private fun attachToolbar() { private fun attachToolbar() {
searchView = B.layoutViewToolbar.inputSearch searchView = B.layoutViewToolbar.inputSearch
@@ -253,4 +282,4 @@ class SearchResultsActivity : BaseActivity(), OnSharedPreferenceChangeListener {
} }
} }
} }
} }

View File

@@ -21,18 +21,25 @@ package com.aurora.store.viewmodel
import android.app.Application import android.app.Application
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.aurora.store.data.RequestState import com.aurora.store.data.RequestState
import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.NetworkProvider import com.aurora.store.data.providers.NetworkProvider
import com.aurora.store.util.Log import com.aurora.store.util.Log
import com.google.android.material.snackbar.Snackbar
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
abstract class BaseAndroidViewModel(application: Application) : AndroidViewModel(application), abstract class BaseAndroidViewModel(application: Application) : AndroidViewModel(application),
NetworkProvider.NetworkListener { NetworkProvider.NetworkListener {
private lateinit var networkListener: NetworkProvider.NetworkListener private lateinit var networkListener: NetworkProvider.NetworkListener
val responseCode = HttpClient.getPreferredClient().responseCode
protected val gson: Gson = GsonBuilder() protected val gson: Gson = GsonBuilder()
.excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC) .excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC)
.create() .create()
@@ -45,6 +52,9 @@ abstract class BaseAndroidViewModel(application: Application) : AndroidViewModel
requestState = RequestState.Init requestState = RequestState.Init
NetworkProvider.addListener(this) NetworkProvider.addListener(this)
// Start collecting response code for requests
responseCode.launchIn(viewModelScope)
} }
abstract fun observe() abstract fun observe()
@@ -77,4 +87,4 @@ abstract class BaseAndroidViewModel(application: Application) : AndroidViewModel
NetworkProvider.removeListener(this) NetworkProvider.removeListener(this)
super.onCleared() super.onCleared()
} }
} }

View File

@@ -20,7 +20,6 @@
package com.aurora.store.viewmodel.search package com.aurora.store.viewmodel.search
import android.app.Application import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.AuthData
@@ -29,11 +28,13 @@ import com.aurora.gplayapi.helpers.SearchHelper
import com.aurora.store.data.network.HttpClient import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.AuthProvider import com.aurora.store.data.providers.AuthProvider
import com.aurora.extensions.flushAndAdd import com.aurora.extensions.flushAndAdd
import com.aurora.store.data.RequestState
import com.aurora.store.viewmodel.BaseAndroidViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.supervisorScope
class SearchResultViewModel(application: Application) : AndroidViewModel(application) { class SearchResultViewModel(application: Application) : BaseAndroidViewModel(application) {
private val authData: AuthData = AuthProvider private val authData: AuthData = AuthProvider
.with(application) .with(application)
@@ -91,4 +92,8 @@ class SearchResultViewModel(application: Application) : AndroidViewModel(applica
} }
} }
} }
}
override fun observe() {
requestState = RequestState.Init
}
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022, Google LLC
~
~ Aurora Store 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 2 of the License, or
~ (at your option) any later version.
~
~ Aurora Store 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 Aurora Store. If not, see <http://www.gnu.org/licenses/>.
~
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="@color/colorAccent">
<path
android:fillColor="@android:color/white"
android:pathData="M261,638Q273,638 282,629Q291,620 291,608Q291,596 282,587Q273,578 261,578Q249,578 240,587Q231,596 231,608Q231,620 240,629Q249,638 261,638ZM231,508L291,508L291,317L231,317L231,508ZM410,588L729,588L729,528L410,528L410,588ZM410,417L729,417L729,357L410,357L410,417ZM132,800Q108,800 90,782Q72,764 72,740L72,220Q72,196 90,178Q108,160 132,160L828,160Q852,160 870,178Q888,196 888,220L888,740Q888,764 870,782Q852,800 828,800L132,800ZM132,740L828,740Q828,740 828,740Q828,740 828,740L828,220Q828,220 828,220Q828,220 828,220L132,220Q132,220 132,220Q132,220 132,220L132,740Q132,740 132,740Q132,740 132,740ZM132,740L132,740Q132,740 132,740Q132,740 132,740L132,220Q132,220 132,220Q132,220 132,220L132,220Q132,220 132,220Q132,220 132,220L132,740Q132,740 132,740Q132,740 132,740Z"/>
</vector>

View File

@@ -40,6 +40,29 @@
app:itemSpacing="@dimen/margin_normal" /> app:itemSpacing="@dimen/margin_normal" />
</RelativeLayout> </RelativeLayout>
<LinearLayout
android:id="@+id/error_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center|center_vertical"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="96dp"
android:layout_height="96dp"
android:contentDescription="@string/error"
android:src="@drawable/ic_problem" />
<TextView
android:id="@+id/error_message"
style="@style/TextAppearance.Aurora.SubTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rate_limited" />
</LinearLayout>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/filter_fab" android:id="@+id/filter_fab"
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon" style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
@@ -53,4 +76,4 @@
app:backgroundTint="@color/colorAccent" app:backgroundTint="@color/colorAccent"
app:icon="@drawable/ic_filter" app:icon="@drawable/ic_filter"
app:iconTint="@color/colorWhite" /> app:iconTint="@color/colorWhite" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -400,4 +400,6 @@
<string name="ui_accent_13">Deep blue</string> <string name="ui_accent_13">Deep blue</string>
<string name="update_available">update available</string> <string name="update_available">update available</string>
<string name="updates_available">updates available</string> <string name="updates_available">updates available</string>
</resources> <string name="rate_limited">Oops, This account is rate limited!</string>
<string name="error">An error occurred!</string>
</resources>