apps-android-commons/commons/src/main/java/org/wikimedia/commons/UploadService.java
2013-02-04 00:34:59 +05:30

293 lines
13 KiB
Java

package org.wikimedia.commons;
import java.io.*;
import java.text.*;
import java.util.Date;
import android.support.v4.content.LocalBroadcastManager;
import org.mediawiki.api.*;
import org.wikimedia.commons.contributions.Contribution;
import org.wikimedia.commons.contributions.ContributionsActivity;
import org.wikimedia.commons.contributions.ContributionsContentProvider;
import in.yuvi.http.fluent.ProgressListener;
import android.app.*;
import android.content.*;
import android.database.Cursor;
import android.os.*;
import android.provider.MediaStore;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;
import android.net.*;
public class UploadService extends IntentService {
private static final String EXTRA_PREFIX = "org.wikimedia.commons.upload";
public static final String INTENT_CONTRIBUTION_STATE_CHANGED = EXTRA_PREFIX + ".progress";
public static final String EXTRA_CONTRIBUTION_ID = EXTRA_PREFIX + ".filename";
public static final String EXTRA_TRANSFERRED_BYTES = EXTRA_PREFIX + ".progress.transferred";
public static final String EXTRA_MEDIA_URI = EXTRA_PREFIX + ".uri";
public static final String EXTRA_TARGET_FILENAME = EXTRA_PREFIX + ".filename";
public static final String EXTRA_DESCRIPTION = EXTRA_PREFIX + ".description";
public static final String EXTRA_EDIT_SUMMARY = EXTRA_PREFIX + ".summary";
public static final String EXTRA_MIMETYPE = EXTRA_PREFIX + ".mimetype";
private NotificationManager notificationManager;
private LocalBroadcastManager localBroadcastManager;
private ContentProviderClient contributionsProviderClient;
private CommonsApplication app;
private Notification curProgressNotification;
private int toUpload;
public UploadService(String name) {
super(name);
}
public UploadService() {
super("UploadService");
}
// DO NOT HAVE NOTIFICATION ID OF 0 FOR ANYTHING
// See http://stackoverflow.com/questions/8725909/startforeground-does-not-show-my-notification
// Seriously, Android?
public static final int NOTIFICATION_DOWNLOAD_IN_PROGRESS = 1;
public static final int NOTIFICATION_DOWNLOAD_COMPLETE = 2;
public static final int NOTIFICATION_UPLOAD_FAILED = 3;
private class NotificationUpdateProgressListener implements ProgressListener {
Notification curNotification;
String notificationTag;
boolean notificationTitleChanged;
Contribution contribution;
String notificationProgressTitle;
String notificationFinishingTitle;
public NotificationUpdateProgressListener(Notification curNotification, String notificationTag, String notificationProgressTitle, String notificationFinishingTitle, Contribution contribution) {
this.curNotification = curNotification;
this.notificationTag = notificationTag;
this.notificationProgressTitle = notificationProgressTitle;
this.notificationFinishingTitle = notificationFinishingTitle;
this.contribution = contribution;
}
@Override
public void onProgress(long transferred, long total) {
Log.d("Commons", String.format("Uploaded %d of %d", transferred, total));
RemoteViews curView = curNotification.contentView;
if(!notificationTitleChanged) {
curView.setTextViewText(R.id.uploadNotificationTitle, notificationProgressTitle);
if(toUpload != 1) {
curView.setTextViewText(R.id.uploadNotificationsCount, String.format(getString(R.string.uploads_pending_notification_indicator), toUpload));
Log.d("Commons", String.format("%d uploads left", toUpload));
}
notificationTitleChanged = true;
contribution.setState(Contribution.STATE_IN_PROGRESS);
contribution.save();
}
if(transferred == total) {
// Completed!
curView.setTextViewText(R.id.uploadNotificationTitle, notificationFinishingTitle);
notificationManager.notify(NOTIFICATION_DOWNLOAD_IN_PROGRESS, curNotification);
} else {
curNotification.contentView.setProgressBar(R.id.uploadNotificationProgress, 100, (int) (((double) transferred / (double) total) * 100), false);
notificationManager.notify(NOTIFICATION_DOWNLOAD_IN_PROGRESS, curNotification);
Intent mediaUploadProgressIntent = new Intent(INTENT_CONTRIBUTION_STATE_CHANGED);
// mediaUploadProgressIntent.putExtra(EXTRA_MEDIA contribution);
mediaUploadProgressIntent.putExtra(EXTRA_TRANSFERRED_BYTES, transferred);
localBroadcastManager.sendBroadcast(mediaUploadProgressIntent);
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
contributionsProviderClient.release();
Log.d("Commons", "ZOMG I AM BEING KILLED HALP!");
}
@Override
public void onCreate() {
super.onCreate();
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
app = (CommonsApplication) this.getApplicationContext();
contributionsProviderClient = this.getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY);
}
private String getRealPathFromURI(Uri contentUri) {
String[] proj = {MediaStore.Images.Media.DATA};
CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null);
Cursor cursor = loader.loadInBackground();
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
private Contribution mediaFromIntent(Intent intent) {
Bundle extras = intent.getExtras();
Uri mediaUri = (Uri) extras.getParcelable(EXTRA_MEDIA_URI);
String filename = intent.getStringExtra(EXTRA_TARGET_FILENAME);
String description = intent.getStringExtra(EXTRA_DESCRIPTION);
String editSummary = intent.getStringExtra(EXTRA_EDIT_SUMMARY);
String mimeType = intent.getStringExtra(EXTRA_MIMETYPE);
Date dateCreated = null;
Long length = null;
try {
length = this.getContentResolver().openAssetFileDescriptor(mediaUri, "r").getLength();
} catch(FileNotFoundException e) {
throw new RuntimeException(e);
}
Log.d("Commons", "MimeType is " + mimeType);
if(mimeType.startsWith("image/")) {
Cursor cursor = this.getContentResolver().query(mediaUri,
new String[]{MediaStore.Images.ImageColumns.DATE_TAKEN}, null, null, null);
if(cursor != null && cursor.getCount() != 0) {
cursor.moveToFirst();
dateCreated = new Date(cursor.getLong(0));
} // FIXME: Alternate way of setting dateCreated if this data is not found
} /* else if (mimeType.startsWith("audio/")) {
Removed Audio implementationf or now
} */
Contribution contribution = new Contribution(mediaUri, null, filename, description, null, length, dateCreated, null, app.getCurrentAccount().name, editSummary);
return contribution;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
toUpload++;
if(curProgressNotification != null && toUpload != 1) {
curProgressNotification.contentView.setTextViewText(R.id.uploadNotificationsCount, String.format(getString(R.string.uploads_pending_notification_indicator), toUpload));
Log.d("Commons", String.format("%d uploads left", toUpload));
notificationManager.notify(NOTIFICATION_DOWNLOAD_IN_PROGRESS, curProgressNotification);
}
Contribution contribution = mediaFromIntent(intent);
contribution.setState(Contribution.STATE_QUEUED);
contribution.setContentProviderClient(contributionsProviderClient);
contribution.save();
Intent mediaUploadQueuedIntent = new Intent();
mediaUploadQueuedIntent.putExtra("dummy-data", contribution); // FIXME: Move to separate handler, do not inherit from IntentService
localBroadcastManager.sendBroadcast(mediaUploadQueuedIntent);
return super.onStartCommand(mediaUploadQueuedIntent, flags, startId);
}
@Override
protected void onHandleIntent(Intent intent) {
MWApi api = app.getApi();
ApiResult result;
RemoteViews notificationView;
Contribution contribution;
InputStream file = null;
contribution = (Contribution) intent.getSerializableExtra("dummy-data");
String notificationTag = contribution.getLocalUri().toString();
try {
file = this.getContentResolver().openInputStream(contribution.getLocalUri());
} catch(FileNotFoundException e) {
throw new RuntimeException(e);
}
notificationView = new RemoteViews(getPackageName(), R.layout.layout_upload_progress);
notificationView.setTextViewText(R.id.uploadNotificationTitle, String.format(getString(R.string.upload_progress_notification_title_start), contribution.getFilename()));
notificationView.setProgressBar(R.id.uploadNotificationProgress, 100, 0, false);
Log.d("Commons", "Before execution!");
curProgressNotification = new NotificationCompat.Builder(this).setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.setAutoCancel(true)
.setContent(notificationView)
.setOngoing(true)
.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, ContributionsActivity.class), 0))
.setTicker(String.format(getString(R.string.upload_progress_notification_title_in_progress), contribution.getFilename()))
.getNotification();
this.startForeground(NOTIFICATION_DOWNLOAD_IN_PROGRESS, curProgressNotification);
Log.d("Commons", "Just before");
try {
if(!api.validateLogin()) {
// Need to revalidate!
if(app.revalidateAuthToken()) {
Log.d("Commons", "Successfully revalidated token!");
} else {
Log.d("Commons", "Unable to revalidate :(");
// TODO: Put up a new notification, ask them to re-login
stopForeground(true);
Toast failureToast = Toast.makeText(this, R.string.authentication_failed, Toast.LENGTH_LONG);
failureToast.show();
return;
}
}
NotificationUpdateProgressListener notificationUpdater = new NotificationUpdateProgressListener(curProgressNotification, notificationTag,
String.format(getString(R.string.upload_progress_notification_title_in_progress), contribution.getFilename()),
String.format(getString(R.string.upload_progress_notification_title_finishing), contribution.getFilename()),
contribution
);
result = api.upload(contribution.getFilename(), file, contribution.getDataLength(), contribution.getPageContents(), contribution.getEditSummary(), notificationUpdater);
} catch(IOException e) {
Log.d("Commons", "I have a network fuckup");
showFailedNotification(contribution);
return;
} finally {
toUpload--;
}
Log.d("Commons", "Response is" + CommonsApplication.getStringFromDOM(result.getDocument()));
stopForeground(true);
curProgressNotification = null;
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); // Assuming MW always gives me UTC
String resultStatus = result.getString("/api/upload/@result");
if(!resultStatus.equals("success")) {
showFailedNotification(contribution);
} else {
Date dateUploaded = null;
try {
dateUploaded = isoFormat.parse(result.getString("/api/upload/imageinfo/@timestamp"));
} catch(java.text.ParseException e) {
throw new RuntimeException(e); // Hopefully mediawiki doesn't give me bogus stuff?
}
contribution.setState(Contribution.STATE_COMPLETED);
contribution.setDateUploaded(dateUploaded);
contribution.save();
}
}
private void showFailedNotification(Contribution contribution) {
stopForeground(true);
Notification failureNotification = new NotificationCompat.Builder(this).setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.setAutoCancel(true)
.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, ContributionsActivity.class), 0))
.setTicker(String.format(getString(R.string.upload_failed_notification_title), contribution.getFilename()))
.setContentTitle(String.format(getString(R.string.upload_failed_notification_title), contribution.getFilename()))
.setContentText(getString(R.string.upload_failed_notification_subtitle))
.getNotification();
notificationManager.notify(NOTIFICATION_UPLOAD_FAILED, failureNotification);
contribution.setState(Contribution.STATE_FAILED);
contribution.save();
}
}