Getting reCaptcha Android - android

I trying to get reCaptcha-code for send to Firebase server. Read here, implement Executor to Activity and write this code at onClick:
override fun onClick(view: View?) {
SafetyNet.getClient(this).verifyWithRecaptcha("Here i write my API Key")
.addOnSuccessListener(this as Executor, OnSuccessListener { response ->
val userResponseToken = response.tokenResult
Log.d("TAG", userResponseToken.toString())
})
.addOnFailureListener(this as Executor, OnFailureListener { e ->
if (e is ApiException) {
Log.d("TAG", "Error: ${CommonStatusCodes.getStatusCodeString(e.statusCode)}")
} else {
Log.d("TAG", "Error: ${e.message}")
})
Logs are not printing when I click button and call onClick method, but this method works. Tell me please, what I do wrong?

I have tested this and it works without using the as Executor like the below example:
SafetyNet.getClient(this)
.verifyWithRecaptcha("MY API KEY")
.addOnSuccessListener(this, OnSuccessListener { response ->
// Indicates communication with reCAPTCHA service was successful.
val userResponseToken = response.tokenResult
if (response.tokenResult?.isNotEmpty() == true) {
// Validate the user response token using the
// reCAPTCHA siteverify API.
}
})
.addOnFailureListener(this, OnFailureListener { e ->
if (e is ApiException) {
// An error occurred when communicating with the
// reCAPTCHA service. Refer to the status code to
// handle the error appropriately.
Log.d("TAG", "Error: ${CommonStatusCodes.getStatusCodeString(e.statusCode)}")
} else {
// A different, unknown type of error occurred.
Log.d("TAG", "Error: ${e.message}")
}
})

Related

Android Kotlin-Flow: Retrofit Call Fails with No Exception thrown

I'm trying to make a GET request to my server from my Android application using Retrofit, OKHttp, and Kotlin Flow w/ MVVM architecture.
For some reason whenever I try to invoke my GET request from a try-catch scope the program always enters catch, but the value of Throwable is always null. It's as if there was a crash but no exception being thrown for me to examine in the logs/debugger.
Filtering logcat for OKHTTP logs I can see that the network request never occurs/reaches the server. It seems to be failing locally, somewhere in my device's app process, before triggering the catch block.
Other network calls execute fine in this same project, so something about my specific implementation for this one must be incorrect. What am I missing?
RemoteDataSource.kt
suspend fun getProductData(skuId: String): Result<ProductLookupResponse>{
return getResponse(
request = {
pickingAPI.lookupProductBySku(
"Test User",
"A place",
skuId
)
},
defaultErrorMessage = "Error looking up product"
)
}
private suspend fun <T> getResponse(
request: suspend () -> Response<T>,
defaultErrorMessage: String
): Result<T> {
return try {
val result = request.invoke() //Always Crashes here for this request
if (result.isSuccessful) {
Result.success(result.body())
} else {
val networkError = NetworkError(code = result.code(), message = result.message())
Result.error(message = networkError.message ?: "", error = networkError)
}
} catch (e: Exception) {
Result.error(defaultErrorMessage, null)
}
}
Repo.kt
suspend fun getProductData(
skuId: String
): Flow<Result<ProductLookupResponse>> {
return flow {
emit(Result.loading())
emit(RemoteDataSource.getProductData(skuId))
}.flowOn(Dispatchers.IO)
}
API.kt
#GET("garments/sku/{skuId}")
fun lookupProductBySku(
#Header(HEADER_ASSOCIATE_ID) userUniqueId: String,
#Header(HEADER_LOCATION_ID) dcId: String,
#Path("skuId") sku: String
): Response<ProductLookupResponse>

Kotlin coroutines - gracefully handling errors from suspend functions

Trying to implement graceful handling of the errors with suspend functions that are called from async methods, How to catch the error thrown by a suspend method.
suspend fun findById(id: Long): User? {
throw Exception("my exception") // intentionally throwing to simulate error situation.
return userModel.findById(id) // IO, may throw an error
}
Caller piece, launching with IO thread
GlobalScope.launch(Dispatchers.IO) {
try {
var userAsync: Deferred<User?>? = null
arguments?.getLong("id")?.let {
userAsync = async { viewModel?.findById(it) } // async for efficiency as i've other async methods too.
}
val data = userAsync?.await()
withContext(Dispatchers.Main) {
user = data // data binding, populating UI fields of user
}
} catch (exception: Exception) {
withContext(Dispatchers.Main) { fault(exception) }
}
}
fault method
private fun fault(exception: Exception) {
Log.d("User", "fault: ${exception.localizedMessage}") // expecting output
}
Currently runtime is crashing, want to implement graceful handling of errors.
Attempt 2
Tried placing try catch within the async block but it didn't like it.
var userAsync: Deferred<UserVO?>? = null
arguments?.getLong("id")?.let {
userAsync = async {
try {
delegate?.findById(it)
} catch (e: Exception) {
print(e)
}
}
}
I would use a CoroutineExceptionHandler to make your coroutines fail gracefully:
1) Define the handler:
val exceptionHandler = CoroutineExceptionHandler { context, error ->
// Do what you want with the error
Log.d(TAG, error)
}
2) Refactor your findById function to be executed within an IO context and make your ui code main safe:
suspend fun findById(id : Int) : User? = withContext(Dispatchers.IO){
when(id){
0 -> throw Exception("not valid")
else -> return#withContext User(id)
}
}
Launch your job within MainScope (and so update the ui), passing exceptionHandler to launch coroutine builder in order to catch the exception:
val exceptionHandler = CoroutineExceptionHandler { _, error ->
// Do what you want with the error
Log.d(TAG, error)
}
MainScope().launch(exceptionHandler) {
val user = delegate?.findById(userId)
user?.let {
Timber.v(it.toString())
}
}

Handling RxJava onErrorReturn and OnErrorNotImplementedException

So i'm trying to implement MVI pattern in android with RxJava, but i want to handle the thrown error in a state, together with success and loading, is there anyway to handle the error not from subscribe(onError = xxx)
PROCESS
sealed class AuthResult : MviResult {
sealed class LoadUserResult : AuthResult() {
object Loading : LoadUserResult()
data class Success(val user: User) : LoadUserResult()
data class Fail(val error: Throwable) : LoadUserResult()
}
}
private val loadUser =
ObservableTransformer<LoadUserAction, LoadUserResult> { actions ->
actions.flatMap {
userManager.getCurrentUser()
.map<LoadUserResult> { LoadUserResult.Success(it) }
.onErrorReturn(LoadUserResult::Fail) // HERE? // EDIT FOR THE ANSWER: REMOVE THIS
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.startWith(LoadUserResult.Loading)
}.onErrorReturn(LoadUserResult::Fail) // ANSWER: ADD THIS TO CATCH API ERROR
}
var actionProcess =
ObservableTransformer<AuthAction, AuthResult> { actions ->
actions.publish { s->
Observable.merge(
s.ofType(LoadUserAction::class.java).compose(loadUser),
s.ofType(SignInWithGoogleAction::class.java).compose(signInWithGoogle)
)
}
}
VIEWMODEL
fun combine(): Observable<AuthViewState> {
return _intents
.map(this::actionFromIntent)
.compose(actionProcess)
.scan(AuthViewState.idle(), reducer)
.distinctUntilChanged()
.replay(1)
.autoConnect(0)
}
FRAGMENT
disposable.add(viewModel.combine().subscribe(this::response))
private fun response(state: AuthViewState) {
val user = state.user
if (user.uid.isBlank() && user.email.isBlank() && user.username.isBlank()) {
Timber.i("user: $user")
} else {
Timber.i("user: $user")
Toast.makeText(requireContext(), "Will navigate to MainActivity", Toast.LENGTH_SHORT)
.show()
}
// HANDLE THE ERROR HERE?
if (state.error != null) {
Toast.makeText(requireContext(), "Error fetching user", Toast.LENGTH_SHORT).show()
Timber.e("Error loading user ${state.error.localizedMessage}")
}
}
THE ERROR i got was
2020-06-03 22:42:15.073 25060-25060/com.xxx W/System.err: io.reactivex.exceptions.OnErrorNotImplementedException: The exception was not handled due to missing onError handler in the subscribe() method call. Further reading: https://github.com/ReactiveX/RxJava/wiki/Error-Handling | com.google.android.gms.tasks.RuntimeExecutionException: com.google.android.gms.common.api.ApiException: 10:
The error you're receiving here is due to you calling .subscribe() in your Fragment. That variant of the .subscribe() (the one that accepts only one parameter -- the onNext consumer callback) will only notify the consumer when the stream successfully emits an item (in this case, AuthViewState). However, when you observable stream encounters an error, RxJava doesn't have a good way to handle it, since an error callback was not provided in .subscribe(). Therefore, it throws the error you've encountered above.
NOTE: RxJava has many overloads of Observable.subscribe(), some of which accept a consumer callback for error handling.
However, if your goal is to have the Observable always successfully emit an AuthViewState, even if an error was encountered, you could make use of Observable.onErrorReturn() (or a similar error handling function provided by RxJava). An example usage of that would be:
sealed class ViewState {
object Loading : ViewState()
data class Success(val username: String) : ViewState()
data class Error(val error: Throwable) : ViewState()
}
class UserProfileViewModel(
private val userService: UserService
) {
fun getViewState(): Observable<ViewState> {
return Observable
.merge(
Observable.just(ViewState.Loading),
userService
.getUserFromApi()
.map { user -> ViewState.Success(user.username) }
)
.onErrorReturn { error -> ViewState.Error(error) }
}
}

How to parse error response to Throwable?

I am using Retrofit and RxJava to make network requests like this:
How I am declaring request:
#POST("auth/profile/edit/")
fun updateProfile(#Body body: ProfileUpdateBody): Single<Response<Void>>
How I am calling:
api.updateProfile(**some data**)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
Log.d("----------", "Subscribed!")
}
.doOnSuccess {
if(it.isSuccessful)
Log.d("----------", "Success!")
else
Log.d("----------", "Not Successfull!")
}
.doOnError {
Log.d("----------", "Error Happened!")
}
.subscribe({
}, {
})
Some code have been dropped for readability. The probem is even though I get responses back with 401 or 400 statuses, doOnSuccess is being called. Should not the doOnError be called here? I am confused.
As a result my logact is showing "Not Successful" message. How can make sure that doOnErro is called when I get responses back with 401 or 400 statuses?
Or can I parse the incoming response to Throwable and call doOnError() function?
Change the Retrofit API call to return Completable:
#POST("auth/profile/edit/")
fun updateProfile(#Body body: ProfileUpdateBody): Completable
then handle the "success case" via doOnComplete:
api.updateProfile(**some data**)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
Log.d("----------", "Subscribed!")
}
.doOnComplete {
Log.d("----------", "Success!")
}
.doOnError {
Log.d("----------", "Error Happened!")
}
.subscribe({ }, { })
The real question is, why would you want to throw and exception when the request fails?
The correct processes are being followed here, doOnSuccess is being called as intended because the request has returned a response without encountering an exception being thrown. Regardless of whether the request's response is successful or not.
You should handle the state of your response accordingly and not throw arbitrary exceptions for it:
api.updateProfile(**some data**)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> {
if (response.isSuccessful()) {
// handle success
} else {
// handle failure
}
}, t -> {
// handle thrown error
yourErrorHandlerMethod(t);
})
The response you getting is correct, the response is shown in doOnSuccess cuz the API you hitting got successfully hit, no matter what was the response code.
doOnError is called when actual API call is failed like network drop in the middle or some server-side issues.
Or can I parse the incoming response to Throwable and call doOnError() function?
You cant do this instead, you can handle the response in doOnSuccess as
try {
api.updateProfile(**some data**)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
Log.d("----------", "Subscribed!")
}
.doOnSuccess {
if(it.isSuccessful) // responce code = 200/201
Log.d("----------", "Success!")
else if (it.responseCode == 400 ){
Log.d("----------", "Not Found!")
// Call a method that handles this according to your requirement.
PageNotFoundHandler();
// OPTIONAL throw new UserException();
}
else if (it.responseCode == 401 ){
Log.d("----------", "Not Authorised!")
// Call a method that handles this according to your requirement.
TokenExpiredHandler(); //OR
UnAuthorizedAccessHandler();
// OPTIONAL throw new UserException();
}
else {
Log.d("----------", "Some another Error!")
// Call a method that handles this according to your requirement.
// OPTIONAL throw new UserException();
}
}
.doOnError {
Log.d("----------", "Error Happened!")
}
.subscribe({
}, {
})
} catch
{
ErrorHandler();
}
Or can I parse the incoming response to Throwable and call doOnError() function?
As you mention that you want a throwable, you can achieve it by using the try-catch block.
Just throw a custom EXCEPTION, you have to create a new Custom Exception class for it.

How to process 400 response in RxJava2

I am struggling with correct exception handling in RxJava2. I am trying to refresh my token inside an OkHttp3 interceptor like this:
tokenRepository.refreshToken(authStateManager.current.refreshToken!!)
.doOnError {
Log.w(TAG, "Could not obtain new access token.")
authStateManager.signOut()
}
.subscribe { tokenResponse ->
Log.d(TAG, "Obtained new access token: " + tokenResponse.toString())
authStateManager.updateAfterTokenResponse(TokenResponse.jsonDeserialize(tokenResponse.toString()), null)
token = authStateManager.current.accessToken
}
It works nicely if the refresh token is valid and the request returns 200. But if the refresh token is invalidated and I get some error code (for example 400), the doOnError block is executed but it then proceeds to the subscribe block where the following exception is thrown:
retrofit2.adapter.rxjava2.HttpException: HTTP 400
at retrofit2.adapter.rxjava2.BodyObservable$BodyObserver.onNext(BodyObservable.java:54)
at retrofit2.adapter.rxjava2.BodyObservable$BodyObserver.onNext(BodyObservable.java:37)
...
I tried using onErrorReturn and onErrorResumeNext but I would like to skip the subscribe block completely (it there is an error, just log user out and don't try to do anything). Is it possible? The refreshToken method returns Single<JsonObject> type of response.
Can't you use the onError in the subscription? I like using RxKotlin for this. For example, I can do the following:
.subscribeBy(onSuccess = {
// do Something in on success
}, onError = {
// do something in onError
})
So in your case, it could be something like:
tokenRepository.refreshToken(authStateManager.current.refreshToken!!)
.subscribeBy(onSuccess = {
Log.d(TAG, "Obtained new access token: " + it.toString())
authStateManager.updateAfterTokenResponse(TokenResponse.jsonDeserialize(it.toString()), null)
token = authStateManager.current.accessToken
}, onError = {
Log.w(TAG, "Could not obtain new access token.")
// the variable "it" here is a throwable which means you can determine if it's a RetrofitException and what status code is returned
})
However, if you do not want to use RxKotlin, you can handle your Rx subscriptions like this in Kotlin:
.subscribe({ Log.d(TAG, "Obtained new access token: " + it.toString()) },
{ Log.e(TAG, "Error is ${it.message}") })
You can handle the error in the second part of the function where the Log.e is being used.
I just had the same problem but I fixed it now. I don't know if my explanation is correct but it seems like doOnError doesn't handle the exception.
You need to use the subscribe() method that takes an onError Consumer as a parameter and handle the error there:
subscribe(Consumer<? super T> onSuccess, Consumer<? super Throwable> onError)
In Java it would look like this:
.subscribe(tokenResponse -> {
Log.d(TAG, "Obtained new access token: " + tokenResponse.toString());
authStateManager.updateAfterTokenResponse(TokenResponse.jsonDeserialize(tokenResponse.toString()), null);
token = authStateManager.current.accessToken;
}, throwable -> {
Log.w(TAG, "Could not obtain new access token.");
authStateManager.signOut();
}));

Categories

Resources