mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 04:43:54 +01:00
Precise error message password change (#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
This commit is contained in:
parent
152e824aa6
commit
2a6ab66c11
9 changed files with 171 additions and 54 deletions
|
|
@ -9,9 +9,11 @@ import static org.acra.ReportField.STACK_TRACE;
|
||||||
import static org.acra.ReportField.USER_COMMENT;
|
import static org.acra.ReportField.USER_COMMENT;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
@ -22,6 +24,7 @@ import androidx.multidex.MultiDexApplication;
|
||||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||||
import com.facebook.imagepipeline.core.ImagePipeline;
|
import com.facebook.imagepipeline.core.ImagePipeline;
|
||||||
import com.facebook.imagepipeline.core.ImagePipelineConfig;
|
import com.facebook.imagepipeline.core.ImagePipelineConfig;
|
||||||
|
import fr.free.nrw.commons.auth.LoginActivity;
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table;
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table;
|
||||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
||||||
|
|
@ -33,6 +36,7 @@ import fr.free.nrw.commons.contributions.ContributionDao;
|
||||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||||
|
import fr.free.nrw.commons.language.AppLanguageLookUpTable;
|
||||||
import fr.free.nrw.commons.logging.FileLoggingTree;
|
import fr.free.nrw.commons.logging.FileLoggingTree;
|
||||||
import fr.free.nrw.commons.logging.LogUtils;
|
import fr.free.nrw.commons.logging.LogUtils;
|
||||||
import fr.free.nrw.commons.media.CustomOkHttpNetworkFetcher;
|
import fr.free.nrw.commons.media.CustomOkHttpNetworkFetcher;
|
||||||
|
|
@ -57,7 +61,6 @@ import org.acra.annotation.AcraCore;
|
||||||
import org.acra.annotation.AcraDialog;
|
import org.acra.annotation.AcraDialog;
|
||||||
import org.acra.annotation.AcraMailSender;
|
import org.acra.annotation.AcraMailSender;
|
||||||
import org.acra.data.StringFormat;
|
import org.acra.data.StringFormat;
|
||||||
import fr.free.nrw.commons.language.AppLanguageLookUpTable;
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
@AcraCore(
|
@AcraCore(
|
||||||
|
|
@ -82,6 +85,9 @@ import timber.log.Timber;
|
||||||
|
|
||||||
public class CommonsApplication extends MultiDexApplication {
|
public class CommonsApplication extends MultiDexApplication {
|
||||||
|
|
||||||
|
public static final String loginMessageIntentKey = "loginMessage";
|
||||||
|
public static final String loginUsernameIntentKey = "loginUsername";
|
||||||
|
|
||||||
public static final String IS_LIMITED_CONNECTION_MODE_ENABLED = "is_limited_connection_mode_enabled";
|
public static final String IS_LIMITED_CONNECTION_MODE_ENABLED = "is_limited_connection_mode_enabled";
|
||||||
@Inject
|
@Inject
|
||||||
SessionManager sessionManager;
|
SessionManager sessionManager;
|
||||||
|
|
@ -137,12 +143,12 @@ public class CommonsApplication extends MultiDexApplication {
|
||||||
ContributionDao contributionDao;
|
ContributionDao contributionDao;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In-memory list of contributions whose uploads have been paused by the user
|
* In-memory list of contributions whose uploads have been paused by the user
|
||||||
*/
|
*/
|
||||||
public static Map<String, Boolean> pauseUploads = new HashMap<>();
|
public static Map<String, Boolean> pauseUploads = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In-memory list of uploads that have been cancelled by the user
|
* In-memory list of uploads that have been cancelled by the user
|
||||||
*/
|
*/
|
||||||
public static HashSet<String> cancelledUploads = new HashSet<>();
|
public static HashSet<String> cancelledUploads = new HashSet<>();
|
||||||
|
|
||||||
|
|
@ -339,4 +345,96 @@ public class CommonsApplication extends MultiDexApplication {
|
||||||
|
|
||||||
void onLogoutComplete();
|
void onLogoutComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener is responsible for handling post-logout actions, specifically invoking the LoginActivity
|
||||||
|
* with relevant intent parameters. It does not perform the actual logout operation.
|
||||||
|
*/
|
||||||
|
public static class BaseLogoutListener implements CommonsApplication.LogoutListener {
|
||||||
|
|
||||||
|
Context ctx;
|
||||||
|
String loginMessage, userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for BaseLogoutListener.
|
||||||
|
*
|
||||||
|
* @param ctx Application context
|
||||||
|
*/
|
||||||
|
public BaseLogoutListener(final Context ctx) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for BaseLogoutListener
|
||||||
|
*
|
||||||
|
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
|
||||||
|
* @param loginMessage Message to be displayed on the login page
|
||||||
|
* @param loginUsername Username to be pre-filled on the login page
|
||||||
|
*/
|
||||||
|
public BaseLogoutListener(final Context ctx, final String loginMessage,
|
||||||
|
final String loginUsername) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.loginMessage = loginMessage;
|
||||||
|
this.userName = loginUsername;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLogoutComplete() {
|
||||||
|
Timber.d("Logout complete callback received.");
|
||||||
|
final Intent loginIntent = new Intent(ctx, LoginActivity.class);
|
||||||
|
loginIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
|
||||||
|
if (loginMessage != null) {
|
||||||
|
loginIntent.putExtra(loginMessageIntentKey, loginMessage);
|
||||||
|
}
|
||||||
|
if (userName != null) {
|
||||||
|
loginIntent.putExtra(loginUsernameIntentKey, userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.startActivity(loginIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is an extension of BaseLogoutListener, providing additional functionality or customization
|
||||||
|
* for the logout process. It includes specific actions to be taken during logout, such as handling redirection to the login screen.
|
||||||
|
*/
|
||||||
|
public static class ActivityLogoutListener extends BaseLogoutListener {
|
||||||
|
|
||||||
|
Activity activity;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ActivityLogoutListener.
|
||||||
|
*
|
||||||
|
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
|
||||||
|
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
|
||||||
|
*/
|
||||||
|
public ActivityLogoutListener(final Activity activity, final Context ctx) {
|
||||||
|
super(ctx);
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ActivityLogoutListener with additional parameters for the login screen.
|
||||||
|
*
|
||||||
|
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
|
||||||
|
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
|
||||||
|
* @param loginMessage Message to be displayed on the login page after logout.
|
||||||
|
* @param loginUsername Username to be pre-filled on the login page after logout.
|
||||||
|
*/
|
||||||
|
public ActivityLogoutListener(final Activity activity, final Context ctx,
|
||||||
|
final String loginMessage, final String loginUsername) {
|
||||||
|
super(activity, loginMessage, loginUsername);
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLogoutComplete() {
|
||||||
|
super.onLogoutComplete();
|
||||||
|
activity.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
import androidx.core.app.NavUtils;
|
import androidx.core.app.NavUtils;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import fr.free.nrw.commons.auth.login.LoginClient;
|
import fr.free.nrw.commons.auth.login.LoginClient;
|
||||||
import fr.free.nrw.commons.auth.login.LoginResult;
|
import fr.free.nrw.commons.auth.login.LoginResult;
|
||||||
import fr.free.nrw.commons.databinding.ActivityLoginBinding;
|
import fr.free.nrw.commons.databinding.ActivityLoginBinding;
|
||||||
|
|
@ -51,6 +50,8 @@ import timber.log.Timber;
|
||||||
import static android.view.KeyEvent.KEYCODE_ENTER;
|
import static android.view.KeyEvent.KEYCODE_ENTER;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||||
|
import static fr.free.nrw.commons.CommonsApplication.loginMessageIntentKey;
|
||||||
|
import static fr.free.nrw.commons.CommonsApplication.loginUsernameIntentKey;
|
||||||
|
|
||||||
public class LoginActivity extends AccountAuthenticatorActivity {
|
public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
|
|
||||||
|
|
@ -93,6 +94,9 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
binding = ActivityLoginBinding.inflate(getLayoutInflater());
|
binding = ActivityLoginBinding.inflate(getLayoutInflater());
|
||||||
setContentView(binding.getRoot());
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
String message = getIntent().getStringExtra(loginMessageIntentKey);
|
||||||
|
String username = getIntent().getStringExtra(loginUsernameIntentKey);
|
||||||
|
|
||||||
binding.loginUsername.addTextChangedListener(textWatcher);
|
binding.loginUsername.addTextChangedListener(textWatcher);
|
||||||
binding.loginPassword.addTextChangedListener(textWatcher);
|
binding.loginPassword.addTextChangedListener(textWatcher);
|
||||||
binding.loginTwoFactor.addTextChangedListener(textWatcher);
|
binding.loginTwoFactor.addTextChangedListener(textWatcher);
|
||||||
|
|
@ -111,6 +115,12 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
} else {
|
} else {
|
||||||
binding.loginCredentials.setVisibility(View.GONE);
|
binding.loginCredentials.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
if (message != null) {
|
||||||
|
showMessage(message, R.color.secondaryDarkColor);
|
||||||
|
}
|
||||||
|
if (username != null) {
|
||||||
|
binding.loginUsername.setText(username);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Hides the keyboard if the user's focus is not on the password (hasFocus is false).
|
* Hides the keyboard if the user's focus is not on the password (hasFocus is false).
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ class CsrfTokenClient(
|
||||||
private var retries = 0
|
private var retries = 0
|
||||||
private var csrfTokenCall: Call<MwQueryResponse?>? = null
|
private var csrfTokenCall: Call<MwQueryResponse?>? = null
|
||||||
|
|
||||||
|
|
||||||
@Throws(Throwable::class)
|
@Throws(Throwable::class)
|
||||||
fun getTokenBlocking(): String {
|
fun getTokenBlocking(): String {
|
||||||
var token = ""
|
var token = ""
|
||||||
|
|
@ -56,7 +57,7 @@ class CsrfTokenClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.isEmpty() || token == ANON_TOKEN) {
|
if (token.isEmpty() || token == ANON_TOKEN) {
|
||||||
throw IOException("Invalid token, or login failure.")
|
throw IOException(INVALID_TOKEN_ERROR_MESSAGE)
|
||||||
}
|
}
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
@ -159,5 +160,6 @@ class CsrfTokenClient(
|
||||||
private const val ANON_TOKEN = "+\\"
|
private const val ANON_TOKEN = "+\\"
|
||||||
private const val MAX_RETRIES = 1
|
private const val MAX_RETRIES = 1
|
||||||
private const val MAX_RETRIES_OF_LOGIN_BLOCKING = 2
|
private const val MAX_RETRIES_OF_LOGIN_BLOCKING = 2
|
||||||
|
const val INVALID_TOKEN_ERROR_MESSAGE = "Invalid token, or login failure."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||||
import fr.free.nrw.commons.AboutActivity;
|
import fr.free.nrw.commons.AboutActivity;
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
|
import fr.free.nrw.commons.CommonsApplication.ActivityLogoutListener;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.WelcomeActivity;
|
import fr.free.nrw.commons.WelcomeActivity;
|
||||||
import fr.free.nrw.commons.actions.PageEditClient;
|
import fr.free.nrw.commons.actions.PageEditClient;
|
||||||
import fr.free.nrw.commons.auth.LoginActivity;
|
|
||||||
import fr.free.nrw.commons.databinding.FragmentMoreBottomSheetBinding;
|
import fr.free.nrw.commons.databinding.FragmentMoreBottomSheetBinding;
|
||||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||||
import fr.free.nrw.commons.feedback.FeedbackContentCreator;
|
import fr.free.nrw.commons.feedback.FeedbackContentCreator;
|
||||||
|
|
@ -41,7 +41,6 @@ import io.reactivex.schedulers.Schedulers;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class MoreBottomSheetFragment extends BottomSheetDialogFragment {
|
public class MoreBottomSheetFragment extends BottomSheetDialogFragment {
|
||||||
|
|
||||||
|
|
@ -122,7 +121,7 @@ public class MoreBottomSheetFragment extends BottomSheetDialogFragment {
|
||||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||||
final CommonsApplication app = (CommonsApplication)
|
final CommonsApplication app = (CommonsApplication)
|
||||||
requireContext().getApplicationContext();
|
requireContext().getApplicationContext();
|
||||||
app.clearApplicationData(requireContext(), new BaseLogoutListener());
|
app.clearApplicationData(requireContext(), new ActivityLogoutListener(requireActivity(), getContext()));
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel())
|
.setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel())
|
||||||
.show();
|
.show();
|
||||||
|
|
@ -221,19 +220,5 @@ public class MoreBottomSheetFragment extends BottomSheetDialogFragment {
|
||||||
protected void onPeerReviewClicked() {
|
protected void onPeerReviewClicked() {
|
||||||
ReviewActivity.startYourself(getActivity(), getString(R.string.title_activity_review));
|
ReviewActivity.startYourself(getActivity(), getString(R.string.title_activity_review));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BaseLogoutListener implements CommonsApplication.LogoutListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLogoutComplete() {
|
|
||||||
Timber.d("Logout complete callback received.");
|
|
||||||
final Intent nearbyIntent = new Intent(
|
|
||||||
getContext(), LoginActivity.class);
|
|
||||||
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
||||||
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
startActivity(nearbyIntent);
|
|
||||||
requireActivity().finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,10 +70,10 @@ import com.jakewharton.rxbinding2.view.RxView;
|
||||||
import com.jakewharton.rxbinding3.appcompat.RxSearchView;
|
import com.jakewharton.rxbinding3.appcompat.RxSearchView;
|
||||||
import fr.free.nrw.commons.BaseMarker;
|
import fr.free.nrw.commons.BaseMarker;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
|
import fr.free.nrw.commons.CommonsApplication.BaseLogoutListener;
|
||||||
import fr.free.nrw.commons.MapController.NearbyPlacesInfo;
|
import fr.free.nrw.commons.MapController.NearbyPlacesInfo;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.Utils;
|
import fr.free.nrw.commons.Utils;
|
||||||
import fr.free.nrw.commons.auth.LoginActivity;
|
|
||||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
||||||
import fr.free.nrw.commons.contributions.ContributionController;
|
import fr.free.nrw.commons.contributions.ContributionController;
|
||||||
import fr.free.nrw.commons.contributions.MainActivity;
|
import fr.free.nrw.commons.contributions.MainActivity;
|
||||||
|
|
@ -1372,8 +1372,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
||||||
.setMessage(R.string.login_alert_message)
|
.setMessage(R.string.login_alert_message)
|
||||||
.setPositiveButton(R.string.login, (dialog, which) -> {
|
.setPositiveButton(R.string.login, (dialog, which) -> {
|
||||||
// logout of the app
|
// logout of the app
|
||||||
BaseLogoutListener logoutListener = new BaseLogoutListener();
|
BaseLogoutListener logoutListener = new BaseLogoutListener(getActivity()); CommonsApplication app = (CommonsApplication) getActivity().getApplication();
|
||||||
CommonsApplication app = (CommonsApplication) getActivity().getApplication();
|
|
||||||
app.clearApplicationData(getContext(), logoutListener);
|
app.clearApplicationData(getContext(), logoutListener);
|
||||||
})
|
})
|
||||||
.show();
|
.show();
|
||||||
|
|
@ -1419,18 +1418,6 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
||||||
* onLogoutComplete is called after shared preferences and data stored in local database are
|
* onLogoutComplete is called after shared preferences and data stored in local database are
|
||||||
* cleared.
|
* cleared.
|
||||||
*/
|
*/
|
||||||
private class BaseLogoutListener implements CommonsApplication.LogoutListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLogoutComplete() {
|
|
||||||
Timber.d("Logout complete callback received.");
|
|
||||||
final Intent nearbyIntent = new Intent(getActivity(), LoginActivity.class);
|
|
||||||
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
||||||
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
startActivity(nearbyIntent);
|
|
||||||
getActivity().finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setFABPlusAction(final View.OnClickListener onClickListener) {
|
public void setFABPlusAction(final View.OnClickListener onClickListener) {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ package fr.free.nrw.commons.upload
|
||||||
|
|
||||||
data class StashUploadResult(
|
data class StashUploadResult(
|
||||||
val state: StashUploadState,
|
val state: StashUploadState,
|
||||||
val fileKey: String?
|
val fileKey: String?,
|
||||||
|
val errorMessage : String?
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class StashUploadState {
|
enum class StashUploadState {
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class UploadClient @Inject constructor(
|
||||||
): Observable<StashUploadResult> {
|
): Observable<StashUploadResult> {
|
||||||
if (contribution.isCompleted()) {
|
if (contribution.isCompleted()) {
|
||||||
return Observable.just(
|
return Observable.just(
|
||||||
StashUploadResult(StashUploadState.SUCCESS, contribution.fileKey)
|
StashUploadResult(StashUploadState.SUCCESS, contribution.fileKey,null)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,12 +76,13 @@ class UploadClient @Inject constructor(
|
||||||
|
|
||||||
val index = AtomicInteger()
|
val index = AtomicInteger()
|
||||||
val failures = AtomicBoolean()
|
val failures = AtomicBoolean()
|
||||||
|
val errorMessage = AtomicReference<String>()
|
||||||
compositeDisposable.add(
|
compositeDisposable.add(
|
||||||
Observable.fromIterable(fileChunks).forEach { chunkFile: File ->
|
Observable.fromIterable(fileChunks).forEach { chunkFile: File ->
|
||||||
if (canProcess(contribution, failures)) {
|
if (canProcess(contribution, failures)) {
|
||||||
processChunk(
|
processChunk(
|
||||||
filename, contribution, notificationUpdater, chunkFile,
|
filename, contribution, notificationUpdater, chunkFile,
|
||||||
failures, chunkInfo, index, mediaType!!, file!!, fileChunks.size
|
failures, chunkInfo, index, errorMessage, mediaType!!, file!!, fileChunks.size
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -90,24 +91,25 @@ class UploadClient @Inject constructor(
|
||||||
return when {
|
return when {
|
||||||
contribution.isPaused() -> {
|
contribution.isPaused() -> {
|
||||||
Timber.d("Upload stash paused %s", contribution.pageId)
|
Timber.d("Upload stash paused %s", contribution.pageId)
|
||||||
Observable.just(StashUploadResult(StashUploadState.PAUSED, null))
|
Observable.just(StashUploadResult(StashUploadState.PAUSED, null, null))
|
||||||
}
|
}
|
||||||
failures.get() -> {
|
failures.get() -> {
|
||||||
Timber.d("Upload stash contains failures %s", contribution.pageId)
|
Timber.d("Upload stash contains failures %s", contribution.pageId)
|
||||||
Observable.just(StashUploadResult(StashUploadState.FAILED, null))
|
Observable.just(StashUploadResult(StashUploadState.FAILED, null, errorMessage.get()))
|
||||||
}
|
}
|
||||||
chunkInfo.get() != null -> {
|
chunkInfo.get() != null -> {
|
||||||
Timber.d("Upload stash success %s", contribution.pageId)
|
Timber.d("Upload stash success %s", contribution.pageId)
|
||||||
Observable.just(
|
Observable.just(
|
||||||
StashUploadResult(
|
StashUploadResult(
|
||||||
StashUploadState.SUCCESS,
|
StashUploadState.SUCCESS,
|
||||||
chunkInfo.get()!!.uploadResult!!.filekey
|
chunkInfo.get()!!.uploadResult!!.filekey,
|
||||||
|
"success"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Timber.d("Upload stash failed %s", contribution.pageId)
|
Timber.d("Upload stash failed %s", contribution.pageId)
|
||||||
Observable.just(StashUploadResult(StashUploadState.FAILED, null))
|
Observable.just(StashUploadResult(StashUploadState.FAILED, null,null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -116,7 +118,7 @@ class UploadClient @Inject constructor(
|
||||||
filename: String, contribution: Contribution,
|
filename: String, contribution: Contribution,
|
||||||
notificationUpdater: NotificationUpdateProgressListener, chunkFile: File,
|
notificationUpdater: NotificationUpdateProgressListener, chunkFile: File,
|
||||||
failures: AtomicBoolean, chunkInfo: AtomicReference<ChunkInfo?>, index: AtomicInteger,
|
failures: AtomicBoolean, chunkInfo: AtomicReference<ChunkInfo?>, index: AtomicInteger,
|
||||||
mediaType: MediaType, file: File, totalChunks: Int
|
errorMessage : AtomicReference<String>, mediaType: MediaType, file: File, totalChunks: Int
|
||||||
) {
|
) {
|
||||||
if (shouldSkip(chunkInfo, index)) {
|
if (shouldSkip(chunkInfo, index)) {
|
||||||
index.incrementAndGet()
|
index.incrementAndGet()
|
||||||
|
|
@ -150,6 +152,7 @@ class UploadClient @Inject constructor(
|
||||||
notificationUpdater.onChunkUploaded(contribution, chunkInfo.get())
|
notificationUpdater.onChunkUploaded(contribution, chunkInfo.get())
|
||||||
}, { throwable: Throwable? ->
|
}, { throwable: Throwable? ->
|
||||||
Timber.e(throwable, "Received error in chunk upload")
|
Timber.e(throwable, "Received error in chunk upload")
|
||||||
|
errorMessage.set(throwable?.message)
|
||||||
failures.set(true)
|
failures.set(true)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,17 @@ import android.graphics.BitmapFactory
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import androidx.multidex.BuildConfig
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.Data
|
import androidx.work.Data
|
||||||
import androidx.work.WorkerParameters
|
|
||||||
import androidx.multidex.BuildConfig
|
|
||||||
import androidx.work.ForegroundInfo
|
import androidx.work.ForegroundInfo
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
import dagger.android.ContributesAndroidInjector
|
import dagger.android.ContributesAndroidInjector
|
||||||
import fr.free.nrw.commons.CommonsApplication
|
import fr.free.nrw.commons.CommonsApplication
|
||||||
import fr.free.nrw.commons.Media
|
import fr.free.nrw.commons.Media
|
||||||
import fr.free.nrw.commons.R
|
import fr.free.nrw.commons.R
|
||||||
import fr.free.nrw.commons.auth.SessionManager
|
import fr.free.nrw.commons.auth.SessionManager
|
||||||
|
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
|
||||||
import fr.free.nrw.commons.contributions.ChunkInfo
|
import fr.free.nrw.commons.contributions.ChunkInfo
|
||||||
import fr.free.nrw.commons.contributions.Contribution
|
import fr.free.nrw.commons.contributions.Contribution
|
||||||
import fr.free.nrw.commons.contributions.ContributionDao
|
import fr.free.nrw.commons.contributions.ContributionDao
|
||||||
|
|
@ -29,8 +30,8 @@ import fr.free.nrw.commons.customselector.database.UploadedStatusDao
|
||||||
import fr.free.nrw.commons.di.ApplicationlessInjection
|
import fr.free.nrw.commons.di.ApplicationlessInjection
|
||||||
import fr.free.nrw.commons.media.MediaClient
|
import fr.free.nrw.commons.media.MediaClient
|
||||||
import fr.free.nrw.commons.theme.BaseActivity
|
import fr.free.nrw.commons.theme.BaseActivity
|
||||||
import fr.free.nrw.commons.upload.StashUploadResult
|
|
||||||
import fr.free.nrw.commons.upload.FileUtilsWrapper
|
import fr.free.nrw.commons.upload.FileUtilsWrapper
|
||||||
|
import fr.free.nrw.commons.upload.StashUploadResult
|
||||||
import fr.free.nrw.commons.upload.StashUploadState
|
import fr.free.nrw.commons.upload.StashUploadState
|
||||||
import fr.free.nrw.commons.upload.UploadClient
|
import fr.free.nrw.commons.upload.UploadClient
|
||||||
import fr.free.nrw.commons.upload.UploadResult
|
import fr.free.nrw.commons.upload.UploadResult
|
||||||
|
|
@ -46,13 +47,14 @@ import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
|
class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
|
||||||
CoroutineWorker(appContext, workerParams) {
|
CoroutineWorker(appContext, workerParams) {
|
||||||
|
|
||||||
private var notificationManager: NotificationManagerCompat? = null
|
private var notificationManager: NotificationManagerCompat? = null
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var wikidataEditService: WikidataEditService
|
lateinit var wikidataEditService: WikidataEditService
|
||||||
|
|
||||||
|
|
@ -79,6 +81,7 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
|
||||||
private val PROCESSING_UPLOADS_NOTIFICATION_ID = 101
|
private val PROCESSING_UPLOADS_NOTIFICATION_ID = 101
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Attributes of the current-upload notification
|
//Attributes of the current-upload notification
|
||||||
private var currentNotificationID: Int = -1// lateinit is not allowed with primitives
|
private var currentNotificationID: Int = -1// lateinit is not allowed with primitives
|
||||||
private lateinit var currentNotificationTag: String
|
private lateinit var currentNotificationTag: String
|
||||||
|
|
@ -295,7 +298,7 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
|
||||||
* Upload the contribution
|
* Upload the contribution
|
||||||
* @param contribution
|
* @param contribution
|
||||||
*/
|
*/
|
||||||
@SuppressLint("StringFormatInvalid")
|
@SuppressLint("StringFormatInvalid", "CheckResult")
|
||||||
private suspend fun uploadContribution(contribution: Contribution) {
|
private suspend fun uploadContribution(contribution: Contribution) {
|
||||||
if (contribution.localUri == null || contribution.localUri.path == null) {
|
if (contribution.localUri == null || contribution.localUri.path == null) {
|
||||||
Timber.e("""upload: ${contribution.media.filename} failed, file path is null""")
|
Timber.e("""upload: ${contribution.media.filename} failed, file path is null""")
|
||||||
|
|
@ -338,7 +341,7 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
|
||||||
val stashUploadResult = uploadClient.uploadFileToStash(
|
val stashUploadResult = uploadClient.uploadFileToStash(
|
||||||
filename!!, contribution, notificationProgressUpdater
|
filename!!, contribution, notificationProgressUpdater
|
||||||
).onErrorReturn{
|
).onErrorReturn{
|
||||||
return@onErrorReturn StashUploadResult(StashUploadState.FAILED,fileKey = null)
|
return@onErrorReturn StashUploadResult(StashUploadState.FAILED,fileKey = null,errorMessage = it.message)
|
||||||
}.blockingSingle()
|
}.blockingSingle()
|
||||||
|
|
||||||
when (stashUploadResult.state) {
|
when (stashUploadResult.state) {
|
||||||
|
|
@ -402,10 +405,21 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Timber.e("""upload file to stash failed with status: ${stashUploadResult.state}""")
|
Timber.e("""upload file to stash failed with status: ${stashUploadResult.state}""")
|
||||||
showFailedNotification(contribution)
|
showInvalidLoginNotification(contribution)
|
||||||
contribution.state = Contribution.STATE_FAILED
|
contribution.state = Contribution.STATE_FAILED
|
||||||
contribution.chunkInfo = null
|
contribution.chunkInfo = null
|
||||||
contributionDao.saveSynchronous(contribution)
|
contributionDao.saveSynchronous(contribution)
|
||||||
|
if (stashUploadResult.errorMessage.equals(CsrfTokenClient.INVALID_TOKEN_ERROR_MESSAGE)) {
|
||||||
|
Timber.e("Invalid Login, logging out")
|
||||||
|
val username = sessionManager.userName
|
||||||
|
var logoutListener = CommonsApplication.BaseLogoutListener(
|
||||||
|
appContext,
|
||||||
|
appContext.getString(R.string.invalid_login_message),
|
||||||
|
username
|
||||||
|
)
|
||||||
|
CommonsApplication.getInstance()
|
||||||
|
.clearApplicationData(appContext, logoutListener)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch (exception: Exception){
|
}catch (exception: Exception){
|
||||||
|
|
@ -566,6 +580,23 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
|
||||||
curentNotification.build()
|
curentNotification.build()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@SuppressLint("StringFormatInvalid")
|
||||||
|
private fun showInvalidLoginNotification(contribution: Contribution) {
|
||||||
|
val displayTitle = contribution.media.displayTitle
|
||||||
|
curentNotification.setContentTitle(
|
||||||
|
appContext.getString(
|
||||||
|
R.string.upload_failed_notification_title,
|
||||||
|
displayTitle
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.setContentText(appContext.getString(R.string.invalid_login_message))
|
||||||
|
.setProgress(0, 0, false)
|
||||||
|
.setOngoing(false)
|
||||||
|
notificationManager?.notify(
|
||||||
|
currentNotificationTag, currentNotificationID,
|
||||||
|
curentNotification.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify that the current upload is paused
|
* Notify that the current upload is paused
|
||||||
|
|
@ -605,5 +636,4 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -792,6 +792,7 @@ Upload your first media by tapping on the add button.</string>
|
||||||
<string name="edit_location">Edit Location</string>
|
<string name="edit_location">Edit Location</string>
|
||||||
<string name="send_thanks_to_author">Thank the author</string>
|
<string name="send_thanks_to_author">Thank the author</string>
|
||||||
<string name="error_sending_thanks">Error sending thanks to author.</string>
|
<string name="error_sending_thanks">Error sending thanks to author.</string>
|
||||||
|
<string name="invalid_login_message">Your login has expired, Please login again.</string>
|
||||||
<plurals name="custom_picker_images_selected_title_appendix">
|
<plurals name="custom_picker_images_selected_title_appendix">
|
||||||
<item quantity="one">%d image selected</item>
|
<item quantity="one">%d image selected</item>
|
||||||
<item quantity="other">%d images selected</item>
|
<item quantity="other">%d images selected</item>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue