Skip to content

Commit ecbff7e

Browse files
Fixes commons-app#3790- Use WorkManagers to upload contributions (commons-app#4298)
* Fixes commons-app#3790 Use WorkManagers to process upload contributions ** Removed UploadService and Added UploadWorker to process contributions Upload ** Made nescessary changes to remove the usages of the Service from the classes ** UI Fxies- Minor changes in the retry and cancel uplaod icons to give them a clickable area of 48 dp * Fixes commons-app#3790 Use WorkManagers to process upload contributions ** Removed UploadService and Added UploadWorker to process contributions Upload ** Made nescessary changes to remove the usages of the Service from the classes ** UI Fxies- Minor changes in the retry and cancel uplaod icons to give them a clickable area of 48 dp * Updated JavaDocs in UploadWorker, Fixed Test cases * Updated JavaDocs in UploadWorker, Fixed Test cases * Updated gradle * Revert "Updated gradle" This reverts commit c8979fe. * rolledback to compileSDKVersion 28, fixed tests * Don't call the show notifications on the main thread * Bug Fix- Duplicate contributions, handle upload stash errors
1 parent fd2a7a9 commit ecbff7e

36 files changed

+692
-802
lines changed

app/build.gradle

+6
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@ dependencies {
6767
implementation "com.squareup.okhttp3:logging-interceptor:$OKHTTP_VERSION"
6868

6969
// Dependency injector
70+
implementation "com.google.dagger:dagger-android:$DAGGER_VERSION"
7071
implementation "com.google.dagger:dagger-android-support:$DAGGER_VERSION"
7172
kapt "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
7273
kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
74+
annotationProcessor "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
7375

7476
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION"
7577
implementation "org.jetbrains.kotlin:kotlin-reflect:$KOTLIN_VERSION"
@@ -145,6 +147,10 @@ dependencies {
145147
implementation "androidx.preference:preference-ktx:$PREFERENCE_VERSION"
146148

147149
implementation "androidx.multidex:multidex:$MULTIDEX_VERSION"
150+
151+
def work_version = "2.4.0"
152+
// Kotlin + coroutines
153+
implementation "androidx.work:work-runtime-ktx:$work_version"
148154
}
149155

150156
android {

app/src/main/AndroidManifest.xml

+1-2
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,7 @@
135135
android:name=".review.ReviewActivity"
136136
android:label="@string/title_activity_review" />
137137

138-
<service android:name=".upload.UploadService" />
139-
<service
138+
<service
140139
android:name=".auth.WikiAccountAuthenticatorService"
141140
android:exported="true"
142141
android:process=":auth">

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@
4747
import io.reactivex.plugins.RxJavaPlugins;
4848
import io.reactivex.schedulers.Schedulers;
4949
import java.io.File;
50+
import java.util.HashMap;
5051
import java.util.HashSet;
52+
import java.util.Map;
5153
import java.util.Set;
5254
import javax.inject.Inject;
5355
import javax.inject.Named;
@@ -126,7 +128,12 @@ public AppLanguageLookUpTable getLanguageLookUpTable() {
126128

127129
@Inject ContributionDao contributionDao;
128130

129-
/**
131+
/**
132+
* In memory list of contributios whose uploads ahve been paused by the user
133+
*/
134+
public static Map<String, Boolean> pauseUploads = new HashMap<>();
135+
136+
/**
130137
* Used to declare and initialize various components and dependencies
131138
*/
132139
@Override

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

+10
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,14 @@ public Completable logout() {
136136
currentAccount = null;
137137
});
138138
}
139+
140+
/**
141+
* Return a corresponding boolean preference
142+
*
143+
* @param key
144+
* @return
145+
*/
146+
public boolean getPreference(String key) {
147+
return defaultKvStore.getBoolean(key);
148+
}
139149
}

app/src/main/java/fr/free/nrw/commons/contributions/ChunkInfo.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import android.os.Parcelable
55
import fr.free.nrw.commons.upload.UploadResult
66

77
data class ChunkInfo(
8-
val uploadResult: UploadResult,
8+
val uploadResult: UploadResult?,
99
val indexOfNextChunkToUpload: Int,
1010
val totalChunks: Int
1111
) : Parcelable {

app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package fr.free.nrw.commons.contributions;
22

3-
import static fr.free.nrw.commons.upload.UploadService.EXTRA_FILES;
43
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
54

65
import android.Manifest;
76
import android.app.Activity;
87
import android.content.Context;
98
import android.content.Intent;
10-
import android.util.Log;
119
import androidx.annotation.NonNull;
1210
import fr.free.nrw.commons.R;
1311
import fr.free.nrw.commons.filepicker.DefaultCallback;
@@ -29,7 +27,6 @@
2927
public class ContributionController {
3028

3129
public static final String ACTION_INTERNAL_UPLOADS = "internalImageUploads";
32-
3330
private final JsonKvStore defaultKvStore;
3431

3532
@Inject
@@ -130,7 +127,8 @@ private Intent handleImagesPicked(Context context,
130127
List<UploadableFile> imagesFiles) {
131128
Intent shareIntent = new Intent(context, UploadActivity.class);
132129
shareIntent.setAction(ACTION_INTERNAL_UPLOADS);
133-
shareIntent.putParcelableArrayListExtra(EXTRA_FILES, new ArrayList<>(imagesFiles));
130+
shareIntent
131+
.putParcelableArrayListExtra(UploadActivity.EXTRA_FILES, new ArrayList<>(imagesFiles));
134132
Place place = defaultKvStore.getJson(PLACE_OBJECT, Place.class);
135133

136134
if (place != null) {

app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java

+2-11
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,6 @@ public void deleteAndSaveContribution(final Contribution oldContribution,
3939
saveSynchronous(newContribution);
4040
}
4141

42-
public Completable saveAndDelete(final Contribution oldContribution,
43-
final Contribution newContribution) {
44-
return Completable
45-
.fromAction(() -> deleteAndSaveContribution(oldContribution, newContribution));
46-
}
47-
4842
@Insert(onConflict = OnConflictStrategy.REPLACE)
4943
public abstract Single<List<Long>> save(List<Contribution> contribution);
5044

@@ -62,11 +56,8 @@ public Completable delete(final Contribution contribution) {
6256
@Query("SELECT * from contribution WHERE pageId=:pageId")
6357
public abstract Contribution getContribution(String pageId);
6458

65-
@Query("SELECT * from contribution WHERE state=:state")
66-
public abstract Single<List<Contribution>> getContribution(int state);
67-
68-
@Query("UPDATE contribution SET state=:state WHERE state in (:toUpdateStates)")
69-
public abstract Single<Integer> updateStates(int state, int[] toUpdateStates);
59+
@Query("SELECT * from contribution WHERE state IN (:states) order by media_dateUploaded DESC")
60+
public abstract Single<List<Contribution>> getContribution(List<Integer> states);
7061

7162
@Query("SELECT COUNT(*) from contribution WHERE state in (:toUpdateStates)")
7263
public abstract Single<Integer> getPendingUploads(int[] toUpdateStates);

app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package fr.free.nrw.commons.contributions;
22

3+
import android.content.Context;
34
import fr.free.nrw.commons.BasePresenter;
45

56
/**
@@ -10,6 +11,8 @@ public class ContributionsContract {
1011
public interface View {
1112

1213
void showMessage(String localizedMessage);
14+
15+
Context getContext();
1316
}
1417

1518
public interface UserActionListener extends BasePresenter<ContributionsContract.View> {
@@ -18,5 +21,6 @@ public interface UserActionListener extends BasePresenter<ContributionsContract.
1821

1922
void deleteUpload(Contribution contribution);
2023

24+
void saveContribution(Contribution contribution);
2125
}
2226
}

app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java

+17-89
Original file line numberDiff line numberDiff line change
@@ -6,49 +6,30 @@
66

77
import android.Manifest;
88
import android.annotation.SuppressLint;
9-
import android.app.Activity;
10-
import android.content.ComponentName;
119
import android.content.Context;
12-
import android.content.Intent;
13-
import android.content.ServiceConnection;
1410
import android.os.Bundle;
15-
import android.os.IBinder;
1611
import android.view.LayoutInflater;
1712
import android.view.Menu;
1813
import android.view.MenuInflater;
1914
import android.view.MenuItem;
2015
import android.view.MenuItem.OnMenuItemClickListener;
2116
import android.view.View;
22-
import android.view.View.OnClickListener;
2317
import android.view.ViewGroup;
2418
import android.widget.CheckBox;
2519
import android.widget.LinearLayout;
2620
import android.widget.TextView;
2721
import android.widget.Toast;
2822
import androidx.annotation.NonNull;
2923
import androidx.annotation.Nullable;
30-
import androidx.appcompat.widget.SwitchCompat;
3124
import androidx.fragment.app.Fragment;
3225
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
3326
import androidx.fragment.app.FragmentTransaction;
34-
35-
import fr.free.nrw.commons.CommonsApplication;
36-
import fr.free.nrw.commons.MediaDataExtractor;
37-
import fr.free.nrw.commons.auth.SessionManager;
38-
import fr.free.nrw.commons.notification.Notification;
39-
import fr.free.nrw.commons.notification.NotificationController;
40-
import fr.free.nrw.commons.theme.BaseActivity;
41-
import fr.free.nrw.commons.upload.UploadService.ServiceCallback;
42-
import io.reactivex.disposables.Disposable;
43-
import java.util.List;
44-
45-
import javax.inject.Inject;
46-
import javax.inject.Named;
47-
4827
import butterknife.BindView;
4928
import butterknife.ButterKnife;
29+
import fr.free.nrw.commons.CommonsApplication;
5030
import fr.free.nrw.commons.Media;
5131
import fr.free.nrw.commons.R;
32+
import fr.free.nrw.commons.auth.SessionManager;
5233
import fr.free.nrw.commons.campaigns.Campaign;
5334
import fr.free.nrw.commons.campaigns.CampaignView;
5435
import fr.free.nrw.commons.campaigns.CampaignsPresenter;
@@ -66,8 +47,10 @@
6647
import fr.free.nrw.commons.nearby.NearbyController;
6748
import fr.free.nrw.commons.nearby.NearbyNotificationCardView;
6849
import fr.free.nrw.commons.nearby.Place;
50+
import fr.free.nrw.commons.notification.Notification;
6951
import fr.free.nrw.commons.notification.NotificationActivity;
70-
import fr.free.nrw.commons.upload.UploadService;
52+
import fr.free.nrw.commons.notification.NotificationController;
53+
import fr.free.nrw.commons.theme.BaseActivity;
7154
import fr.free.nrw.commons.utils.ConfigUtils;
7255
import fr.free.nrw.commons.utils.DialogUtil;
7356
import fr.free.nrw.commons.utils.NetworkUtils;
@@ -77,6 +60,9 @@
7760
import io.reactivex.android.schedulers.AndroidSchedulers;
7861
import io.reactivex.disposables.CompositeDisposable;
7962
import io.reactivex.schedulers.Schedulers;
63+
import java.util.List;
64+
import javax.inject.Inject;
65+
import javax.inject.Named;
8066
import timber.log.Timber;
8167

8268
public class ContributionsFragment
@@ -85,16 +71,14 @@ public class ContributionsFragment
8571
OnBackStackChangedListener,
8672
LocationUpdateListener,
8773
MediaDetailProvider,
88-
ICampaignsView, ContributionsContract.View, Callback , ServiceCallback {
74+
ICampaignsView, ContributionsContract.View, Callback{
8975
@Inject @Named("default_preferences") JsonKvStore store;
9076
@Inject NearbyController nearbyController;
9177
@Inject OkHttpJsonApiClient okHttpJsonApiClient;
9278
@Inject CampaignsPresenter presenter;
9379
@Inject LocationServiceManager locationManager;
9480
@Inject NotificationController notificationController;
9581

96-
private UploadService uploadService;
97-
private boolean isUploadServiceConnected;
9882
private CompositeDisposable compositeDisposable = new CompositeDisposable();
9983

10084
private ContributionsListFragment contributionsListFragment;
@@ -127,33 +111,7 @@ public static ContributionsFragment newInstance() {
127111
return fragment;
128112
}
129113

130-
/**
131-
* Since we will need to use parent activity on onAuthCookieAcquired, we have to wait
132-
* fragment to be attached. Latch will be responsible for this sync.
133-
*/
134-
private ServiceConnection uploadServiceConnection = new ServiceConnection() {
135-
@Override
136-
public void onServiceConnected(ComponentName componentName, IBinder binder) {
137-
uploadService = (UploadService) ((UploadService.UploadServiceLocalBinder) binder)
138-
.getService();
139-
uploadService.setServiceCallback(ContributionsFragment.this);
140-
isUploadServiceConnected = true;
141-
}
142-
143-
@Override
144-
public void onServiceDisconnected(ComponentName componentName) {
145-
// this should never happen
146-
Timber.e(new RuntimeException("UploadService died but the rest of the process did not!"));
147-
isUploadServiceConnected = false;
148-
}
149-
150-
@Override
151-
public void onBindingDied(final ComponentName name) {
152-
isUploadServiceConnected = false;
153-
}
154-
};
155114
private boolean shouldShowMediaDetailsFragment;
156-
private boolean isAuthCookieAcquired;
157115

158116
@Override
159117
public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -272,7 +230,6 @@ public void onAttach(Context context) {
272230
until fragment life time ends.
273231
*/
274232
if (!isFragmentAttachedBefore && getActivity() != null) {
275-
onAuthCookieAcquired();
276233
isFragmentAttachedBefore = true;
277234
}
278235
}
@@ -312,19 +269,6 @@ public void onBackStackChanged() {
312269
fetchCampaigns();
313270
}
314271

315-
/**
316-
* Called when onAuthCookieAcquired is called on authenticated parent activity
317-
*/
318-
void onAuthCookieAcquired() {
319-
// Since we call onAuthCookieAcquired method from onAttach, isAdded is still false. So don't use it
320-
isAuthCookieAcquired=true;
321-
if (getActivity() != null) { // If fragment is attached to parent activity
322-
getActivity().bindService(getUploadServiceIntent(), uploadServiceConnection, Context.BIND_AUTO_CREATE);
323-
isUploadServiceConnected = true;
324-
}
325-
326-
}
327-
328272
private void initFragments() {
329273
if (null == contributionsListFragment) {
330274
contributionsListFragment = new ContributionsListFragment();
@@ -381,13 +325,6 @@ public void removeFragment(Fragment fragment) {
381325
getChildFragmentManager().executePendingTransactions();
382326
}
383327

384-
385-
public Intent getUploadServiceIntent(){
386-
Intent intent = new Intent(getActivity(), UploadService.class);
387-
intent.setAction(UploadService.ACTION_START_SERVICE);
388-
return intent;
389-
}
390-
391328
@SuppressWarnings("ConstantConditions")
392329
private void setUploadCount() {
393330
compositeDisposable.add(okHttpJsonApiClient
@@ -524,14 +461,6 @@ public void onDestroy() {
524461
locationManager.unregisterLocationManager();
525462
locationManager.removeLocationListener(this);
526463
super.onDestroy();
527-
528-
if (isUploadServiceConnected) {
529-
if (getActivity() != null) {
530-
uploadService.setServiceCallback(null);
531-
getActivity().unbindService(uploadServiceConnection);
532-
isUploadServiceConnected = false;
533-
}
534-
}
535464
} catch (IllegalArgumentException | IllegalStateException exception) {
536465
Timber.e(exception);
537466
}
@@ -594,7 +523,6 @@ private void fetchCampaigns() {
594523

595524
@Override public void onDestroyView() {
596525
super.onDestroyView();
597-
isUploadServiceConnected = false;
598526
presenter.onDetachView();
599527
}
600528

@@ -606,8 +534,9 @@ private void fetchCampaigns() {
606534
@Override
607535
public void retryUpload(Contribution contribution) {
608536
if (NetworkUtils.isInternetConnectionEstablished(getContext())) {
609-
if (contribution.getState() == STATE_FAILED || contribution.getState() == STATE_PAUSED || contribution.getState()==Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE && null != uploadService) {
610-
uploadService.queue(contribution);
537+
if (contribution.getState() == STATE_FAILED || contribution.getState() == STATE_PAUSED || contribution.getState()==Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE) {
538+
contribution.setState(Contribution.STATE_QUEUED);
539+
contributionsPresenter.saveContribution(contribution);
611540
Timber.d("Restarting for %s", contribution.toString());
612541
} else {
613542
Timber.d("Skipping re-upload for non-failed %s", contribution.toString());
@@ -624,7 +553,11 @@ public void retryUpload(Contribution contribution) {
624553
*/
625554
@Override
626555
public void pauseUpload(Contribution contribution) {
627-
uploadService.pauseUpload(contribution);
556+
//Pause the upload in the global singleton
557+
CommonsApplication.pauseUploads.put(contribution.getPageId(), true);
558+
//Retain the paused state in DB
559+
contribution.setState(STATE_PAUSED);
560+
contributionsPresenter.saveContribution(contribution);
628561
}
629562

630563
/**
@@ -677,10 +610,5 @@ public void backButtonClicked() {
677610
public MediaDetailPagerFragment getMediaDetailPagerFragment() {
678611
return mediaDetailPagerFragment;
679612
}
680-
681-
@Override
682-
public void updateUploadCount() {
683-
setUploadCount();
684-
}
685613
}
686614

0 commit comments

Comments
 (0)