From 3d1efecb55c931ffe55137bff7e69671ef5cada8 Mon Sep 17 00:00:00 2001 From: Shashank Kumar <126143257+shashankiitbhu@users.noreply.github.com> Date: Mon, 1 Apr 2024 13:53:00 +0530 Subject: [PATCH] Fix - No Precise Error Message After Error Due to Password Change (#5643) * initial commit * Fix No Precise Message After Clicking Review Buttons * Fix For ThanksClient * Fix For Depictions * Fix For Categories * Fix For Description & Coordinates * Fix For Description & Coordinates * Fix For Description & Coordinates * Fix For Mark as Read notifications * resolve conflicts * fix merge conflicts --- .../LocationPickerActivity.java | 36 ++++++-- .../nrw/commons/actions/PageEditClient.kt | 30 +++++-- .../free/nrw/commons/actions/ThanksClient.kt | 15 +++- .../nrw/commons/auth/csrf/CsrfTokenClient.kt | 14 +++- .../free/nrw/commons/delete/DeleteHelper.java | 56 ++++++++----- .../commons/media/MediaDetailFragment.java | 83 +++++++++++++++---- .../notification/NotificationActivity.java | 25 +++++- .../notification/NotificationClient.kt | 7 +- .../nrw/commons/review/ReviewController.java | 27 +++++- .../commons/review/ReviewImageFragment.java | 48 ++++++++--- .../upload/categories/CategoriesContract.java | 6 ++ .../upload/categories/CategoriesPresenter.kt | 45 +++++----- .../categories/UploadCategoriesFragment.java | 20 +++++ .../upload/depicts/DepictsContract.java | 5 ++ .../upload/depicts/DepictsFragment.java | 21 +++++ .../upload/depicts/DepictsPresenter.kt | 16 ++-- .../nrw/commons/wikidata/WikiBaseClient.kt | 7 +- .../commons/wikidata/WikidataEditService.java | 10 ++- 18 files changed, 361 insertions(+), 110 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java index d5b132e71..9a1c5c935 100644 --- a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java @@ -30,9 +30,13 @@ import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.content.ContextCompat; import com.google.android.material.floatingactionbutton.FloatingActionButton; import fr.free.nrw.commons.CameraPosition; +import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; +import fr.free.nrw.commons.auth.SessionManager; +import fr.free.nrw.commons.auth.csrf.CsrfTokenClient; +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException; import fr.free.nrw.commons.coordinates.CoordinateEditHelper; import fr.free.nrw.commons.filepicker.Constants; import fr.free.nrw.commons.kvstore.JsonKvStore; @@ -141,6 +145,8 @@ public class LocationPickerActivity extends BaseActivity implements @Inject LocationServiceManager locationManager; + @Inject + SessionManager sessionManager; /** * Constants @@ -404,13 +410,29 @@ public class LocationPickerActivity extends BaseActivity implements if (media == null) { return; } - compositeDisposable.add(coordinateEditHelper.makeCoordinatesEdit(getApplicationContext(),media, - Latitude, Longitude, Accuracy) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(s -> { - Timber.d("Coordinates are added."); - })); + + try { + compositeDisposable.add( + coordinateEditHelper.makeCoordinatesEdit(getApplicationContext(), media, + Latitude, Longitude, Accuracy) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(s -> { + Timber.d("Coordinates are added."); + })); + } catch (Exception e) { + if (e.getLocalizedMessage().equals(CsrfTokenClient.ANONYMOUS_TOKEN_MESSAGE)) { + final String username = sessionManager.getUserName(); + final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( + this, + getString(R.string.invalid_login_message), + username + ); + + CommonsApplication.getInstance().clearApplicationData( + this, logoutListener); + } + } } /** diff --git a/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt b/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt index 077a3a0bb..0036f2f8e 100644 --- a/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt @@ -1,5 +1,6 @@ package fr.free.nrw.commons.actions +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException import io.reactivex.Observable import io.reactivex.Single import fr.free.nrw.commons.auth.csrf.CsrfTokenClient @@ -28,7 +29,11 @@ class PageEditClient( pageEditInterface.postEdit(pageTitle, summary, text, csrfTokenClient.getTokenBlocking()) .map { editResponse -> editResponse.edit()!!.editSucceeded() } } catch (throwable: Throwable) { - Observable.just(false) + if (throwable is InvalidLoginTokenException) { + throw throwable + } else { + Observable.just(false) + } } } @@ -44,7 +49,11 @@ class PageEditClient( pageEditInterface.postAppendEdit(pageTitle, summary, appendText, csrfTokenClient.getTokenBlocking()) .map { editResponse -> editResponse.edit()!!.editSucceeded() } } catch (throwable: Throwable) { - Observable.just(false) + if (throwable is InvalidLoginTokenException) { + throw throwable + } else { + Observable.just(false) + } } } @@ -58,12 +67,17 @@ class PageEditClient( fun prependEdit(pageTitle: String, prependText: String, summary: String): Observable { return try { pageEditInterface.postPrependEdit(pageTitle, summary, prependText, csrfTokenClient.getTokenBlocking()) - .map { editResponse -> editResponse.edit()!!.editSucceeded() } + .map { editResponse -> editResponse.edit()?.editSucceeded() ?: false } } catch (throwable: Throwable) { - Observable.just(false) + if (throwable is InvalidLoginTokenException) { + throw throwable + } else { + Observable.just(false) + } } } + /** * Set new labels to Wikibase server of commons * @param summary Edit summary @@ -79,7 +93,11 @@ class PageEditClient( value, csrfTokenClient.getTokenBlocking() ).map { it.success } } catch (throwable: Throwable) { - Observable.just(0) + if (throwable is InvalidLoginTokenException) { + throw throwable + } else { + Observable.just(0) + } } } @@ -93,4 +111,4 @@ class PageEditClient( it.query()?.pages()?.get(0)?.revisions()?.get(0)?.content() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt b/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt index 2401c9bad..06ad63d25 100644 --- a/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt @@ -4,6 +4,8 @@ import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF import io.reactivex.Observable import fr.free.nrw.commons.auth.csrf.CsrfTokenClient +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException +import fr.free.nrw.commons.auth.login.LoginFailedException import javax.inject.Inject import javax.inject.Named import javax.inject.Singleton @@ -32,8 +34,15 @@ class ThanksClient @Inject constructor( ).map { mwThankPostResponse -> mwThankPostResponse.result?.success == 1 } - } catch (throwable: Throwable) { - Observable.just(false) + } + catch (throwable: Throwable) { + if (throwable is InvalidLoginTokenException) { + Observable.error(throwable) + } + else { + Observable.just(false) + } } } -} \ No newline at end of file + +} diff --git a/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt b/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt index f4141c170..9e3136237 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt @@ -48,16 +48,19 @@ class CsrfTokenClient( token = response.body()!!.query()!!.csrfToken()!! if (sessionManager.isUserLoggedIn && token == ANON_TOKEN) { - throw RuntimeException("App believes we're logged in, but got anonymous token.") + throw InvalidLoginTokenException(ANONYMOUS_TOKEN_MESSAGE) } break - } catch (t: Throwable) { + } catch (e: LoginFailedException) { + throw InvalidLoginTokenException(ANONYMOUS_TOKEN_MESSAGE) + } + catch (t: Throwable) { Timber.w(t) } } if (token.isEmpty() || token == ANON_TOKEN) { - throw IOException(INVALID_TOKEN_ERROR_MESSAGE) + throw InvalidLoginTokenException(ANONYMOUS_TOKEN_MESSAGE) } return token } @@ -68,7 +71,7 @@ class CsrfTokenClient( override fun success(token: String?) { if (sessionManager.isUserLoggedIn && token == ANON_TOKEN) { retryWithLogin(cb) { - RuntimeException("App believes we're logged in, but got anonymous token.") + InvalidLoginTokenException(ANONYMOUS_TOKEN_MESSAGE) } } else { cb.success(token) @@ -161,5 +164,8 @@ class CsrfTokenClient( private const val MAX_RETRIES = 1 private const val MAX_RETRIES_OF_LOGIN_BLOCKING = 2 const val INVALID_TOKEN_ERROR_MESSAGE = "Invalid token, or login failure." + const val ANONYMOUS_TOKEN_MESSAGE = "App believes we're logged in, but got anonymous token." } } +class InvalidLoginTokenException(message: String) : Exception(message) + diff --git a/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.java b/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.java index 8d807aadb..02f7d418e 100644 --- a/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.java +++ b/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.java @@ -13,6 +13,7 @@ import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; import fr.free.nrw.commons.actions.PageEditClient; +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException; import fr.free.nrw.commons.notification.NotificationHelper; import fr.free.nrw.commons.review.ReviewController; import fr.free.nrw.commons.utils.ViewUtilWrapper; @@ -66,7 +67,13 @@ public class DeleteHelper { return delete(media, reason) .flatMapSingle(result -> Single.just(showDeletionNotification(context, media, result))) - .firstOrError(); + .firstOrError() + .onErrorResumeNext(throwable -> { + if (throwable instanceof InvalidLoginTokenException) { + return Single.error(throwable); + } + return Single.error(throwable); + }); } /** @@ -104,22 +111,30 @@ public class DeleteHelper { } return pageEditClient.prependEdit(media.getFilename(), fileDeleteString + "\n", summary) - .flatMap(result -> { - if (result) { - return pageEditClient.edit("Commons:Deletion_requests/" + media.getFilename(), subpageString + "\n", summary); - } - throw new RuntimeException("Failed to nominate for deletion"); - }).flatMap(result -> { - if (result) { - return pageEditClient.appendEdit("Commons:Deletion_requests/" + date, logPageString + "\n", summary); - } - throw new RuntimeException("Failed to nominate for deletion"); - }).flatMap(result -> { - if (result) { - return pageEditClient.appendEdit("User_Talk:" + creator, userPageString + "\n", summary); - } - throw new RuntimeException("Failed to nominate for deletion"); - }); + .onErrorResumeNext(throwable -> { + if (throwable instanceof InvalidLoginTokenException) { + return Observable.error(throwable); + } + return Observable.error(throwable); + }) + .flatMap(result -> { + if (result) { + return pageEditClient.edit("Commons:Deletion_requests/" + media.getFilename(), subpageString + "\n", summary); + } + return Observable.error(new RuntimeException("Failed to nominate for deletion")); + }) + .flatMap(result -> { + if (result) { + return pageEditClient.appendEdit("Commons:Deletion_requests/" + date, logPageString + "\n", summary); + } + return Observable.error(new RuntimeException("Failed to nominate for deletion")); + }) + .flatMap(result -> { + if (result) { + return pageEditClient.appendEdit("User_Talk:" + creator, userPageString + "\n", summary); + } + return Observable.error(new RuntimeException("Failed to nominate for deletion")); + }); } private boolean showDeletionNotification(Context context, Media media, boolean result) { @@ -226,14 +241,15 @@ public class DeleteHelper { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(aBoolean -> { - if (aBoolean) { - reviewCallback.onSuccess(); + reviewCallback.onSuccess(); + }, throwable -> { + if (throwable instanceof InvalidLoginTokenException) { + reviewCallback.onTokenException((InvalidLoginTokenException) throwable); } else { reviewCallback.onFailure(); } reviewCallback.enableButtons(); }); - }); alert.setNegativeButton(context.getString(R.string.cancel), (dialog, which) -> reviewCallback.onFailure()); d = alert.create(); diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index 249494caa..e3fdf7dec 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -49,6 +49,7 @@ import com.facebook.imagepipeline.image.ImageInfo; import com.facebook.imagepipeline.request.ImageRequest; import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.CameraPosition; +import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.LocationPicker.LocationPicker; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.MediaDataExtractor; @@ -57,6 +58,8 @@ import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.actions.ThanksClient; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.SessionManager; +import fr.free.nrw.commons.auth.csrf.CsrfTokenClient; +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException; import fr.free.nrw.commons.category.CategoryClient; import fr.free.nrw.commons.category.CategoryDetailsActivity; import fr.free.nrw.commons.category.CategoryEditHelper; @@ -780,9 +783,23 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements firstRevision.getRevisionId())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe((result) -> { - displayThanksToast(context, result); - }, Timber::e); + .subscribe(result -> { + displayThanksToast(getContext(), result); + }, throwable -> { + if (throwable instanceof InvalidLoginTokenException) { + final String username = sessionManager.getUserName(); + final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( + getActivity(), + requireActivity().getString(R.string.invalid_login_message), + username + ); + + CommonsApplication.getInstance().clearApplicationData( + requireActivity(), logoutListener); + } else { + Timber.e(throwable); + } + }); } /** @@ -1056,13 +1073,28 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements if (requestCode == REQUEST_CODE_EDIT_DESCRIPTION && resultCode == RESULT_OK) { final String updatedWikiText = data.getStringExtra(UPDATED_WIKITEXT); - compositeDisposable.add(descriptionEditHelper.addDescription(getContext(), media, - updatedWikiText) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(s -> { - Timber.d("Descriptions are added."); - })); + + try { + compositeDisposable.add(descriptionEditHelper.addDescription(getContext(), media, + updatedWikiText) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(s -> { + Timber.d("Descriptions are added."); + })); + } catch (Exception e) { + if (e.getLocalizedMessage().equals(CsrfTokenClient.ANONYMOUS_TOKEN_MESSAGE)) { + final String username = sessionManager.getUserName(); + final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( + getActivity(), + requireActivity().getString(R.string.invalid_login_message), + username + ); + + CommonsApplication.getInstance().clearApplicationData( + requireActivity(), logoutListener); + } + } final ArrayList uploadMediaDetails = data.getParcelableArrayListExtra(LIST_OF_DESCRIPTION_AND_CAPTION); @@ -1070,14 +1102,29 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements LinkedHashMap updatedCaptions = new LinkedHashMap<>(); for (UploadMediaDetail mediaDetail: uploadMediaDetails) { - compositeDisposable.add(descriptionEditHelper.addCaption(getContext(), media, - mediaDetail.getLanguageCode(), mediaDetail.getCaptionText()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(s -> { - updateCaptions(mediaDetail, updatedCaptions); - Timber.d("Caption is added."); - })); + try { + compositeDisposable.add(descriptionEditHelper.addCaption(getContext(), media, + mediaDetail.getLanguageCode(), mediaDetail.getCaptionText()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(s -> { + updateCaptions(mediaDetail, updatedCaptions); + Timber.d("Caption is added."); + })); + + } catch (Exception e) { + if (e.getLocalizedMessage().equals(CsrfTokenClient.ANONYMOUS_TOKEN_MESSAGE)) { + final String username = sessionManager.getUserName(); + final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( + getActivity(), + requireActivity().getString(R.string.invalid_login_message), + username + ); + + CommonsApplication.getInstance().clearApplicationData( + requireActivity(), logoutListener); + } + } } binding.progressBarEdit.setVisibility(GONE); binding.descriptionEdit.setVisibility(VISIBLE); diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java index c0c1f967b..75a010ea7 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java @@ -12,9 +12,12 @@ import android.view.View; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import com.google.android.material.snackbar.Snackbar; +import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.databinding.ActivityNotificationBinding; +import fr.free.nrw.commons.auth.SessionManager; +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException; import fr.free.nrw.commons.notification.models.Notification; import fr.free.nrw.commons.theme.BaseActivity; import fr.free.nrw.commons.utils.NetworkUtils; @@ -41,6 +44,9 @@ public class NotificationActivity extends BaseActivity { @Inject NotificationController controller; + @Inject + SessionManager sessionManager; + private static final String TAG_NOTIFICATION_WORKER_FRAGMENT = "NotificationWorkerFragment"; private NotificationWorkerFragment mNotificationWorkerFragment; private NotificatinAdapter adapter; @@ -107,10 +113,23 @@ public class NotificationActivity extends BaseActivity { ViewUtil.showLongToast(this,getString(R.string.some_error)); } }, throwable -> { + if (throwable instanceof InvalidLoginTokenException) { + final String username = sessionManager.getUserName(); + final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( + this, + getString(R.string.invalid_login_message), + username + ); - Timber.e(throwable, "Error occurred while loading notifications"); - throwable.printStackTrace(); + CommonsApplication.getInstance().clearApplicationData( + this, logoutListener); + } else { + Timber.e(throwable, "Error occurred while loading notifications"); + throwable.printStackTrace(); ViewUtil.showShortSnackbar(binding.container, R.string.error_notifications); + binding.progressBar.setVisibility(View.GONE); + ViewUtil.showShortSnackbar(binding.container, R.string.error_notifications); + } binding.progressBar.setVisibility(View.GONE); }); compositeDisposable.add(disposable); @@ -170,7 +189,7 @@ public class NotificationActivity extends BaseActivity { } binding.progressBar.setVisibility(View.GONE); }, throwable -> { - Timber.e(throwable, "Error occurred while loading notifications"); + Timber.e(throwable, "Error occurred while loading notifications "); ViewUtil.showShortSnackbar(binding.container, R.string.error_notifications); binding.progressBar.setVisibility(View.GONE); })); diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt b/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt index 399b5ae09..6e29575ea 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt @@ -6,6 +6,7 @@ import fr.free.nrw.commons.notification.models.NotificationType import io.reactivex.Observable import io.reactivex.Single import fr.free.nrw.commons.auth.csrf.CsrfTokenClient +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse import fr.free.nrw.commons.utils.DateUtil import javax.inject.Inject @@ -39,7 +40,11 @@ class NotificationClient @Inject constructor( unreadList = "" ).map(MwQueryResponse::success) } catch (throwable: Throwable) { - Observable.just(false) + if (throwable is InvalidLoginTokenException) { + Observable.error(throwable) + } else { + Observable.just(false) + } } } diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewController.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.java index ae69607d1..e3d5b2256 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewController.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.java @@ -9,6 +9,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; +import fr.free.nrw.commons.auth.SessionManager; +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException; import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage; import java.util.ArrayList; @@ -39,6 +41,9 @@ public class ReviewController { protected static ArrayList categories; @Inject ThanksClient thanksClient; + + @Inject + SessionManager sessionManager; private final DeleteHelper deleteHelper; @Nullable MwQueryPage.Revision firstRevision; // TODO: maybe we can expand this class to include fileName @@ -155,9 +160,23 @@ public class ReviewController { Observable.defer((Callable>) () -> thanksClient.thank(firstRevision.getRevisionId())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe((result) -> { - displayThanksToast(context,result); - }, Timber::e); + .subscribe(result -> { + displayThanksToast(context, result); + }, throwable -> { + if (throwable instanceof InvalidLoginTokenException) { + final String username = sessionManager.getUserName(); + final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( + activity, + activity.getString(R.string.invalid_login_message), + username + ); + + CommonsApplication.getInstance().clearApplicationData( + activity, logoutListener); + } else { + Timber.e(throwable); + } + }); } @SuppressLint("StringFormatInvalid") @@ -192,6 +211,8 @@ public class ReviewController { void onFailure(); + void onTokenException(Exception e); + void disableButtons(); void enableButtons(); diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java index 950e4cf12..7e0cd0ee3 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java @@ -7,15 +7,17 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; - +import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.auth.SessionManager; +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException; import fr.free.nrw.commons.databinding.FragmentReviewImageBinding; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; public class ReviewImageFragment extends CommonsDaggerSupportFragment { @@ -28,7 +30,8 @@ public class ReviewImageFragment extends CommonsDaggerSupportFragment { private FragmentReviewImageBinding binding; - + @Inject + SessionManager sessionManager; // Constant variable used to store user's key name for onSaveInstanceState method @@ -37,20 +40,20 @@ public class ReviewImageFragment extends CommonsDaggerSupportFragment { // Variable that stores the value of user private String user; - public void update(int position) { + public void update(final int position) { this.position = position; } private String updateCategoriesQuestion() { - Media media = getReviewActivity().getMedia(); + final Media media = getReviewActivity().getMedia(); if (media != null && media.getCategoriesHiddenStatus() != null && isAdded()) { // Filter category name attribute from all categories - List categories = new ArrayList<>(); - for(String key : media.getCategoriesHiddenStatus().keySet()) { + final List categories = new ArrayList<>(); + for(final String key : media.getCategoriesHiddenStatus().keySet()) { String value = String.valueOf(key); // Each category returned has a format like "Category:" // so remove the prefix "Category:" - int index = key.indexOf("Category:"); + final int index = key.indexOf("Category:"); if(index == 0) { value = key.substring(9); } @@ -59,7 +62,7 @@ public class ReviewImageFragment extends CommonsDaggerSupportFragment { String catString = TextUtils.join(", ", categories); if (catString != null && !catString.equals("") && binding.tvReviewQuestionContext != null) { catString = "" + catString + ""; - String stringToConvertHtml = String.format(getResources().getString(R.string.review_category_explanation), catString); + final String stringToConvertHtml = String.format(getResources().getString(R.string.review_category_explanation), catString); return Html.fromHtml(stringToConvertHtml).toString(); } } @@ -67,17 +70,20 @@ public class ReviewImageFragment extends CommonsDaggerSupportFragment { } @Override - public void onCreate(Bundle savedInstanceState) { + public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { position = getArguments().getInt("position"); binding = FragmentReviewImageBinding.inflate(inflater, container, false); - String question, explanation=null, yesButtonText, noButtonText; + final String question; + String explanation=null; + String yesButtonText; + final String noButtonText; binding.buttonYes.setOnClickListener(view -> onYesButtonClicked()); @@ -182,6 +188,22 @@ public class ReviewImageFragment extends CommonsDaggerSupportFragment { //do nothing } + @Override + public void onTokenException(final Exception e) { + if (e instanceof InvalidLoginTokenException){ + final String username = sessionManager.getUserName(); + final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( + getActivity(), + requireActivity().getString(R.string.invalid_login_message), + username + ); + + CommonsApplication.getInstance().clearApplicationData( + requireActivity(), logoutListener); + + } + } + /** * This function is called when an image is being loaded * to disable the review buttons diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesContract.java b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesContract.java index 2a661632b..dc53e1a93 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesContract.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesContract.java @@ -56,6 +56,12 @@ public interface CategoriesContract { * Refreshes the categories */ void refreshCategories(); + + + /** + * Navigate the user to Login Activity + */ + void navigateToLoginScreen(); } interface UserActionListener extends BasePresenter { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.kt b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.kt index 836541716..1ce7c7cdd 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import fr.free.nrw.commons.Media import fr.free.nrw.commons.R +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException import fr.free.nrw.commons.category.CategoryEditHelper import fr.free.nrw.commons.category.CategoryItem import fr.free.nrw.commons.di.CommonsApplicationModule @@ -211,25 +212,31 @@ class CategoriesPresenter @Inject constructor( if (selectedCategories.isNotEmpty()) { view.showProgressDialog() - compositeDisposable.add( - categoryEditHelper.makeCategoryEdit(view.fragmentContext, media, - selectedCategories, wikiText) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - Timber.d("Categories are added.") - media.addedCategories = selectedCategories - repository.cleanup() - view.dismissProgressDialog() - view.refreshCategories() - view.goBackToPreviousScreen() - }) - { - Timber.e( - "Failed to update categories" - ) - } - ) + + try { + compositeDisposable.add( + categoryEditHelper.makeCategoryEdit( + view.fragmentContext, media, + selectedCategories, wikiText + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + Timber.d("Categories are added.") + media.addedCategories = selectedCategories + repository.cleanup() + view.dismissProgressDialog() + view.refreshCategories() + view.goBackToPreviousScreen() + }, { + Timber.e( + "Failed to update categories" + ) + }) + ) + } catch (e: InvalidLoginTokenException) { + view.navigateToLoginScreen(); + } } } else { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java index 688a9969e..8503b1d05 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java @@ -19,8 +19,10 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import com.jakewharton.rxbinding2.view.RxView; import com.jakewharton.rxbinding2.widget.RxTextView; +import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.category.CategoryItem; import fr.free.nrw.commons.contributions.ContributionsFragment; import fr.free.nrw.commons.databinding.UploadCategoriesFragmentBinding; @@ -41,6 +43,8 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate @Inject CategoriesContract.UserActionListener presenter; + @Inject + SessionManager sessionManager; private UploadCategoryAdapter adapter; private Disposable subscribe; /** @@ -295,6 +299,22 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate mediaDetailFragment.updateCategories(); } + /** + * + */ + @Override + public void navigateToLoginScreen() { + final String username = sessionManager.getUserName(); + final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( + getActivity(), + requireActivity().getString(R.string.invalid_login_message), + username + ); + + CommonsApplication.getInstance().clearApplicationData( + requireActivity(), logoutListener); + } + public void onNextButtonClicked() { if (media != null) { presenter.updateCategories(media, wikiText); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsContract.java b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsContract.java index 1705330cc..1ff79ab27 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsContract.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsContract.java @@ -73,6 +73,11 @@ public interface DepictsContract { * Update the depictions */ void updateDepicts(); + + /** + * Navigate the user to Login Activity + */ + void navigateToLoginScreen(); } interface UserActionListener extends BasePresenter { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsFragment.java index d227b2d95..bd52a8d35 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsFragment.java @@ -17,8 +17,10 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import com.jakewharton.rxbinding2.view.RxView; import com.jakewharton.rxbinding2.widget.RxTextView; +import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.contributions.ContributionsFragment; import fr.free.nrw.commons.databinding.UploadDepictsFragmentBinding; import fr.free.nrw.commons.kvstore.JsonKvStore; @@ -63,6 +65,9 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra private UploadDepictsFragmentBinding binding; + @Inject + SessionManager sessionManager; + @Nullable @Override public android.view.View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @@ -311,6 +316,22 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra mediaDetailFragment.onResume(); } + /** + * Navigates to the login Activity + */ + @Override + public void navigateToLoginScreen() { + final String username = sessionManager.getUserName(); + final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( + getActivity(), + requireActivity().getString(R.string.invalid_login_message), + username + ); + + CommonsApplication.getInstance().clearApplicationData( + requireActivity(), logoutListener); + } + /** * Determines the calling fragment by media nullability and act accordingly */ diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsPresenter.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsPresenter.kt index ab4d76ca0..edcda24f0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsPresenter.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import fr.free.nrw.commons.bookmarks.items.BookmarkItemsController import fr.free.nrw.commons.Media +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException import fr.free.nrw.commons.di.CommonsApplicationModule import fr.free.nrw.commons.repository.UploadRepository import fr.free.nrw.commons.upload.structure.depictions.DepictedItem @@ -218,13 +219,18 @@ class DepictsPresenter @Inject constructor( view.dismissProgressDialog() view.updateDepicts() view.goBackToPreviousScreen() + }, { error -> + if (error is InvalidLoginTokenException) { + view.navigateToLoginScreen(); + } else { + Timber.e( + "Failed to update depictions" + ) + } }) - { - Timber.e( - "Failed to update depictions" - ) - } ) + + } } else { repository.cleanup() diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikiBaseClient.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/WikiBaseClient.kt index 22bb6d8b2..f6f8b9ade 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikiBaseClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikiBaseClient.kt @@ -59,11 +59,6 @@ class WikiBaseClient @Inject constructor( } private fun csrfToken(): Observable = Observable.fromCallable { - try { - csrfTokenClient.getTokenBlocking() - } catch (throwable: Throwable) { - Timber.e(throwable) - "" - } + csrfTokenClient.getTokenBlocking() } } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java index ae3537354..ca2093e26 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java @@ -8,6 +8,7 @@ import android.content.Context; import androidx.annotation.Nullable; import com.google.gson.Gson; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException; import fr.free.nrw.commons.contributions.Contribution; import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.upload.UploadResult; @@ -123,8 +124,13 @@ public class WikidataEditService { } }) .doOnError(throwable -> { - Timber.e(throwable, "Error occurred while setting DEPICTS property"); - ViewUtil.showLongToast(context, throwable.toString()); + if (throwable instanceof InvalidLoginTokenException) { + Observable.error(throwable); + } else { + Timber.e(throwable, "Error occurred while setting DEPICTS property"); + ViewUtil.showLongToast(context, throwable.toString()); + } + }) .subscribeOn(Schedulers.io()); }