Skip to content

Commit

Permalink
Two stage upload process
Browse files Browse the repository at this point in the history
- split upload process and use stash
- resolve filename conflict after upload not before
- use NotificationManagerCompat; add notification tag; assign temporaty stash file name
  • Loading branch information
whym committed Mar 13, 2019
1 parent f5f8e6c commit 750a492
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -845,16 +845,46 @@ public boolean logEvents(LogBuilder[] logBuilders) {

@Override
@NonNull
public Single<UploadResult> uploadFile(String filename,
@NonNull InputStream file,
long dataLength,
String pageContents,
String editSummary,
Uri fileUri,
Uri contentProviderUri,
final ProgressListener progressListener) throws IOException {
public Single<UploadStash> uploadFile(
String filename,
@NonNull InputStream file,
long dataLength,
Uri fileUri,
Uri contentProviderUri,
ProgressListener progressListener) throws IOException {
return Single.fromCallable(() -> {
CustomApiResult result = api.upload(filename, file, dataLength, pageContents, editSummary, getEditToken(), progressListener::onProgress);
CustomApiResult result = api.uploadToStash(filename, file, dataLength, getEditToken(), progressListener::onProgress);

Timber.wtf("Result: " + result.toString());

String resultStatus = result.getString("/api/upload/@result");
if (!resultStatus.equals("Success")) {
String errorCode = result.getString("/api/error/@code");
Timber.e(errorCode);

if (errorCode.equals(ERROR_CODE_BAD_TOKEN)) {
ViewUtil.showLongToast(context, R.string.bad_token_error_proposed_solution);
}
return new UploadStash(errorCode, resultStatus, filename, "");
} else {
String filekey = result.getString("/api/upload/@filekey");
return new UploadStash("", resultStatus, filename, filekey);
}
});
}


@Override
@NonNull
public Single<UploadResult> uploadFileFinalize(
String filename,
String filekey,
String pageContents,
String editSummary) throws IOException {
return Single.fromCallable(() -> {
CustomApiResult result = api.uploadFromStash(
filename, filekey, pageContents, editSummary,
getEditToken());

Timber.d("Result: %s", result.toString());

Expand Down
32 changes: 18 additions & 14 deletions app/src/main/java/fr/free/nrw/commons/mwapi/CustomMwApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,22 +131,13 @@ public String login(String username, String password) throws IOException {
}
}

public CustomApiResult upload(String filename, InputStream file, long length, String text, String comment, String token) throws IOException {
return this.upload(filename, file, length, text, comment, token, null);
}

public CustomApiResult upload(String filename, InputStream file, String text, String comment, String token) throws IOException {
return this.upload(filename, file, -1, text, comment, token, null);
}

public CustomApiResult upload(String filename, InputStream file, long length, String text, String comment, String token, ProgressListener uploadProgressListener) throws IOException {
Timber.d("Initiating upload for file %s", filename);
Http.HttpRequestBuilder builder = Http.multipart(apiURL)
public CustomApiResult uploadToStash(String filename, InputStream file, long length, String token, ProgressListener uploadProgressListener) throws IOException {
Timber.d("Initiating upload for file %s", filename);
Http.HttpRequestBuilder builder = Http.multipart(apiURL)
.data("action", "upload")
.data("stash", "1")
.data("token", token)
.data("text", text)
.data("ignorewarnings", "1")
.data("comment", comment)
.data("filename", filename)
.sendProgressListener(uploadProgressListener);
if (length != -1) {
Expand All @@ -155,7 +146,20 @@ public CustomApiResult upload(String filename, InputStream file, long length, St
builder.file("file", filename, file);
}

return CustomApiResult.fromRequestBuilder("uploadFile", builder, client);
return CustomApiResult.fromRequestBuilder("uploadToStash", builder, client);
}

public CustomApiResult uploadFromStash(String filename, String filekey, String text, String comment, String token) throws IOException {
Http.HttpRequestBuilder builder = Http.multipart(apiURL)
.data("action", "upload")
.data("token", token)
.data("ignorewarnings", "1")
.data("text", text)
.data("comment", comment)
.data("filename", filename)
.data("filekey", filekey);

return CustomApiResult.fromRequestBuilder("uploadFromStash", builder, client);
}

public void logout() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,13 @@ public interface MediaWikiApi {
List<String> searchCategory(String title, int offset);

@NonNull
Single<UploadResult> uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, Uri fileUri, Uri contentProviderUri, ProgressListener progressListener) throws IOException;
Single<UploadStash> uploadFile(String filename, InputStream file,
long dataLength, Uri fileUri, Uri contentProviderUri,
final ProgressListener progressListener) throws IOException;

@NonNull
Single<UploadResult> uploadFileFinalize(String filename, String filekey,
String pageContents, String editSummary) throws IOException;
@Nullable
String edit(String editToken, String processedPageContent, String filename, String summary) throws IOException;

Expand Down
70 changes: 70 additions & 0 deletions app/src/main/java/fr/free/nrw/commons/mwapi/UploadStash.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package fr.free.nrw.commons.mwapi;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

public class UploadStash {
@NonNull
private String errorCode;
@NonNull
private String resultStatus;
@NonNull
private String filename;
@NonNull
private String filekey;

@NonNull
public final String getErrorCode() {
return this.errorCode;
}

@NonNull
public final String getResultStatus() {
return this.resultStatus;
}

@NonNull
public final String getFilename() {
return this.filename;
}

@NonNull
public final String getFilekey() {
return this.filekey;
}

public UploadStash(@NonNull String errorCode, @NonNull String resultStatus, @NonNull String filename, @NonNull String filekey) {
this.errorCode = errorCode;
this.resultStatus = resultStatus;
this.filename = filename;
this.filekey = filekey;
}

public String toString() {
return "UploadStash(errorCode=" + this.errorCode + ", resultStatus=" + this.resultStatus + ", filename=" + this.filename + ", filekey=" + this.filekey + ")";
}

public int hashCode() {
return ((this.errorCode.hashCode() * 31 + this.resultStatus.hashCode()
) * 31 + this.filename.hashCode()
) * 31 + this.filekey.hashCode();
}

public boolean equals(@Nullable Object obj) {
if (this != obj) {
if (obj instanceof UploadStash) {
UploadStash that = (UploadStash)obj;
if (this.errorCode.equals(that.errorCode)
&& this.resultStatus.equals(that.resultStatus)
&& this.filename.equals(that.filename)
&& this.filekey.equals(that.filekey)) {
return true;
}
}

return false;
} else {
return true;
}
}
}
63 changes: 41 additions & 22 deletions app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.widget.Toast;

import java.io.File;
Expand All @@ -34,9 +35,8 @@
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.mwapi.UploadResult;
import fr.free.nrw.commons.wikidata.WikidataEditService;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.Single;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;

Expand All @@ -56,7 +56,7 @@ public class UploadService extends HandlerService<Contribution> {
@Inject SessionManager sessionManager;
@Inject ContributionDao contributionDao;

private NotificationManager notificationManager;
private NotificationManagerCompat notificationManager;
private NotificationCompat.Builder curNotification;
private int toUpload;

Expand Down Expand Up @@ -108,7 +108,7 @@ public void onProgress(long transferred, long total) {
} else {
curNotification.setProgress(100, (int) (((double) transferred / (double) total) * 100), false);
}
notificationManager.notify(NOTIFICATION_UPLOAD_IN_PROGRESS, curNotification.build());
notificationManager.notify(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS, curNotification.build());

contribution.setTransferred(transferred);
contributionDao.save(contribution);
Expand All @@ -126,7 +126,7 @@ public void onDestroy() {
public void onCreate() {
super.onCreate();
CommonsApplication.createNotificationChannel(getApplicationContext());
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager = NotificationManagerCompat.from(this);
curNotification = getNotificationBuilder(CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL);
}

Expand Down Expand Up @@ -154,7 +154,7 @@ public void queue(int what, Contribution contribution) {
if (curNotification != null && toUpload != 1) {
curNotification.setContentText(getResources().getQuantityString(R.plurals.uploads_pending_notification_indicator, toUpload, toUpload));
Timber.d("%d uploads left", toUpload);
notificationManager.notify(NOTIFICATION_UPLOAD_IN_PROGRESS, curNotification.build());
notificationManager.notify(contribution.getLocalUri().toString(), NOTIFICATION_UPLOAD_IN_PROGRESS, curNotification.build());
}

super.queue(what, contribution);
Expand Down Expand Up @@ -219,15 +219,12 @@ private void uploadContribution(Contribution contribution) {
curNotification.setContentTitle(getString(R.string.upload_progress_notification_title_start, contribution.getDisplayTitle()))
.setContentText(getResources().getQuantityString(R.plurals.uploads_pending_notification_indicator, toUpload, toUpload))
.setTicker(getString(R.string.upload_progress_notification_title_in_progress, contribution.getDisplayTitle()));
startForeground(NOTIFICATION_UPLOAD_IN_PROGRESS, curNotification.build());
notificationManager.notify(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS, curNotification.build());

String filename = contribution.getFilename();

try {
synchronized (unfinishedUploads) {
Timber.d("making sure of uniqueness of name: %s", filename);
filename = findUniqueFilename(filename);
unfinishedUploads.add(filename);
}

if (!mwApi.validateLogin()) {
// Need to revalidate!
if (sessionManager.revalidateAuthToken()) {
Expand All @@ -246,17 +243,39 @@ private void uploadContribution(Contribution contribution) {
getString(R.string.upload_progress_notification_title_finishing, contribution.getDisplayTitle()),
contribution
);
String stashFilename = "Temp_" + contribution.hashCode() + filename;
mwApi.uploadFile(
filename, fileInputStream,
contribution.getDataLength(), contribution.getPageContents(getApplicationContext()),
contribution.getEditSummary(), localUri,
contribution.getContentProviderUri(), notificationUpdater
)
stashFilename, fileInputStream, contribution.getDataLength(),
localUri, contribution.getContentProviderUri(), notificationUpdater)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(uploadResult -> {
.flatMap(uploadStash -> {
notificationManager.cancel(NOTIFICATION_UPLOAD_IN_PROGRESS);
Timber.d("Response is %s", uploadResult.toString());

Timber.d("Stash upload response 1 is %s", uploadStash.toString());

String resultStatus = uploadStash.getResultStatus();
if (!resultStatus.equals("Success")) {
Timber.d("Contribution upload failed. Wikidata entity won't be edited");
showFailedNotification(contribution);
return Single.never();
} else {
synchronized (unfinishedUploads) {
Timber.d("making sure of uniqueness of name: %s", filename);
String uniqueFilename = findUniqueFilename(filename);
unfinishedUploads.add(uniqueFilename);
return mwApi.uploadFileFinalize(
uniqueFilename,
uploadStash.getFilekey(),
contribution.getPageContents(getApplicationContext()),
contribution.getEditSummary());
}
}
})
.subscribe(uploadResult -> {
Timber.d("Stash upload response 2 is %s", uploadResult.toString());

notificationManager.cancel(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS);

String resultStatus = uploadResult.getResultStatus();
if (!resultStatus.equals("Success")) {
Expand All @@ -274,10 +293,10 @@ private void uploadContribution(Contribution contribution) {
contributionDao.save(contribution);
}
}, throwable -> {

throw new RuntimeException(throwable);
});
} catch (IOException e) {
Timber.d("I have a network fuckup");
Timber.w(e,"IOException during upload");
notificationManager.cancel(NOTIFICATION_UPLOAD_IN_PROGRESS);
showFailedNotification(contribution);
} finally {
Expand All @@ -300,7 +319,7 @@ private void showFailedNotification(Contribution contribution) {
.setContentTitle(getString(R.string.upload_failed_notification_title, contribution.getDisplayTitle()))
.setContentText(getString(R.string.upload_failed_notification_subtitle))
.setProgress(0, 0, false);
notificationManager.notify(NOTIFICATION_UPLOAD_FAILED, curNotification.build());
notificationManager.notify(contribution.getLocalUri().toString(), NOTIFICATION_UPLOAD_FAILED, curNotification.build());

contribution.setState(Contribution.STATE_FAILED);
contributionDao.save(contribution);
Expand Down

0 comments on commit 750a492

Please sign in to comment.