From 4aa4ea0083334b0d1ec84a6a370785cfd472ccf3 Mon Sep 17 00:00:00 2001 From: Ritika Date: Thu, 6 Jul 2023 14:24:27 +0530 Subject: [PATCH 01/23] fix stuck uploads --- app/build.gradle | 4 ++-- .../commons/customselector/helper/OnSwipeTouchListener.kt | 4 ++-- .../java/fr/free/nrw/commons/upload/worker/UploadWorker.kt | 7 ++++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f4ce47239b..a7b9cf4c61 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -142,7 +142,7 @@ dependencies { implementation "androidx.multidex:multidex:$MULTIDEX_VERSION" - def work_version = "2.8.0" + def work_version = "2.8.1" // Kotlin + coroutines implementation "androidx.work:work-runtime-ktx:$work_version" implementation("androidx.work:work-runtime:$work_version") @@ -168,7 +168,7 @@ project.gradle.taskGraph.whenReady { } android { - compileSdkVersion 31 + compileSdkVersion 33 defaultConfig { //applicationId 'fr.free.nrw.commons' diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListener.kt b/app/src/main/java/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListener.kt index 89cbb8fb40..f454a3af8c 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListener.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListener.kt @@ -16,7 +16,7 @@ open class OnSwipeTouchListener(context: Context?) : View.OnTouchListener { private val SWIPE_THRESHOLD_WIDTH = (getScreenResolution(context!!)).first / 3 private val SWIPE_VELOCITY_THRESHOLD = 1000 - override fun onTouch(view: View?, motionEvent: MotionEvent?): Boolean { + override fun onTouch(view: View?, motionEvent: MotionEvent): Boolean { return gestureDetector.onTouchEvent(motionEvent) } @@ -32,7 +32,7 @@ open class OnSwipeTouchListener(context: Context?) : View.OnTouchListener { inner class GestureListener : GestureDetector.SimpleOnGestureListener() { - override fun onDown(e: MotionEvent?): Boolean { + override fun onDown(e: MotionEvent): Boolean { return true } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index 6a4497ea15..4c5ba083ac 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -6,6 +6,7 @@ import android.app.TaskStackBuilder import android.content.Context import android.content.Intent import android.graphics.BitmapFactory +import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.work.CoroutineWorker @@ -540,7 +541,11 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : val intent = Intent(appContext,toClass) return TaskStackBuilder.create(appContext).run { addNextIntentWithParentStack(intent) - getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + getPendingIntent(0, PendingIntent.FLAG_IMMUTABLE) + } else { + getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) + } }; } From 96058ff6c7771ef11a05e3a5b9aaa024b77efbcb Mon Sep 17 00:00:00 2001 From: Ritika Date: Fri, 7 Jul 2023 17:40:56 +0530 Subject: [PATCH 02/23] automate retries for failed uploads once the user returns to the app --- .../commons/contributions/MainActivity.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index a96f1f37bd..a2d3988026 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -1,6 +1,7 @@ package fr.free.nrw.commons.contributions; import android.Manifest.permission; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -47,6 +48,8 @@ import fr.free.nrw.commons.upload.worker.UploadWorker; import fr.free.nrw.commons.utils.PermissionUtils; import fr.free.nrw.commons.utils.ViewUtilWrapper; +import io.reactivex.schedulers.Schedulers; +import java.util.Collections; import javax.inject.Inject; import javax.inject.Named; import timber.log.Timber; @@ -58,6 +61,8 @@ public class MainActivity extends BaseActivity SessionManager sessionManager; @Inject ContributionController controller; + @Inject + ContributionDao contributionDao; @BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.pager) @@ -360,6 +365,21 @@ public boolean onOptionsItemSelected(MenuItem item) { } } + /** + * Retry all failed uploads as soon as the user returns to the app + */ + @SuppressLint("CheckResult") + private void retryAllFailedUploads() { + contributionDao. + getContribution(Collections.singletonList(Contribution.STATE_FAILED)) + .subscribeOn(Schedulers.io()) + .subscribe(failedUploads -> { + for (Contribution contribution: failedUploads) { + contributionsFragment.retryUpload(contribution); + } + }); + } + public void toggleLimitedConnectionMode() { defaultKvStore.putBoolean(CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED, !defaultKvStore @@ -405,6 +425,8 @@ protected void onResume() { (!applicationKvStore.getBoolean("login_skipped"))) { WelcomeActivity.startYourself(this); } + + retryAllFailedUploads(); } @Override From 870f2860e5d870c65c5b31b10bae8d5459a6c4f2 Mon Sep 17 00:00:00 2001 From: Ritika Date: Mon, 10 Jul 2023 17:57:24 +0530 Subject: [PATCH 03/23] UploadWorker: modify PendingIntent flag and Android version code --- .../java/fr/free/nrw/commons/upload/worker/UploadWorker.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index 4c5ba083ac..ced50c1e35 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -541,8 +541,9 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : val intent = Intent(appContext,toClass) return TaskStackBuilder.create(appContext).run { addNextIntentWithParentStack(intent) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - getPendingIntent(0, PendingIntent.FLAG_IMMUTABLE) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + getPendingIntent(0, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) } else { getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) } From 1ebb1174d90300da701e100acb43bab7d510d616 Mon Sep 17 00:00:00 2001 From: Ritika Date: Thu, 13 Jul 2023 21:13:41 +0530 Subject: [PATCH 04/23] MainActivity: remove automatic retry logic --- .../nrw/commons/contributions/MainActivity.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index a2d3988026..9ec089a934 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -365,21 +365,6 @@ public boolean onOptionsItemSelected(MenuItem item) { } } - /** - * Retry all failed uploads as soon as the user returns to the app - */ - @SuppressLint("CheckResult") - private void retryAllFailedUploads() { - contributionDao. - getContribution(Collections.singletonList(Contribution.STATE_FAILED)) - .subscribeOn(Schedulers.io()) - .subscribe(failedUploads -> { - for (Contribution contribution: failedUploads) { - contributionsFragment.retryUpload(contribution); - } - }); - } - public void toggleLimitedConnectionMode() { defaultKvStore.putBoolean(CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED, !defaultKvStore @@ -426,7 +411,6 @@ protected void onResume() { WelcomeActivity.startYourself(this); } - retryAllFailedUploads(); } @Override From 2dd2be1e4db7cd60d9c0dc8d5695dfa76f783919 Mon Sep 17 00:00:00 2001 From: Ritika Date: Fri, 14 Jul 2023 12:00:50 +0530 Subject: [PATCH 05/23] Revert "MainActivity: remove automatic retry logic" This reverts commit 1ebb1174d90300da701e100acb43bab7d510d616. --- .../nrw/commons/contributions/MainActivity.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index 9ec089a934..a2d3988026 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -365,6 +365,21 @@ public boolean onOptionsItemSelected(MenuItem item) { } } + /** + * Retry all failed uploads as soon as the user returns to the app + */ + @SuppressLint("CheckResult") + private void retryAllFailedUploads() { + contributionDao. + getContribution(Collections.singletonList(Contribution.STATE_FAILED)) + .subscribeOn(Schedulers.io()) + .subscribe(failedUploads -> { + for (Contribution contribution: failedUploads) { + contributionsFragment.retryUpload(contribution); + } + }); + } + public void toggleLimitedConnectionMode() { defaultKvStore.putBoolean(CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED, !defaultKvStore @@ -411,6 +426,7 @@ protected void onResume() { WelcomeActivity.startYourself(this); } + retryAllFailedUploads(); } @Override From 6b9e5abd8ca987fec3dac0b4f3254f1882af69e2 Mon Sep 17 00:00:00 2001 From: Ritika Date: Sun, 16 Jul 2023 14:08:27 +0530 Subject: [PATCH 06/23] set work request as expedited --- .../commons/contributions/ContributionsPresenter.java | 7 ++++++- .../fr/free/nrw/commons/contributions/MainActivity.java | 7 ++++++- .../java/fr/free/nrw/commons/upload/UploadActivity.java | 7 ++++++- .../fr/free/nrw/commons/upload/worker/UploadWorker.kt | 9 +++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java index 002e8bc95b..6fb1785dd8 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java @@ -2,6 +2,7 @@ import androidx.work.ExistingWorkPolicy; import androidx.work.OneTimeWorkRequest; +import androidx.work.OutOfQuotaPolicy; import androidx.work.WorkManager; import fr.free.nrw.commons.MediaDataExtractor; import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener; @@ -77,10 +78,14 @@ public void saveContribution(Contribution contribution) { .save(contribution) .subscribeOn(ioThreadScheduler) .subscribe(() -> { + OneTimeWorkRequest updatedUploadRequest = new OneTimeWorkRequest + .Builder(UploadWorker.class) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .build(); WorkManager.getInstance(view.getContext().getApplicationContext()) .enqueueUniqueWork( UploadWorker.class.getSimpleName(), - ExistingWorkPolicy.KEEP, OneTimeWorkRequest.from(UploadWorker.class)); + ExistingWorkPolicy.KEEP, updatedUploadRequest); })); } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index a2d3988026..bb54bc11a5 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -20,6 +20,7 @@ import androidx.fragment.app.FragmentManager; import androidx.work.ExistingWorkPolicy; import androidx.work.OneTimeWorkRequest; +import androidx.work.OutOfQuotaPolicy; import androidx.work.WorkManager; import butterknife.BindView; import butterknife.ButterKnife; @@ -389,9 +390,13 @@ public void toggleLimitedConnectionMode() { viewUtilWrapper .showShortToast(getBaseContext(), getString(R.string.limited_connection_enabled)); } else { + OneTimeWorkRequest restartUploadsRequest = new OneTimeWorkRequest + .Builder(UploadWorker.class) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .build(); WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork( UploadWorker.class.getSimpleName(), - ExistingWorkPolicy.APPEND_OR_REPLACE, OneTimeWorkRequest.from(UploadWorker.class)); + ExistingWorkPolicy.APPEND_OR_REPLACE, restartUploadsRequest); viewUtilWrapper .showShortToast(getBaseContext(), getString(R.string.limited_connection_disabled)); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index 7fd4ecce7f..f514a65931 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -26,6 +26,7 @@ import androidx.viewpager.widget.ViewPager; import androidx.work.ExistingWorkPolicy; import androidx.work.OneTimeWorkRequest; +import androidx.work.OutOfQuotaPolicy; import androidx.work.WorkManager; import butterknife.BindView; import butterknife.ButterKnife; @@ -317,9 +318,13 @@ public void updateTopCardTitle() { @Override public void makeUploadRequest() { + OneTimeWorkRequest uploadRequest = new OneTimeWorkRequest + .Builder(UploadWorker.class) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .build(); WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork( UploadWorker.class.getSimpleName(), - ExistingWorkPolicy.APPEND_OR_REPLACE, OneTimeWorkRequest.from(UploadWorker.class)); + ExistingWorkPolicy.APPEND_OR_REPLACE, uploadRequest); } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index ced50c1e35..95382b91f0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -13,6 +13,7 @@ import androidx.work.CoroutineWorker import androidx.work.Data import androidx.work.WorkerParameters import androidx.multidex.BuildConfig +import androidx.work.ForegroundInfo import dagger.android.ContributesAndroidInjector import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.Media @@ -228,6 +229,14 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : return Result.success() } + override suspend fun getForegroundInfo(): ForegroundInfo { + // TODO: handle notifications for older Android versions + return ForegroundInfo( + 1, + curentNotification.build() + ) + } + /** * Returns true is the limited connection mode is enabled */ From f1b2e03dbe2701e7956af3d09d8616f5fbf4279d Mon Sep 17 00:00:00 2001 From: Ritika Date: Wed, 2 Aug 2023 13:13:01 +0530 Subject: [PATCH 07/23] handle notification for foreground service on older versions of Android --- .../commons/contributions/ContributionsPresenter.java | 10 ++++++---- .../free/nrw/commons/contributions/MainActivity.java | 6 ++++++ .../fr/free/nrw/commons/upload/UploadActivity.java | 6 ++++++ .../fr/free/nrw/commons/upload/worker/UploadWorker.kt | 11 +++++++++-- app/src/main/res/values/strings.xml | 1 + 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java index 6fb1785dd8..45f97b82cc 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java @@ -1,6 +1,8 @@ package fr.free.nrw.commons.contributions; +import androidx.work.Constraints; import androidx.work.ExistingWorkPolicy; +import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; import androidx.work.OutOfQuotaPolicy; import androidx.work.WorkManager; @@ -10,10 +12,6 @@ import fr.free.nrw.commons.upload.worker.UploadWorker; import io.reactivex.Scheduler; import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.functions.Action; -import io.reactivex.functions.Consumer; -import java.util.Collections; -import java.util.List; import javax.inject.Inject; import javax.inject.Named; @@ -78,9 +76,13 @@ public void saveContribution(Contribution contribution) { .save(contribution) .subscribeOn(ioThreadScheduler) .subscribe(() -> { + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build(); OneTimeWorkRequest updatedUploadRequest = new OneTimeWorkRequest .Builder(UploadWorker.class) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .setConstraints(constraints) .build(); WorkManager.getInstance(view.getContext().getApplicationContext()) .enqueueUniqueWork( diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index bb54bc11a5..2dc596d7e6 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -18,7 +18,9 @@ import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import androidx.work.Constraints; import androidx.work.ExistingWorkPolicy; +import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; import androidx.work.OutOfQuotaPolicy; import androidx.work.WorkManager; @@ -390,9 +392,13 @@ public void toggleLimitedConnectionMode() { viewUtilWrapper .showShortToast(getBaseContext(), getString(R.string.limited_connection_enabled)); } else { + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build(); OneTimeWorkRequest restartUploadsRequest = new OneTimeWorkRequest .Builder(UploadWorker.class) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .setConstraints(constraints) .build(); WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork( UploadWorker.class.getSimpleName(), diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index f514a65931..78a4daf895 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -24,7 +24,9 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; +import androidx.work.Constraints; import androidx.work.ExistingWorkPolicy; +import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; import androidx.work.OutOfQuotaPolicy; import androidx.work.WorkManager; @@ -318,9 +320,13 @@ public void updateTopCardTitle() { @Override public void makeUploadRequest() { + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build(); OneTimeWorkRequest uploadRequest = new OneTimeWorkRequest .Builder(UploadWorker.class) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .setConstraints(constraints) .build(); WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork( UploadWorker.class.getSimpleName(), diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index 95382b91f0..441a382f58 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -1,6 +1,7 @@ package fr.free.nrw.commons.upload.worker import android.annotation.SuppressLint +import android.app.Notification import android.app.PendingIntent import android.app.TaskStackBuilder import android.content.Context @@ -230,13 +231,19 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : } override suspend fun getForegroundInfo(): ForegroundInfo { - // TODO: handle notifications for older Android versions + // TODO: improve notifications for older Android versions return ForegroundInfo( 1, - curentNotification.build() + createNotificationForForegroundService() ) } + private fun createNotificationForForegroundService(): Notification { + return getNotificationBuilder( + CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL)!! + .setContentTitle(appContext.getString(R.string.upload_in_progress)) + .build() + } /** * Returns true is the limited connection mode is enabled */ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e402e0c1ca..202a9d027e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -62,6 +62,7 @@ Settings Upload to Commons + Upload in progress Username Password Log in to your Commons Beta account From 0a649895817c03aa7168bc23f05c1bf1dbc07e55 Mon Sep 17 00:00:00 2001 From: Ritika Date: Wed, 9 Aug 2023 12:28:44 +0530 Subject: [PATCH 08/23] set backoff criteria for work requests --- .../contributions/ContributionsPresenter.java | 3 + .../commons/contributions/MainActivity.java | 3 + .../nrw/commons/upload/UploadActivity.java | 3 + .../nrw/commons/upload/worker/UploadWorker.kt | 119 +++++++++--------- 4 files changed, 71 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java index 45f97b82cc..d32c1cc9a6 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java @@ -1,5 +1,6 @@ package fr.free.nrw.commons.contributions; +import androidx.work.BackoffPolicy; import androidx.work.Constraints; import androidx.work.ExistingWorkPolicy; import androidx.work.NetworkType; @@ -12,6 +13,7 @@ import fr.free.nrw.commons.upload.worker.UploadWorker; import io.reactivex.Scheduler; import io.reactivex.disposables.CompositeDisposable; +import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Named; @@ -83,6 +85,7 @@ public void saveContribution(Contribution contribution) { .Builder(UploadWorker.class) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setConstraints(constraints) + .setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.SECONDS) .build(); WorkManager.getInstance(view.getContext().getApplicationContext()) .enqueueUniqueWork( diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index 2dc596d7e6..a07b732075 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -18,6 +18,7 @@ import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import androidx.work.BackoffPolicy; import androidx.work.Constraints; import androidx.work.ExistingWorkPolicy; import androidx.work.NetworkType; @@ -53,6 +54,7 @@ import fr.free.nrw.commons.utils.ViewUtilWrapper; import io.reactivex.schedulers.Schedulers; import java.util.Collections; +import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Named; import timber.log.Timber; @@ -399,6 +401,7 @@ public void toggleLimitedConnectionMode() { .Builder(UploadWorker.class) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setConstraints(constraints) + .setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.SECONDS) .build(); WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork( UploadWorker.class.getSimpleName(), diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index 78a4daf895..1df3ac21e3 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -24,6 +24,7 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; +import androidx.work.BackoffPolicy; import androidx.work.Constraints; import androidx.work.ExistingWorkPolicy; import androidx.work.NetworkType; @@ -60,6 +61,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Named; import timber.log.Timber; @@ -327,6 +329,7 @@ public void makeUploadRequest() { .Builder(UploadWorker.class) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setConstraints(constraints) + .setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.SECONDS) .build(); WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork( UploadWorker.class.getSimpleName(), diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index 441a382f58..3da03efac2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -43,6 +43,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber +import java.net.SocketTimeoutException import java.util.* import java.util.regex.Pattern import javax.inject.Inject @@ -163,71 +164,75 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : } override suspend fun doWork(): Result { - var countUpload = 0 - notificationManager = NotificationManagerCompat.from(appContext) - val processingUploads = getNotificationBuilder( - CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL - )!! - withContext(Dispatchers.IO) { - val queuedContributions = contributionDao.getContribution(statesToProcess) - .blockingGet() - //Showing initial notification for the number of uploads being processed - - Timber.e("Queued Contributions: "+ queuedContributions.size) - - processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads)) - processingUploads.setContentText( - appContext.resources.getQuantityString( - R.plurals.starting_multiple_uploads, - queuedContributions.size, - queuedContributions.size - ) - ) - notificationManager?.notify( - PROCESSING_UPLOADS_NOTIFICATION_TAG, - PROCESSING_UPLOADS_NOTIFICATION_ID, - processingUploads.build() - ) + try { + var countUpload = 0 + notificationManager = NotificationManagerCompat.from(appContext) + val processingUploads = getNotificationBuilder( + CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL + )!! + withContext(Dispatchers.IO) { + val queuedContributions = contributionDao.getContribution(statesToProcess) + .blockingGet() + //Showing initial notification for the number of uploads being processed - /** - * To avoid race condition when multiple of these workers are working, assign this state - so that the next one does not process these contribution again - */ - queuedContributions.forEach { - it.state=Contribution.STATE_IN_PROGRESS - contributionDao.saveSynchronous(it) - } + Timber.e("Queued Contributions: " + queuedContributions.size) + + processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads)) + processingUploads.setContentText( + appContext.resources.getQuantityString( + R.plurals.starting_multiple_uploads, + queuedContributions.size, + queuedContributions.size + ) + ) + notificationManager?.notify( + PROCESSING_UPLOADS_NOTIFICATION_TAG, + PROCESSING_UPLOADS_NOTIFICATION_ID, + processingUploads.build() + ) - queuedContributions.asFlow().map { contribution -> /** - * If the limited connection mode is on, lets iterate through the queued - * contributions - * and set the state as STATE_QUEUED_LIMITED_CONNECTION_MODE , - * otherwise proceed with the upload + * To avoid race condition when multiple of these workers are working, assign this state + so that the next one does not process these contribution again */ - if (isLimitedConnectionModeEnabled()) { - if (contribution.state == Contribution.STATE_QUEUED) { - contribution.state = Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE + queuedContributions.forEach { + it.state = Contribution.STATE_IN_PROGRESS + contributionDao.saveSynchronous(it) + } + + queuedContributions.asFlow().map { contribution -> + /** + * If the limited connection mode is on, lets iterate through the queued + * contributions + * and set the state as STATE_QUEUED_LIMITED_CONNECTION_MODE , + * otherwise proceed with the upload + */ + if (isLimitedConnectionModeEnabled()) { + if (contribution.state == Contribution.STATE_QUEUED) { + contribution.state = Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE + contributionDao.saveSynchronous(contribution) + } + } else { + contribution.transferred = 0 + contribution.state = Contribution.STATE_IN_PROGRESS contributionDao.saveSynchronous(contribution) + setProgressAsync(Data.Builder().putInt("progress", countUpload).build()) + countUpload++ + uploadContribution(contribution = contribution) } - } else { - contribution.transferred = 0 - contribution.state = Contribution.STATE_IN_PROGRESS - contributionDao.saveSynchronous(contribution) - setProgressAsync(Data.Builder().putInt("progress", countUpload).build()) - countUpload++ - uploadContribution(contribution = contribution) - } - }.collect() + }.collect() - //Dismiss the global notification - notificationManager?.cancel( - PROCESSING_UPLOADS_NOTIFICATION_TAG, - PROCESSING_UPLOADS_NOTIFICATION_ID - ) + //Dismiss the global notification + notificationManager?.cancel( + PROCESSING_UPLOADS_NOTIFICATION_TAG, + PROCESSING_UPLOADS_NOTIFICATION_ID + ) + } + //TODO make this smart, think of handling retries in the future + return Result.success() + } catch (e: Exception) { + return Result.retry() } - //TODO make this smart, think of handling retries in the future - return Result.success() } override suspend fun getForegroundInfo(): ForegroundInfo { From c785d6fc1cb592ae808c3b1e54e0b3dca5668557 Mon Sep 17 00:00:00 2001 From: Ritika Date: Mon, 14 Aug 2023 14:46:51 +0530 Subject: [PATCH 09/23] enqueue failed uploads for a retry --- .../commons/contributions/MainActivity.java | 16 --- .../nrw/commons/upload/worker/UploadWorker.kt | 123 +++++++++--------- 2 files changed, 63 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index a07b732075..c2d3feebb9 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -370,21 +370,6 @@ public boolean onOptionsItemSelected(MenuItem item) { } } - /** - * Retry all failed uploads as soon as the user returns to the app - */ - @SuppressLint("CheckResult") - private void retryAllFailedUploads() { - contributionDao. - getContribution(Collections.singletonList(Contribution.STATE_FAILED)) - .subscribeOn(Schedulers.io()) - .subscribe(failedUploads -> { - for (Contribution contribution: failedUploads) { - contributionsFragment.retryUpload(contribution); - } - }); - } - public void toggleLimitedConnectionMode() { defaultKvStore.putBoolean(CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED, !defaultKvStore @@ -440,7 +425,6 @@ protected void onResume() { WelcomeActivity.startYourself(this); } - retryAllFailedUploads(); } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index 3da03efac2..d17a7ae593 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -105,6 +105,7 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : statesToProcess.add(Contribution.STATE_QUEUED) statesToProcess.add(Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE) + statesToProcess.add(Contribution.STATE_FAILED) } @dagger.Module @@ -164,75 +165,77 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : } override suspend fun doWork(): Result { - try { - var countUpload = 0 - notificationManager = NotificationManagerCompat.from(appContext) - val processingUploads = getNotificationBuilder( - CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL - )!! - withContext(Dispatchers.IO) { - val queuedContributions = contributionDao.getContribution(statesToProcess) - .blockingGet() - //Showing initial notification for the number of uploads being processed - - Timber.e("Queued Contributions: " + queuedContributions.size) - - processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads)) - processingUploads.setContentText( - appContext.resources.getQuantityString( - R.plurals.starting_multiple_uploads, - queuedContributions.size, - queuedContributions.size - ) - ) - notificationManager?.notify( - PROCESSING_UPLOADS_NOTIFICATION_TAG, - PROCESSING_UPLOADS_NOTIFICATION_ID, - processingUploads.build() + var countUpload = 0 + notificationManager = NotificationManagerCompat.from(appContext) + val processingUploads = getNotificationBuilder( + CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL + )!! + withContext(Dispatchers.IO) { + val queuedContributions = contributionDao.getContribution(statesToProcess) + .blockingGet() + //Showing initial notification for the number of uploads being processed + + Timber.e("Queued Contributions: " + queuedContributions.size) + + processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads)) + processingUploads.setContentText( + appContext.resources.getQuantityString( + R.plurals.starting_multiple_uploads, + queuedContributions.size, + queuedContributions.size ) + ) + notificationManager?.notify( + PROCESSING_UPLOADS_NOTIFICATION_TAG, + PROCESSING_UPLOADS_NOTIFICATION_ID, + processingUploads.build() + ) + /** + * To avoid race condition when multiple of these workers are working, assign this state + so that the next one does not process these contribution again + */ + queuedContributions.forEach { + it.state = Contribution.STATE_IN_PROGRESS + contributionDao.saveSynchronous(it) + } + + queuedContributions.asFlow().map { contribution -> /** - * To avoid race condition when multiple of these workers are working, assign this state - so that the next one does not process these contribution again + * If the limited connection mode is on, lets iterate through the queued + * contributions + * and set the state as STATE_QUEUED_LIMITED_CONNECTION_MODE , + * otherwise proceed with the upload */ - queuedContributions.forEach { - it.state = Contribution.STATE_IN_PROGRESS - contributionDao.saveSynchronous(it) - } - - queuedContributions.asFlow().map { contribution -> - /** - * If the limited connection mode is on, lets iterate through the queued - * contributions - * and set the state as STATE_QUEUED_LIMITED_CONNECTION_MODE , - * otherwise proceed with the upload - */ - if (isLimitedConnectionModeEnabled()) { - if (contribution.state == Contribution.STATE_QUEUED) { - contribution.state = Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE - contributionDao.saveSynchronous(contribution) - } - } else { - contribution.transferred = 0 - contribution.state = Contribution.STATE_IN_PROGRESS + if (isLimitedConnectionModeEnabled()) { + if (contribution.state == Contribution.STATE_QUEUED) { + contribution.state = Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE contributionDao.saveSynchronous(contribution) - setProgressAsync(Data.Builder().putInt("progress", countUpload).build()) - countUpload++ - uploadContribution(contribution = contribution) } - }.collect() + } else { + contribution.transferred = 0 + contribution.state = Contribution.STATE_IN_PROGRESS + contributionDao.saveSynchronous(contribution) + setProgressAsync(Data.Builder().putInt("progress", countUpload).build()) + countUpload++ + uploadContribution(contribution = contribution) + } + }.collect() - //Dismiss the global notification - notificationManager?.cancel( - PROCESSING_UPLOADS_NOTIFICATION_TAG, - PROCESSING_UPLOADS_NOTIFICATION_ID - ) - } - //TODO make this smart, think of handling retries in the future - return Result.success() - } catch (e: Exception) { + //Dismiss the global notification + notificationManager?.cancel( + PROCESSING_UPLOADS_NOTIFICATION_TAG, + PROCESSING_UPLOADS_NOTIFICATION_ID + ) + } + + // Retry if any new contribution is added to the queue + val updatedContributionsQueue = contributionDao.getContribution(statesToProcess).blockingGet() + if (updatedContributionsQueue.isNotEmpty()) { return Result.retry() } + + return Result.success() } override suspend fun getForegroundInfo(): ForegroundInfo { From e1855b928a4510831b147a74b3eefca58304b7f6 Mon Sep 17 00:00:00 2001 From: Ritika Date: Tue, 15 Aug 2023 18:52:35 +0530 Subject: [PATCH 10/23] revert "enqueue failed uploads for a retry" This reverts commit c785d6fc1cb592ae808c3b1e54e0b3dca5668557. --- .../commons/contributions/MainActivity.java | 16 +++ .../nrw/commons/upload/worker/UploadWorker.kt | 123 +++++++++--------- 2 files changed, 76 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index c2d3feebb9..a07b732075 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -370,6 +370,21 @@ public boolean onOptionsItemSelected(MenuItem item) { } } + /** + * Retry all failed uploads as soon as the user returns to the app + */ + @SuppressLint("CheckResult") + private void retryAllFailedUploads() { + contributionDao. + getContribution(Collections.singletonList(Contribution.STATE_FAILED)) + .subscribeOn(Schedulers.io()) + .subscribe(failedUploads -> { + for (Contribution contribution: failedUploads) { + contributionsFragment.retryUpload(contribution); + } + }); + } + public void toggleLimitedConnectionMode() { defaultKvStore.putBoolean(CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED, !defaultKvStore @@ -425,6 +440,7 @@ protected void onResume() { WelcomeActivity.startYourself(this); } + retryAllFailedUploads(); } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index d17a7ae593..3da03efac2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -105,7 +105,6 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : statesToProcess.add(Contribution.STATE_QUEUED) statesToProcess.add(Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE) - statesToProcess.add(Contribution.STATE_FAILED) } @dagger.Module @@ -165,77 +164,75 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : } override suspend fun doWork(): Result { - var countUpload = 0 - notificationManager = NotificationManagerCompat.from(appContext) - val processingUploads = getNotificationBuilder( - CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL - )!! - withContext(Dispatchers.IO) { - val queuedContributions = contributionDao.getContribution(statesToProcess) - .blockingGet() - //Showing initial notification for the number of uploads being processed - - Timber.e("Queued Contributions: " + queuedContributions.size) - - processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads)) - processingUploads.setContentText( - appContext.resources.getQuantityString( - R.plurals.starting_multiple_uploads, - queuedContributions.size, - queuedContributions.size - ) - ) - notificationManager?.notify( - PROCESSING_UPLOADS_NOTIFICATION_TAG, - PROCESSING_UPLOADS_NOTIFICATION_ID, - processingUploads.build() - ) + try { + var countUpload = 0 + notificationManager = NotificationManagerCompat.from(appContext) + val processingUploads = getNotificationBuilder( + CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL + )!! + withContext(Dispatchers.IO) { + val queuedContributions = contributionDao.getContribution(statesToProcess) + .blockingGet() + //Showing initial notification for the number of uploads being processed - /** - * To avoid race condition when multiple of these workers are working, assign this state - so that the next one does not process these contribution again - */ - queuedContributions.forEach { - it.state = Contribution.STATE_IN_PROGRESS - contributionDao.saveSynchronous(it) - } + Timber.e("Queued Contributions: " + queuedContributions.size) + + processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads)) + processingUploads.setContentText( + appContext.resources.getQuantityString( + R.plurals.starting_multiple_uploads, + queuedContributions.size, + queuedContributions.size + ) + ) + notificationManager?.notify( + PROCESSING_UPLOADS_NOTIFICATION_TAG, + PROCESSING_UPLOADS_NOTIFICATION_ID, + processingUploads.build() + ) - queuedContributions.asFlow().map { contribution -> /** - * If the limited connection mode is on, lets iterate through the queued - * contributions - * and set the state as STATE_QUEUED_LIMITED_CONNECTION_MODE , - * otherwise proceed with the upload + * To avoid race condition when multiple of these workers are working, assign this state + so that the next one does not process these contribution again */ - if (isLimitedConnectionModeEnabled()) { - if (contribution.state == Contribution.STATE_QUEUED) { - contribution.state = Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE - contributionDao.saveSynchronous(contribution) - } - } else { - contribution.transferred = 0 - contribution.state = Contribution.STATE_IN_PROGRESS - contributionDao.saveSynchronous(contribution) - setProgressAsync(Data.Builder().putInt("progress", countUpload).build()) - countUpload++ - uploadContribution(contribution = contribution) + queuedContributions.forEach { + it.state = Contribution.STATE_IN_PROGRESS + contributionDao.saveSynchronous(it) } - }.collect() - //Dismiss the global notification - notificationManager?.cancel( - PROCESSING_UPLOADS_NOTIFICATION_TAG, - PROCESSING_UPLOADS_NOTIFICATION_ID - ) - } + queuedContributions.asFlow().map { contribution -> + /** + * If the limited connection mode is on, lets iterate through the queued + * contributions + * and set the state as STATE_QUEUED_LIMITED_CONNECTION_MODE , + * otherwise proceed with the upload + */ + if (isLimitedConnectionModeEnabled()) { + if (contribution.state == Contribution.STATE_QUEUED) { + contribution.state = Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE + contributionDao.saveSynchronous(contribution) + } + } else { + contribution.transferred = 0 + contribution.state = Contribution.STATE_IN_PROGRESS + contributionDao.saveSynchronous(contribution) + setProgressAsync(Data.Builder().putInt("progress", countUpload).build()) + countUpload++ + uploadContribution(contribution = contribution) + } + }.collect() - // Retry if any new contribution is added to the queue - val updatedContributionsQueue = contributionDao.getContribution(statesToProcess).blockingGet() - if (updatedContributionsQueue.isNotEmpty()) { + //Dismiss the global notification + notificationManager?.cancel( + PROCESSING_UPLOADS_NOTIFICATION_TAG, + PROCESSING_UPLOADS_NOTIFICATION_ID + ) + } + //TODO make this smart, think of handling retries in the future + return Result.success() + } catch (e: Exception) { return Result.retry() } - - return Result.success() } override suspend fun getForegroundInfo(): ForegroundInfo { From 6d013e67d8526d2e422fa27b2a8acebdb4c037c6 Mon Sep 17 00:00:00 2001 From: Ritika Date: Thu, 17 Aug 2023 23:48:57 +0530 Subject: [PATCH 11/23] limit the number of retries for a failed upload --- .../nrw/commons/contributions/Contribution.kt | 6 +++- .../contributions/ContributionsFragment.java | 30 ++++++++++++++++--- app/src/main/res/values/strings.xml | 1 + 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt index c3d084f0af..b75271f054 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt @@ -43,7 +43,11 @@ data class Contribution constructor( var hasInvalidLocation : Int = 0, var contentUri: Uri? = null, var countryCode : String? = null, - var imageSHA1 : String? = null + var imageSHA1 : String? = null, + /** + * Number of times a contribution has been retried after a failure + */ + var retries: Int = 0 ) : Parcelable { fun completeWith(media: Media): Contribution { diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java index 03e3e56116..33cc7d69d3 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java @@ -92,6 +92,7 @@ public class ContributionsFragment private static final String CONTRIBUTION_LIST_FRAGMENT_TAG = "ContributionListFragmentTag"; private MediaDetailPagerFragment mediaDetailPagerFragment; static final String MEDIA_DETAIL_PAGER_FRAGMENT_TAG = "MediaDetailFragmentTag"; + private static final int MAX_RETRIES = 10; @BindView(R.id.card_view_nearby) public NearbyNotificationCardView nearbyNotificationCardView; @BindView(R.id.campaigns_view) CampaignView campaignView; @@ -593,6 +594,15 @@ public void notifyDataSetChanged() { } } + /** + * Restarts the upload process for a contribution + * @param contribution + */ + public void restartUpload(Contribution contribution) { + contribution.setState(Contribution.STATE_QUEUED); + contributionsPresenter.saveContribution(contribution); + Timber.d("Restarting for %s", contribution.toString()); + } /** * Retry upload when it is failed * @@ -601,10 +611,22 @@ public void notifyDataSetChanged() { @Override public void retryUpload(Contribution contribution) { if (NetworkUtils.isInternetConnectionEstablished(getContext())) { - if (contribution.getState() == STATE_FAILED || contribution.getState() == STATE_PAUSED || contribution.getState()==Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE) { - contribution.setState(Contribution.STATE_QUEUED); - contributionsPresenter.saveContribution(contribution); - Timber.d("Restarting for %s", contribution.toString()); + if (contribution.getState() == STATE_PAUSED || contribution.getState()==Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE) { + restartUpload(contribution); + } else if (contribution.getState() == STATE_FAILED) { + int retries = contribution.getRetries(); + /* Limit the number of retries for a failed upload + to handle cases like invalid filename as such uploads + will never be successful */ + if(retries < MAX_RETRIES) { + contribution.setRetries(retries + 1); + Timber.d("Retried %d times", retries + 1); + restartUpload(contribution); + } else { + // TODO: Show the exact reason for failure + Toast.makeText(getContext(), + R.string.retry_limit_reached, Toast.LENGTH_SHORT).show(); + } } else { Timber.d("Skipping re-upload for non-failed %s", contribution.toString()); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 202a9d027e..4f89bee655 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -76,6 +76,7 @@ Login success! Login failed! File not found. Please try another file. + Maximum retry limit reached! Please cancel the upload and try again Authentication failed, please login again Upload started! Upload queued (limited connection mode enabled) From 4cafd2a57cd0526bae7ebf87bee36688cf175438 Mon Sep 17 00:00:00 2001 From: Ritika Date: Sat, 19 Aug 2023 00:06:27 +0530 Subject: [PATCH 12/23] add a popup that suggests users to switch to unrestricted battery usage mode --- .../nrw/commons/contributions/MainActivity.java | 3 +++ .../fr/free/nrw/commons/upload/UploadActivity.java | 13 +++++++++++++ app/src/main/res/values/strings.xml | 2 ++ 3 files changed, 18 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index a07b732075..3fffdd5394 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -148,6 +148,9 @@ public void onCreate(Bundle savedInstanceState) { setTitle(getString(R.string.navigation_item_explore)); setUpLoggedOutPager(); } else { + if (applicationKvStore.getBoolean("firstrun", true)) { + applicationKvStore.putBoolean("firstBigUploadSet", true); + } if(savedInstanceState == null){ //starting a fresh fragment. // Open Last opened screen if it is Contributions or Nearby, otherwise Contributions diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index 1df3ac21e3..b3e93c022c 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -378,6 +378,19 @@ private void receiveSharedItems() { .getQuantityString(R.plurals.upload_count_title, uploadableFiles.size(), uploadableFiles.size())); fragments = new ArrayList<>(); + // Suggest users to switch to Unrestricted battery usage mode + if (uploadableFiles.size() > 3 + && defaultKvStore.getBoolean("firstBigUploadSet")) { + DialogUtil.showAlertDialog( + this, + getString(R.string.unrestricted_battery_mode), + getString(R.string.suggest_unrestricted_mode), + getString(R.string.ok), + () -> {}, + true + ); + defaultKvStore.putBoolean("firstBigUploadSet", false); + } for (UploadableFile uploadableFile : uploadableFiles) { UploadMediaDetailFragment uploadMediaDetailFragment = new UploadMediaDetailFragment(); uploadMediaDetailFragment.setImageTobeUploaded(uploadableFile, place); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4f89bee655..203224e7ae 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -77,6 +77,8 @@ Login failed! File not found. Please try another file. Maximum retry limit reached! Please cancel the upload and try again + Switch to Unrestricted battery usage mode + Uploading more than 3 images works more reliably when the battery optimisations are turned off. Please switch to Unrestricted battery usage mode for the Commons app from the settings for a smooth upload experience. Authentication failed, please login again Upload started! Upload queued (limited connection mode enabled) From 04b7255aa8b50b67992041e87c0d30f61d7d59ae Mon Sep 17 00:00:00 2001 From: Ritika Date: Sat, 19 Aug 2023 13:19:37 +0530 Subject: [PATCH 13/23] take users to the battery settings page on the first big upload --- app/build.gradle | 3 +++ .../free/nrw/commons/upload/UploadActivity.java | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a7b9cf4c61..caab3dcbf4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -153,6 +153,9 @@ dependencies { annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' implementation("io.github.coordinates2country:coordinates2country-android:1.3") { exclude group: 'com.google.android', module: 'android' } + + // Battery Permission Helper + implementation 'com.waseemsabir:betterypermissionhelper:1.0.0' } task disableAnimations(type: Exec) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index b3e93c022c..ba6a5f6c64 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -34,6 +34,7 @@ import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; +import com.waseemsabir.betterypermissionhelper.BatteryPermissionHelper; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.LoginActivity; @@ -385,9 +386,17 @@ private void receiveSharedItems() { this, getString(R.string.unrestricted_battery_mode), getString(R.string.suggest_unrestricted_mode), - getString(R.string.ok), - () -> {}, - true + getString(R.string.title_activity_settings), + getString(R.string.cancel), + () -> { + BatteryPermissionHelper batteryPermissionHelper = BatteryPermissionHelper + .Companion.getInstance(); + boolean isBatteryPermissionAvailable = batteryPermissionHelper. + isBatterySaverPermissionAvailable(this, true); + Timber.d("BatteryPermissionAvailable = " + isBatteryPermissionAvailable); + batteryPermissionHelper.getPermission(this, true, true); + }, + () -> {} ); defaultKvStore.putBoolean("firstBigUploadSet", false); } From 533e976fa8b0d88186f647b057382c03a451c882 Mon Sep 17 00:00:00 2001 From: Ritika Date: Sat, 19 Aug 2023 14:02:11 +0530 Subject: [PATCH 14/23] take users to battery optimisation settings page using the standard intent --- app/build.gradle | 2 -- .../fr/free/nrw/commons/upload/UploadActivity.java | 14 ++++---------- app/src/main/res/values/strings.xml | 4 ++-- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index caab3dcbf4..7363beecc0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -154,8 +154,6 @@ dependencies { implementation("io.github.coordinates2country:coordinates2country-android:1.3") { exclude group: 'com.google.android', module: 'android' } - // Battery Permission Helper - implementation 'com.waseemsabir:betterypermissionhelper:1.0.0' } task disableAnimations(type: Exec) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index ba6a5f6c64..bc198a94f4 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -1,7 +1,6 @@ package fr.free.nrw.commons.upload; import static fr.free.nrw.commons.contributions.ContributionController.ACTION_INTERNAL_UPLOADS; -import static fr.free.nrw.commons.upload.UploadPresenter.COUNTER_OF_CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES; import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; import android.Manifest; @@ -9,13 +8,13 @@ import android.app.ProgressDialog; import android.content.Intent; import android.os.Bundle; +import android.provider.Settings; import android.util.DisplayMetrics; import android.view.View; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; -import androidx.appcompat.app.AlertDialog; import androidx.cardview.widget.CardView; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; @@ -34,13 +33,11 @@ import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; -import com.waseemsabir.betterypermissionhelper.BatteryPermissionHelper; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.LoginActivity; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.contributions.ContributionController; -import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.filepicker.UploadableFile; import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.mwapi.UserClient; @@ -389,12 +386,9 @@ private void receiveSharedItems() { getString(R.string.title_activity_settings), getString(R.string.cancel), () -> { - BatteryPermissionHelper batteryPermissionHelper = BatteryPermissionHelper - .Companion.getInstance(); - boolean isBatteryPermissionAvailable = batteryPermissionHelper. - isBatterySaverPermissionAvailable(this, true); - Timber.d("BatteryPermissionAvailable = " + isBatteryPermissionAvailable); - batteryPermissionHelper.getPermission(this, true, true); + Intent batteryOptimisationSettingsIntent = new Intent( + Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); + startActivity(batteryOptimisationSettingsIntent); }, () -> {} ); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 203224e7ae..c293a0b0c4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -77,8 +77,8 @@ Login failed! File not found. Please try another file. Maximum retry limit reached! Please cancel the upload and try again - Switch to Unrestricted battery usage mode - Uploading more than 3 images works more reliably when the battery optimisations are turned off. Please switch to Unrestricted battery usage mode for the Commons app from the settings for a smooth upload experience. + Turn battery optimisations off + Uploading more than 3 images works more reliably when the battery optimisations are turned off. Please turn battery optimisations off for the Commons app from the settings for a smooth upload experience. Authentication failed, please login again Upload started! Upload queued (limited connection mode enabled) From 4fbe261ce57a24a2ce8bea87bc16482855f76c63 Mon Sep 17 00:00:00 2001 From: Ritika Date: Sat, 19 Aug 2023 15:27:03 +0530 Subject: [PATCH 15/23] add instructions to the battery optimisation settings popup --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c293a0b0c4..059ac89a3b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -78,7 +78,7 @@ File not found. Please try another file. Maximum retry limit reached! Please cancel the upload and try again Turn battery optimisations off - Uploading more than 3 images works more reliably when the battery optimisations are turned off. Please turn battery optimisations off for the Commons app from the settings for a smooth upload experience. + Uploading more than 3 images works more reliably when the battery optimisations are turned off. Please turn battery optimisations off for the Commons app or fr.free.nrw.commons from the settings for a smooth upload experience. \n\nPossible steps to turn battery optimisation off:\n\nStep 1: Tap on the \'Settings\' button below.\n\nStep 2: Switch from \'Not optimised\' to \'All apps\'.\n\nStep 3: Scroll to find the Commons app or fr.free.nrw.commons.\n\nStep 4: Tap it and select \'Don\'t optimise\'.\n\nStep 5: Press \'Done\'. Authentication failed, please login again Upload started! Upload queued (limited connection mode enabled) From c35d93682c4eee0086c8975909ee54d68a5052ca Mon Sep 17 00:00:00 2001 From: Ritika Date: Sat, 19 Aug 2023 15:44:35 +0530 Subject: [PATCH 16/23] remove the first usage of fr.free.nrw.commons from the popup --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 059ac89a3b..68f5a088cd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -78,7 +78,7 @@ File not found. Please try another file. Maximum retry limit reached! Please cancel the upload and try again Turn battery optimisations off - Uploading more than 3 images works more reliably when the battery optimisations are turned off. Please turn battery optimisations off for the Commons app or fr.free.nrw.commons from the settings for a smooth upload experience. \n\nPossible steps to turn battery optimisation off:\n\nStep 1: Tap on the \'Settings\' button below.\n\nStep 2: Switch from \'Not optimised\' to \'All apps\'.\n\nStep 3: Scroll to find the Commons app or fr.free.nrw.commons.\n\nStep 4: Tap it and select \'Don\'t optimise\'.\n\nStep 5: Press \'Done\'. + Uploading more than 3 images works more reliably when the battery optimisations are turned off. Please turn battery optimisations off for the Commons app from the settings for a smooth upload experience. \n\nPossible steps to turn battery optimisation off:\n\nStep 1: Tap on the \'Settings\' button below.\n\nStep 2: Switch from \'Not optimised\' to \'All apps\'.\n\nStep 3: Scroll to find the Commons app or fr.free.nrw.commons.\n\nStep 4: Tap it and select \'Don\'t optimise\'.\n\nStep 5: Press \'Done\'. Authentication failed, please login again Upload started! Upload queued (limited connection mode enabled) From d1bfb489a1f09b1a4a725dba0f2c35962a47017f Mon Sep 17 00:00:00 2001 From: Ritika Date: Sat, 19 Aug 2023 15:53:37 +0530 Subject: [PATCH 17/23] comply with the wording in the OS settings --- app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 68f5a088cd..732395eb56 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -77,8 +77,8 @@ Login failed! File not found. Please try another file. Maximum retry limit reached! Please cancel the upload and try again - Turn battery optimisations off - Uploading more than 3 images works more reliably when the battery optimisations are turned off. Please turn battery optimisations off for the Commons app from the settings for a smooth upload experience. \n\nPossible steps to turn battery optimisation off:\n\nStep 1: Tap on the \'Settings\' button below.\n\nStep 2: Switch from \'Not optimised\' to \'All apps\'.\n\nStep 3: Scroll to find the Commons app or fr.free.nrw.commons.\n\nStep 4: Tap it and select \'Don\'t optimise\'.\n\nStep 5: Press \'Done\'. + Turn battery optimization off? + Uploading more than 3 images works more reliably when the battery optimization are turned off. Please turn battery optimization off for the Commons app from the settings for a smooth upload experience. \n\nPossible steps to turn battery optimization off:\n\nStep 1: Tap on the \'Settings\' button below.\n\nStep 2: Switch from \'Not optimized\' to \'All apps\'.\n\nStep 3: Scroll to find the Commons app or fr.free.nrw.commons.\n\nStep 4: Tap it and select \'Don\'t optimize\'.\n\nStep 5: Press \'Done\'. Authentication failed, please login again Upload started! Upload queued (limited connection mode enabled) From 3ff04d396268837206abe08184c21e89f0babe46 Mon Sep 17 00:00:00 2001 From: Ritika Date: Mon, 21 Aug 2023 13:20:00 +0530 Subject: [PATCH 18/23] modify battery optimisation popup instructions, add comments and rename firstBigUploadSet --- .../commons/contributions/MainActivity.java | 2 +- .../nrw/commons/upload/UploadActivity.java | 19 ++++++++++++++++--- app/src/main/res/values/strings.xml | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index 3fffdd5394..44d4093105 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -149,7 +149,7 @@ public void onCreate(Bundle savedInstanceState) { setUpLoggedOutPager(); } else { if (applicationKvStore.getBoolean("firstrun", true)) { - applicationKvStore.putBoolean("firstBigUploadSet", true); + applicationKvStore.putBoolean("hasAlreadyLaunchedBigMultiupload", false); } if(savedInstanceState == null){ //starting a fresh fragment. diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index bc198a94f4..b2acb2451f 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -376,9 +376,12 @@ private void receiveSharedItems() { .getQuantityString(R.plurals.upload_count_title, uploadableFiles.size(), uploadableFiles.size())); fragments = new ArrayList<>(); - // Suggest users to switch to Unrestricted battery usage mode + /* Suggest users to turn battery optimisation off when uploading more than a few files. + That's because we have noticed that many-files uploads have + a much higher probability of failing than uploads with less files. + */ if (uploadableFiles.size() > 3 - && defaultKvStore.getBoolean("firstBigUploadSet")) { + && !defaultKvStore.getBoolean("hasAlreadyLaunchedBigMultiupload")) { DialogUtil.showAlertDialog( this, getString(R.string.unrestricted_battery_mode), @@ -386,13 +389,23 @@ private void receiveSharedItems() { getString(R.string.title_activity_settings), getString(R.string.cancel), () -> { + /* Since opening the right settings page might be device dependent, using + https://github.com/WaseemSabir/BatteryPermissionHelper + directly appeared like a promising idea. + However, this simply closed the popup and did not make + the settings page appear on a Pixel as well as a Xiaomi device. + + Used the standard intent instead of using this library as + it shows a list of all the apps on the device and allows users to + turn battery optimisation off. + */ Intent batteryOptimisationSettingsIntent = new Intent( Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); startActivity(batteryOptimisationSettingsIntent); }, () -> {} ); - defaultKvStore.putBoolean("firstBigUploadSet", false); + defaultKvStore.putBoolean("hasAlreadyLaunchedBigMultiupload", true); } for (UploadableFile uploadableFile : uploadableFiles) { UploadMediaDetailFragment uploadMediaDetailFragment = new UploadMediaDetailFragment(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 732395eb56..66d1a4744c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -78,7 +78,7 @@ File not found. Please try another file. Maximum retry limit reached! Please cancel the upload and try again Turn battery optimization off? - Uploading more than 3 images works more reliably when the battery optimization are turned off. Please turn battery optimization off for the Commons app from the settings for a smooth upload experience. \n\nPossible steps to turn battery optimization off:\n\nStep 1: Tap on the \'Settings\' button below.\n\nStep 2: Switch from \'Not optimized\' to \'All apps\'.\n\nStep 3: Scroll to find the Commons app or fr.free.nrw.commons.\n\nStep 4: Tap it and select \'Don\'t optimize\'.\n\nStep 5: Press \'Done\'. + Uploading more than 3 images works more reliably when the battery optimization is turned off. Please turn battery optimization off for the Commons app from the settings for a smooth upload experience. \n\nPossible steps to turn battery optimization off:\n\nStep 1: Tap on the \'Settings\' button below.\n\nStep 2: Switch from \'Not optimized\' to \'All apps\'.\n\nStep 3: Search for \"Commons\" or \"fr.free.nrw.commons\".\n\nStep 4: Tap it and select \'Don\'t optimize\'.\n\nStep 5: Press \'Done\'. Authentication failed, please login again Upload started! Upload queued (limited connection mode enabled) From ab2f114c4058f693f90c93dbaac654fd7e2178a5 Mon Sep 17 00:00:00 2001 From: Ritika Date: Tue, 22 Aug 2023 22:24:04 +0530 Subject: [PATCH 19/23] add filename to the retry log statement --- app/build.gradle | 1 - .../free/nrw/commons/contributions/ContributionsFragment.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7363beecc0..a7b9cf4c61 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -153,7 +153,6 @@ dependencies { annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' implementation("io.github.coordinates2country:coordinates2country-android:1.3") { exclude group: 'com.google.android', module: 'android' } - } task disableAnimations(type: Exec) { diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java index 33cc7d69d3..8c1702d296 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java @@ -620,7 +620,7 @@ public void retryUpload(Contribution contribution) { will never be successful */ if(retries < MAX_RETRIES) { contribution.setRetries(retries + 1); - Timber.d("Retried %d times", retries + 1); + Timber.d("Retried uploading %s %d times", contribution.getMedia().getFilename(), retries + 1); restartUpload(contribution); } else { // TODO: Show the exact reason for failure From faf0cded4b21564414ee063a3780ba1ea59b8219 Mon Sep 17 00:00:00 2001 From: Ritika Date: Wed, 23 Aug 2023 12:06:13 +0530 Subject: [PATCH 20/23] update database version --- app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt index 49c95343a5..6d63e58a1c 100644 --- a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt +++ b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt @@ -15,7 +15,7 @@ import fr.free.nrw.commons.upload.depicts.DepictsDao * The database for accessing the respective DAOs * */ -@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class, NotForUploadStatus::class, ReviewEntity::class], version = 15, exportSchema = false) +@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class, NotForUploadStatus::class, ReviewEntity::class], version = 16, exportSchema = false) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun contributionDao(): ContributionDao From f441eaa20463e80487d43021c7f9cf6e1fc0a5d2 Mon Sep 17 00:00:00 2001 From: Ritika Date: Wed, 23 Aug 2023 15:59:37 +0530 Subject: [PATCH 21/23] make battery optimisation dialog appear only on Android 6 and above --- .../nrw/commons/upload/UploadActivity.java | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index b2acb2451f..f59ababf81 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -7,6 +7,7 @@ import android.annotation.SuppressLint; import android.app.ProgressDialog; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.util.DisplayMetrics; @@ -379,16 +380,20 @@ private void receiveSharedItems() { /* Suggest users to turn battery optimisation off when uploading more than a few files. That's because we have noticed that many-files uploads have a much higher probability of failing than uploads with less files. + + Show the dialog for Android 6 and above as + the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS intent was added in API level 23 */ - if (uploadableFiles.size() > 3 - && !defaultKvStore.getBoolean("hasAlreadyLaunchedBigMultiupload")) { - DialogUtil.showAlertDialog( - this, - getString(R.string.unrestricted_battery_mode), - getString(R.string.suggest_unrestricted_mode), - getString(R.string.title_activity_settings), - getString(R.string.cancel), - () -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (uploadableFiles.size() > 3 + && !defaultKvStore.getBoolean("hasAlreadyLaunchedBigMultiupload")) { + DialogUtil.showAlertDialog( + this, + getString(R.string.unrestricted_battery_mode), + getString(R.string.suggest_unrestricted_mode), + getString(R.string.title_activity_settings), + getString(R.string.cancel), + () -> { /* Since opening the right settings page might be device dependent, using https://github.com/WaseemSabir/BatteryPermissionHelper directly appeared like a promising idea. @@ -399,13 +404,14 @@ private void receiveSharedItems() { it shows a list of all the apps on the device and allows users to turn battery optimisation off. */ - Intent batteryOptimisationSettingsIntent = new Intent( - Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); - startActivity(batteryOptimisationSettingsIntent); - }, - () -> {} - ); - defaultKvStore.putBoolean("hasAlreadyLaunchedBigMultiupload", true); + Intent batteryOptimisationSettingsIntent = new Intent( + Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); + startActivity(batteryOptimisationSettingsIntent); + }, + () -> {} + ); + defaultKvStore.putBoolean("hasAlreadyLaunchedBigMultiupload", true); + } } for (UploadableFile uploadableFile : uploadableFiles) { UploadMediaDetailFragment uploadMediaDetailFragment = new UploadMediaDetailFragment(); From 5ec4d56fa16786784aa182a15293b94a84854892 Mon Sep 17 00:00:00 2001 From: Ritika Date: Fri, 25 Aug 2023 00:32:49 +0530 Subject: [PATCH 22/23] use foreground service instead of setting work request as expedited --- .../contributions/ContributionsPresenter.java | 1 - .../free/nrw/commons/contributions/MainActivity.java | 1 - .../fr/free/nrw/commons/upload/UploadActivity.java | 1 - .../free/nrw/commons/upload/worker/UploadWorker.kt | 12 ++++++++++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java index d32c1cc9a6..e6fdaa11ec 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java @@ -83,7 +83,6 @@ public void saveContribution(Contribution contribution) { .build(); OneTimeWorkRequest updatedUploadRequest = new OneTimeWorkRequest .Builder(UploadWorker.class) - .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setConstraints(constraints) .setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.SECONDS) .build(); diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index 44d4093105..18a5709309 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -402,7 +402,6 @@ public void toggleLimitedConnectionMode() { .build(); OneTimeWorkRequest restartUploadsRequest = new OneTimeWorkRequest .Builder(UploadWorker.class) - .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setConstraints(constraints) .setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.SECONDS) .build(); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index f59ababf81..8279f8e74c 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -326,7 +326,6 @@ public void makeUploadRequest() { .build(); OneTimeWorkRequest uploadRequest = new OneTimeWorkRequest .Builder(UploadWorker.class) - .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setConstraints(constraints) .setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.SECONDS) .build(); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index 3da03efac2..c9591fd51a 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -166,6 +166,8 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : override suspend fun doWork(): Result { try { var countUpload = 0 + // Start a foreground service + setForeground(createForegroundInfo()) notificationManager = NotificationManagerCompat.from(appContext) val processingUploads = getNotificationBuilder( CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL @@ -235,15 +237,21 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : } } - override suspend fun getForegroundInfo(): ForegroundInfo { - // TODO: improve notifications for older Android versions + /** + * Create new notification for foreground service + */ + private fun createForegroundInfo(): ForegroundInfo { return ForegroundInfo( 1, createNotificationForForegroundService() ) } + override suspend fun getForegroundInfo(): ForegroundInfo { + return createForegroundInfo() + } private fun createNotificationForForegroundService(): Notification { + // TODO: Improve notification for foreground service return getNotificationBuilder( CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL)!! .setContentTitle(appContext.getString(R.string.upload_in_progress)) From 034d8d6ed7dc9d84ca0e86667625f34aade9130a Mon Sep 17 00:00:00 2001 From: Ritika Date: Thu, 31 Aug 2023 14:15:58 +0530 Subject: [PATCH 23/23] forbid retries for images which have got uploaded without caption --- .../nrw/commons/contributions/Contribution.kt | 3 +- .../contributions/ContributionsFragment.java | 52 +++++++++++++++++-- .../nrw/commons/upload/worker/UploadWorker.kt | 1 + app/src/main/res/values/strings.xml | 2 + 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt index b75271f054..28e5892bd3 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt @@ -47,7 +47,8 @@ data class Contribution constructor( /** * Number of times a contribution has been retried after a failure */ - var retries: Int = 0 + var retries: Int = 0, + var wikidataUpdateWasSuccessful: Boolean = false ) : Parcelable { fun completeWith(media: Media): Contribution { diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java index 8c1702d296..8276f9c741 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java @@ -29,10 +29,13 @@ import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.auth.SessionManager; +import fr.free.nrw.commons.media.MediaClient; import fr.free.nrw.commons.notification.models.Notification; import fr.free.nrw.commons.notification.NotificationController; import fr.free.nrw.commons.profile.ProfileActivity; import fr.free.nrw.commons.theme.BaseActivity; +import fr.free.nrw.commons.upload.FileUtilsWrapper; +import java.io.FileNotFoundException; import java.util.Date; import java.util.List; import javax.inject.Inject; @@ -85,6 +88,10 @@ public class ContributionsFragment @Inject CampaignsPresenter presenter; @Inject LocationServiceManager locationManager; @Inject NotificationController notificationController; + @Inject + MediaClient mediaClient; + @Inject + FileUtilsWrapper fileUtilsWrapper; private CompositeDisposable compositeDisposable = new CompositeDisposable(); @@ -598,11 +605,50 @@ public void notifyDataSetChanged() { * Restarts the upload process for a contribution * @param contribution */ - public void restartUpload(Contribution contribution) { + private void restartUpload(Contribution contribution) { contribution.setState(Contribution.STATE_QUEUED); contributionsPresenter.saveContribution(contribution); Timber.d("Restarting for %s", contribution.toString()); } + + /** + * Retry for a failed contribution only if it does not exist on the server + * @param contribution + * @param retries + */ + private void retryFailedUpload(Contribution contribution, int retries) { + String filePath = contribution.getLocalUri().getPath(); + String fileSHA; + try { + fileSHA = fileUtilsWrapper.getSHA1(fileUtilsWrapper.getFileInputStream(filePath)); + } catch (FileNotFoundException e) { + fileSHA = null; + } + boolean fileExists = mediaClient + .checkFileExistsUsingSha(fileSHA) + .subscribeOn(Schedulers.io()).blockingGet(); + if (fileExists && !contribution.getWikidataUpdateWasSuccessful()) { + Timber.d("%s exists on the server already but lacks caption", + contribution.getMedia().getFilename()); + // Show a popup to the user to edit the caption + DialogUtil.showAlertDialog( + getActivity(), + getString(R.string.wikidata_edit_failed_title), + getString(R.string.wikidata_edit_failed_explanation, contribution.getMedia().getFilename()), + getString(R.string.ok), + () -> {}, + true + ); + contribution.setState(Contribution.STATE_COMPLETED); + contributionsPresenter.saveContribution(contribution); + } else { + contribution.setRetries(retries + 1); + Timber.d("Retried uploading %s %d times", + contribution.getMedia().getFilename(), retries + 1); + restartUpload(contribution); + } + } + /** * Retry upload when it is failed * @@ -619,9 +665,7 @@ public void retryUpload(Contribution contribution) { to handle cases like invalid filename as such uploads will never be successful */ if(retries < MAX_RETRIES) { - contribution.setRetries(retries + 1); - Timber.d("Retried uploading %s %d times", contribution.getMedia().getFilename(), retries + 1); - restartUpload(contribution); + retryFailedUpload(contribution, retries); } else { // TODO: Show the exact reason for failure Toast.makeText(getContext(), diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index c9591fd51a..4761f48c5d 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -409,6 +409,7 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : contribution.media.captions ) if (null != revisionID) { + contribution.wikidataUpdateWasSuccessful = true showSuccessNotification(contribution) } }catch (exception: Exception){ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 66d1a4744c..647d20e81b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -63,6 +63,8 @@ Settings Upload to Commons Upload in progress + Wikidata edit failed + Wikidata edit failed for the contribution %s. Please edit its caption and depiction. Username Password Log in to your Commons Beta account