mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-30 22:34:02 +01:00
5136: Fix retried uploads stuck in queued state (#5272)
* fix stuck uploads * automate retries for failed uploads once the user returns to the app * UploadWorker: modify PendingIntent flag and Android version code * MainActivity: remove automatic retry logic * Revert "MainActivity: remove automatic retry logic" * set work request as expedited * handle notification for foreground service on older versions of Android * set backoff criteria for work requests * enqueue failed uploads for a retry * revert "enqueue failed uploads for a retry" * limit the number of retries for a failed upload * add a popup that suggests users to switch to unrestricted battery usage mode * take users to the battery settings page on the first big upload * take users to battery optimisation settings page using the standard intent * add instructions to the battery optimisation settings popup * remove the first usage of fr.free.nrw.commons from the popup * comply with the wording in the OS settings * modify battery optimisation popup instructions, add comments and rename firstBigUploadSet * add filename to the retry log statement * update database version * make battery optimisation dialog appear only on Android 6 and above * use foreground service instead of setting work request as expedited * fix retried uploads stuck in queued state * use MIN_BACKOFF_MILLIS constant instead of using the number 10 and add comments * factorise the creation of the new OneTimeWorkRequest at one place * ensure work requests are in accordance with the unit tests * forbid retries for images which have got uploaded without caption * add a TODO for the suggestion related to retries * revert "forbid retries for images which have got uploaded without caption"
This commit is contained in:
parent
4540f54d59
commit
81030d1e78
11 changed files with 200 additions and 47 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 class ContributionsFragment
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,23 @@ public class ContributionsFragment
|
|||
@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();
|
||||
// TODO: Improve UX. Additional details: https://github.com/commons-app/apps-android-commons/pull/5257#discussion_r1304662562
|
||||
/* 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 uploading %s %d times", contribution.getMedia().getFilename(), 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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,12 @@
|
|||
package fr.free.nrw.commons.contributions;
|
||||
|
||||
import androidx.work.ExistingWorkPolicy;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import fr.free.nrw.commons.MediaDataExtractor;
|
||||
import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener;
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule;
|
||||
import fr.free.nrw.commons.upload.worker.UploadWorker;
|
||||
import fr.free.nrw.commons.upload.worker.WorkRequestHelper;
|
||||
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;
|
||||
|
||||
|
|
@ -76,11 +70,7 @@ public class ContributionsPresenter implements UserActionListener {
|
|||
compositeDisposable.add(repository
|
||||
.save(contribution)
|
||||
.subscribeOn(ioThreadScheduler)
|
||||
.subscribe(() -> {
|
||||
WorkManager.getInstance(view.getContext().getApplicationContext())
|
||||
.enqueueUniqueWork(
|
||||
UploadWorker.class.getSimpleName(),
|
||||
ExistingWorkPolicy.KEEP, OneTimeWorkRequest.from(UploadWorker.class));
|
||||
}));
|
||||
.subscribe(() -> WorkRequestHelper.Companion.makeOneTimeWorkRequest(
|
||||
view.getContext().getApplicationContext(), ExistingWorkPolicy.KEEP)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -18,8 +19,6 @@ import androidx.appcompat.widget.Toolbar;
|
|||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.work.ExistingWorkPolicy;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
|
|
@ -44,9 +43,11 @@ import fr.free.nrw.commons.notification.NotificationController;
|
|||
import fr.free.nrw.commons.quiz.QuizChecker;
|
||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.upload.worker.UploadWorker;
|
||||
import fr.free.nrw.commons.upload.worker.WorkRequestHelper;
|
||||
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 +59,8 @@ public class MainActivity extends BaseActivity
|
|||
SessionManager sessionManager;
|
||||
@Inject
|
||||
ContributionController controller;
|
||||
@Inject
|
||||
ContributionDao contributionDao;
|
||||
@BindView(R.id.toolbar)
|
||||
Toolbar toolbar;
|
||||
@BindView(R.id.pager)
|
||||
|
|
@ -138,6 +141,9 @@ public class MainActivity extends BaseActivity
|
|||
setTitle(getString(R.string.navigation_item_explore));
|
||||
setUpLoggedOutPager();
|
||||
} else {
|
||||
if (applicationKvStore.getBoolean("firstrun", true)) {
|
||||
applicationKvStore.putBoolean("hasAlreadyLaunchedBigMultiupload", false);
|
||||
}
|
||||
if(savedInstanceState == null){
|
||||
//starting a fresh fragment.
|
||||
// Open Last opened screen if it is Contributions or Nearby, otherwise Contributions
|
||||
|
|
@ -360,6 +366,21 @@ public class MainActivity extends BaseActivity
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -369,10 +390,8 @@ public class MainActivity extends BaseActivity
|
|||
viewUtilWrapper
|
||||
.showShortToast(getBaseContext(), getString(R.string.limited_connection_enabled));
|
||||
} else {
|
||||
WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork(
|
||||
UploadWorker.class.getSimpleName(),
|
||||
ExistingWorkPolicy.APPEND_OR_REPLACE, OneTimeWorkRequest.from(UploadWorker.class));
|
||||
|
||||
WorkRequestHelper.Companion.makeOneTimeWorkRequest(getApplicationContext(),
|
||||
ExistingWorkPolicy.APPEND_OR_REPLACE);
|
||||
viewUtilWrapper
|
||||
.showShortToast(getBaseContext(), getString(R.string.limited_connection_disabled));
|
||||
}
|
||||
|
|
@ -406,6 +425,8 @@ public class MainActivity extends BaseActivity
|
|||
defaultKvStore.putBoolean("inAppCameraFirstRun", true);
|
||||
WelcomeActivity.startYourself(this);
|
||||
}
|
||||
|
||||
retryAllFailedUploads();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue