Skip to content

Commit 2a6ab66

Browse files
Precise error message password change (commons-app#5544)
* Precise Error Message and Action When Password is Changed * Make message persistent * code cleanup and fixes * removed unnecessary string resource * removed unnecessary string resource * fix * fix * Upload Client to kotlin * Remove Redundant Code * Remove Redundant Code * code cleanup and Improvements * Improved Java doc * Improved Java doc * Improved Javadoc
1 parent 152e824 commit 2a6ab66

File tree

9 files changed

+171
-54
lines changed

9 files changed

+171
-54
lines changed

app/src/main/java/fr/free/nrw/commons/CommonsApplication.java

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
import static org.acra.ReportField.USER_COMMENT;
1010

1111
import android.annotation.SuppressLint;
12+
import android.app.Activity;
1213
import android.app.NotificationChannel;
1314
import android.app.NotificationManager;
1415
import android.content.Context;
16+
import android.content.Intent;
1517
import android.database.sqlite.SQLiteDatabase;
1618
import android.database.sqlite.SQLiteException;
1719
import android.os.Build;
@@ -22,6 +24,7 @@
2224
import com.facebook.drawee.backends.pipeline.Fresco;
2325
import com.facebook.imagepipeline.core.ImagePipeline;
2426
import com.facebook.imagepipeline.core.ImagePipelineConfig;
27+
import fr.free.nrw.commons.auth.LoginActivity;
2528
import fr.free.nrw.commons.auth.SessionManager;
2629
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table;
2730
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
@@ -33,6 +36,7 @@
3336
import fr.free.nrw.commons.data.DBOpenHelper;
3437
import fr.free.nrw.commons.di.ApplicationlessInjection;
3538
import fr.free.nrw.commons.kvstore.JsonKvStore;
39+
import fr.free.nrw.commons.language.AppLanguageLookUpTable;
3640
import fr.free.nrw.commons.logging.FileLoggingTree;
3741
import fr.free.nrw.commons.logging.LogUtils;
3842
import fr.free.nrw.commons.media.CustomOkHttpNetworkFetcher;
@@ -57,7 +61,6 @@
5761
import org.acra.annotation.AcraDialog;
5862
import org.acra.annotation.AcraMailSender;
5963
import org.acra.data.StringFormat;
60-
import fr.free.nrw.commons.language.AppLanguageLookUpTable;
6164
import timber.log.Timber;
6265

6366
@AcraCore(
@@ -82,6 +85,9 @@
8285

8386
public class CommonsApplication extends MultiDexApplication {
8487

88+
public static final String loginMessageIntentKey = "loginMessage";
89+
public static final String loginUsernameIntentKey = "loginUsername";
90+
8591
public static final String IS_LIMITED_CONNECTION_MODE_ENABLED = "is_limited_connection_mode_enabled";
8692
@Inject
8793
SessionManager sessionManager;
@@ -137,12 +143,12 @@ public AppLanguageLookUpTable getLanguageLookUpTable() {
137143
ContributionDao contributionDao;
138144

139145
/**
140-
* In-memory list of contributions whose uploads have been paused by the user
146+
* In-memory list of contributions whose uploads have been paused by the user
141147
*/
142148
public static Map<String, Boolean> pauseUploads = new HashMap<>();
143149

144150
/**
145-
* In-memory list of uploads that have been cancelled by the user
151+
* In-memory list of uploads that have been cancelled by the user
146152
*/
147153
public static HashSet<String> cancelledUploads = new HashSet<>();
148154

@@ -339,4 +345,96 @@ public interface LogoutListener {
339345

340346
void onLogoutComplete();
341347
}
348+
349+
/**
350+
* This listener is responsible for handling post-logout actions, specifically invoking the LoginActivity
351+
* with relevant intent parameters. It does not perform the actual logout operation.
352+
*/
353+
public static class BaseLogoutListener implements CommonsApplication.LogoutListener {
354+
355+
Context ctx;
356+
String loginMessage, userName;
357+
358+
/**
359+
* Constructor for BaseLogoutListener.
360+
*
361+
* @param ctx Application context
362+
*/
363+
public BaseLogoutListener(final Context ctx) {
364+
this.ctx = ctx;
365+
}
366+
367+
/**
368+
* Constructor for BaseLogoutListener
369+
*
370+
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
371+
* @param loginMessage Message to be displayed on the login page
372+
* @param loginUsername Username to be pre-filled on the login page
373+
*/
374+
public BaseLogoutListener(final Context ctx, final String loginMessage,
375+
final String loginUsername) {
376+
this.ctx = ctx;
377+
this.loginMessage = loginMessage;
378+
this.userName = loginUsername;
379+
}
380+
381+
@Override
382+
public void onLogoutComplete() {
383+
Timber.d("Logout complete callback received.");
384+
final Intent loginIntent = new Intent(ctx, LoginActivity.class);
385+
loginIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
386+
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
387+
388+
if (loginMessage != null) {
389+
loginIntent.putExtra(loginMessageIntentKey, loginMessage);
390+
}
391+
if (userName != null) {
392+
loginIntent.putExtra(loginUsernameIntentKey, userName);
393+
}
394+
395+
ctx.startActivity(loginIntent);
396+
}
397+
}
398+
399+
/**
400+
* This class is an extension of BaseLogoutListener, providing additional functionality or customization
401+
* for the logout process. It includes specific actions to be taken during logout, such as handling redirection to the login screen.
402+
*/
403+
public static class ActivityLogoutListener extends BaseLogoutListener {
404+
405+
Activity activity;
406+
407+
408+
/**
409+
* Constructor for ActivityLogoutListener.
410+
*
411+
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
412+
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
413+
*/
414+
public ActivityLogoutListener(final Activity activity, final Context ctx) {
415+
super(ctx);
416+
this.activity = activity;
417+
}
418+
419+
/**
420+
* Constructor for ActivityLogoutListener with additional parameters for the login screen.
421+
*
422+
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
423+
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
424+
* @param loginMessage Message to be displayed on the login page after logout.
425+
* @param loginUsername Username to be pre-filled on the login page after logout.
426+
*/
427+
public ActivityLogoutListener(final Activity activity, final Context ctx,
428+
final String loginMessage, final String loginUsername) {
429+
super(activity, loginMessage, loginUsername);
430+
this.activity = activity;
431+
}
432+
433+
@Override
434+
public void onLogoutComplete() {
435+
super.onLogoutComplete();
436+
activity.finish();
437+
}
438+
}
342439
}
440+

app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import androidx.appcompat.app.AppCompatDelegate;
2525
import androidx.core.app.NavUtils;
2626
import androidx.core.content.ContextCompat;
27-
2827
import fr.free.nrw.commons.auth.login.LoginClient;
2928
import fr.free.nrw.commons.auth.login.LoginResult;
3029
import fr.free.nrw.commons.databinding.ActivityLoginBinding;
@@ -51,6 +50,8 @@
5150
import static android.view.KeyEvent.KEYCODE_ENTER;
5251
import static android.view.View.VISIBLE;
5352
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
53+
import static fr.free.nrw.commons.CommonsApplication.loginMessageIntentKey;
54+
import static fr.free.nrw.commons.CommonsApplication.loginUsernameIntentKey;
5455

5556
public class LoginActivity extends AccountAuthenticatorActivity {
5657

@@ -93,6 +94,9 @@ public void onCreate(Bundle savedInstanceState) {
9394
binding = ActivityLoginBinding.inflate(getLayoutInflater());
9495
setContentView(binding.getRoot());
9596

97+
String message = getIntent().getStringExtra(loginMessageIntentKey);
98+
String username = getIntent().getStringExtra(loginUsernameIntentKey);
99+
96100
binding.loginUsername.addTextChangedListener(textWatcher);
97101
binding.loginPassword.addTextChangedListener(textWatcher);
98102
binding.loginTwoFactor.addTextChangedListener(textWatcher);
@@ -111,6 +115,12 @@ public void onCreate(Bundle savedInstanceState) {
111115
} else {
112116
binding.loginCredentials.setVisibility(View.GONE);
113117
}
118+
if (message != null) {
119+
showMessage(message, R.color.secondaryDarkColor);
120+
}
121+
if (username != null) {
122+
binding.loginUsername.setText(username);
123+
}
114124
}
115125
/**
116126
* Hides the keyboard if the user's focus is not on the password (hasFocus is false).

app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class CsrfTokenClient(
2323
private var retries = 0
2424
private var csrfTokenCall: Call<MwQueryResponse?>? = null
2525

26+
2627
@Throws(Throwable::class)
2728
fun getTokenBlocking(): String {
2829
var token = ""
@@ -56,7 +57,7 @@ class CsrfTokenClient(
5657
}
5758

5859
if (token.isEmpty() || token == ANON_TOKEN) {
59-
throw IOException("Invalid token, or login failure.")
60+
throw IOException(INVALID_TOKEN_ERROR_MESSAGE)
6061
}
6162
return token
6263
}
@@ -159,5 +160,6 @@ class CsrfTokenClient(
159160
private const val ANON_TOKEN = "+\\"
160161
private const val MAX_RETRIES = 1
161162
private const val MAX_RETRIES_OF_LOGIN_BLOCKING = 2
163+
const val INVALID_TOKEN_ERROR_MESSAGE = "Invalid token, or login failure."
162164
}
163165
}

app/src/main/java/fr/free/nrw/commons/navtab/MoreBottomSheetFragment.java

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
import fr.free.nrw.commons.AboutActivity;
2020
import fr.free.nrw.commons.BuildConfig;
2121
import fr.free.nrw.commons.CommonsApplication;
22+
import fr.free.nrw.commons.CommonsApplication.ActivityLogoutListener;
2223
import fr.free.nrw.commons.R;
2324
import fr.free.nrw.commons.WelcomeActivity;
2425
import fr.free.nrw.commons.actions.PageEditClient;
25-
import fr.free.nrw.commons.auth.LoginActivity;
2626
import fr.free.nrw.commons.databinding.FragmentMoreBottomSheetBinding;
2727
import fr.free.nrw.commons.di.ApplicationlessInjection;
2828
import fr.free.nrw.commons.feedback.FeedbackContentCreator;
@@ -41,7 +41,6 @@
4141
import java.util.concurrent.Callable;
4242
import javax.inject.Inject;
4343
import javax.inject.Named;
44-
import timber.log.Timber;
4544

4645
public class MoreBottomSheetFragment extends BottomSheetDialogFragment {
4746

@@ -122,7 +121,7 @@ protected void onLogoutClicked() {
122121
.setPositiveButton(R.string.yes, (dialog, which) -> {
123122
final CommonsApplication app = (CommonsApplication)
124123
requireContext().getApplicationContext();
125-
app.clearApplicationData(requireContext(), new BaseLogoutListener());
124+
app.clearApplicationData(requireContext(), new ActivityLogoutListener(requireActivity(), getContext()));
126125
})
127126
.setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel())
128127
.show();
@@ -221,19 +220,5 @@ protected void onProfileClicked() {
221220
protected void onPeerReviewClicked() {
222221
ReviewActivity.startYourself(getActivity(), getString(R.string.title_activity_review));
223222
}
224-
225-
private class BaseLogoutListener implements CommonsApplication.LogoutListener {
226-
227-
@Override
228-
public void onLogoutComplete() {
229-
Timber.d("Logout complete callback received.");
230-
final Intent nearbyIntent = new Intent(
231-
getContext(), LoginActivity.class);
232-
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
233-
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
234-
startActivity(nearbyIntent);
235-
requireActivity().finish();
236-
}
237-
}
238223
}
239224

app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@
7070
import com.jakewharton.rxbinding3.appcompat.RxSearchView;
7171
import fr.free.nrw.commons.BaseMarker;
7272
import fr.free.nrw.commons.CommonsApplication;
73+
import fr.free.nrw.commons.CommonsApplication.BaseLogoutListener;
7374
import fr.free.nrw.commons.MapController.NearbyPlacesInfo;
7475
import fr.free.nrw.commons.R;
7576
import fr.free.nrw.commons.Utils;
76-
import fr.free.nrw.commons.auth.LoginActivity;
7777
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
7878
import fr.free.nrw.commons.contributions.ContributionController;
7979
import fr.free.nrw.commons.contributions.MainActivity;
@@ -1372,8 +1372,7 @@ public void displayLoginSkippedWarning() {
13721372
.setMessage(R.string.login_alert_message)
13731373
.setPositiveButton(R.string.login, (dialog, which) -> {
13741374
// logout of the app
1375-
BaseLogoutListener logoutListener = new BaseLogoutListener();
1376-
CommonsApplication app = (CommonsApplication) getActivity().getApplication();
1375+
BaseLogoutListener logoutListener = new BaseLogoutListener(getActivity()); CommonsApplication app = (CommonsApplication) getActivity().getApplication();
13771376
app.clearApplicationData(getContext(), logoutListener);
13781377
})
13791378
.show();
@@ -1419,18 +1418,6 @@ public boolean backButtonClicked() {
14191418
* onLogoutComplete is called after shared preferences and data stored in local database are
14201419
* cleared.
14211420
*/
1422-
private class BaseLogoutListener implements CommonsApplication.LogoutListener {
1423-
1424-
@Override
1425-
public void onLogoutComplete() {
1426-
Timber.d("Logout complete callback received.");
1427-
final Intent nearbyIntent = new Intent(getActivity(), LoginActivity.class);
1428-
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
1429-
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1430-
startActivity(nearbyIntent);
1431-
getActivity().finish();
1432-
}
1433-
}
14341421

14351422
@Override
14361423
public void setFABPlusAction(final View.OnClickListener onClickListener) {

app/src/main/java/fr/free/nrw/commons/upload/StashUploadResult.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ package fr.free.nrw.commons.upload
22

33
data class StashUploadResult(
44
val state: StashUploadState,
5-
val fileKey: String?
5+
val fileKey: String?,
6+
val errorMessage : String?
67
)
78

89
enum class StashUploadState {

app/src/main/java/fr/free/nrw/commons/upload/UploadClient.kt

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class UploadClient @Inject constructor(
5454
): Observable<StashUploadResult> {
5555
if (contribution.isCompleted()) {
5656
return Observable.just(
57-
StashUploadResult(StashUploadState.SUCCESS, contribution.fileKey)
57+
StashUploadResult(StashUploadState.SUCCESS, contribution.fileKey,null)
5858
)
5959
}
6060

@@ -76,12 +76,13 @@ class UploadClient @Inject constructor(
7676

7777
val index = AtomicInteger()
7878
val failures = AtomicBoolean()
79+
val errorMessage = AtomicReference<String>()
7980
compositeDisposable.add(
8081
Observable.fromIterable(fileChunks).forEach { chunkFile: File ->
8182
if (canProcess(contribution, failures)) {
8283
processChunk(
8384
filename, contribution, notificationUpdater, chunkFile,
84-
failures, chunkInfo, index, mediaType!!, file!!, fileChunks.size
85+
failures, chunkInfo, index, errorMessage, mediaType!!, file!!, fileChunks.size
8586
)
8687
}
8788
}
@@ -90,24 +91,25 @@ class UploadClient @Inject constructor(
9091
return when {
9192
contribution.isPaused() -> {
9293
Timber.d("Upload stash paused %s", contribution.pageId)
93-
Observable.just(StashUploadResult(StashUploadState.PAUSED, null))
94+
Observable.just(StashUploadResult(StashUploadState.PAUSED, null, null))
9495
}
9596
failures.get() -> {
9697
Timber.d("Upload stash contains failures %s", contribution.pageId)
97-
Observable.just(StashUploadResult(StashUploadState.FAILED, null))
98+
Observable.just(StashUploadResult(StashUploadState.FAILED, null, errorMessage.get()))
9899
}
99100
chunkInfo.get() != null -> {
100101
Timber.d("Upload stash success %s", contribution.pageId)
101102
Observable.just(
102103
StashUploadResult(
103104
StashUploadState.SUCCESS,
104-
chunkInfo.get()!!.uploadResult!!.filekey
105+
chunkInfo.get()!!.uploadResult!!.filekey,
106+
"success"
105107
)
106108
)
107109
}
108110
else -> {
109111
Timber.d("Upload stash failed %s", contribution.pageId)
110-
Observable.just(StashUploadResult(StashUploadState.FAILED, null))
112+
Observable.just(StashUploadResult(StashUploadState.FAILED, null,null))
111113
}
112114
}
113115
}
@@ -116,7 +118,7 @@ class UploadClient @Inject constructor(
116118
filename: String, contribution: Contribution,
117119
notificationUpdater: NotificationUpdateProgressListener, chunkFile: File,
118120
failures: AtomicBoolean, chunkInfo: AtomicReference<ChunkInfo?>, index: AtomicInteger,
119-
mediaType: MediaType, file: File, totalChunks: Int
121+
errorMessage : AtomicReference<String>, mediaType: MediaType, file: File, totalChunks: Int
120122
) {
121123
if (shouldSkip(chunkInfo, index)) {
122124
index.incrementAndGet()
@@ -150,6 +152,7 @@ class UploadClient @Inject constructor(
150152
notificationUpdater.onChunkUploaded(contribution, chunkInfo.get())
151153
}, { throwable: Throwable? ->
152154
Timber.e(throwable, "Received error in chunk upload")
155+
errorMessage.set(throwable?.message)
153156
failures.set(true)
154157
}
155158
)

0 commit comments

Comments
 (0)