Tap to retry failed uploads implemented

UploadService can now be bound to as a local service. Yay!
This commit is contained in:
YuviPanda 2013-02-08 16:34:41 +05:30
parent 91aa440eac
commit 297f3277de
6 changed files with 104 additions and 34 deletions

View file

@ -57,6 +57,8 @@ public class CommonsApplication extends Application {
public static final String API_URL = "https://test.wikipedia.org/w/api.php";
public static final String IMAGE_URL_BASE = "https://upload.wikimedia.org/wikipedia/test";
public static final String DEFAULT_EDIT_SUMMARY = "Uploaded using Android Commons app";
public static MWApi createMWApi() {
DefaultHttpClient client = new DefaultHttpClient();

View file

@ -7,6 +7,9 @@ import java.util.Date;
public class Media implements Serializable {
protected Media() {
}
public Uri getLocalUri() {
return localUri;
}

View file

@ -41,10 +41,8 @@ public class UploadService extends Service {
private int toUpload;
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private volatile Looper uploadThreadLooper;
private volatile ServiceHandler uploadThreadHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
@ -119,14 +117,22 @@ public class UploadService extends Service {
@Override
public void onDestroy() {
super.onDestroy();
mServiceLooper.quit();
uploadThreadLooper.quit();
contributionsProviderClient.release();
Log.d("Commons", "ZOMG I AM BEING KILLED HALP!");
}
public class UploadServiceLocalBinder extends Binder {
public UploadService getService() {
return UploadService.this;
}
}
private final IBinder localBinder = new UploadServiceLocalBinder();
@Override
public IBinder onBind(Intent intent) {
return null;
return localBinder;
}
@Override
@ -135,8 +141,8 @@ public class UploadService extends Service {
HandlerThread thread = new HandlerThread("UploadService");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
uploadThreadLooper = thread.getLooper();
uploadThreadHandler = new ServiceHandler(uploadThreadLooper);
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
app = (CommonsApplication) this.getApplicationContext();
contributionsProviderClient = this.getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY);
@ -179,9 +185,20 @@ public class UploadService extends Service {
}
private void postMessage(int type, Object obj) {
Message msg = mServiceHandler.obtainMessage(type);
Message msg = uploadThreadHandler.obtainMessage(type);
msg.obj = obj;
mServiceHandler.sendMessage(msg);
uploadThreadHandler.sendMessage(msg);
}
public void queueContribution(Contribution contribution) {
contribution.setState(Contribution.STATE_QUEUED);
contribution.setTransferred(0);
contribution.setContentProviderClient(contributionsProviderClient);
contribution.save();
postMessage(ACTION_UPLOAD_FILE, contribution);
}
@Override
@ -193,13 +210,10 @@ public class UploadService extends Service {
notificationManager.notify(NOTIFICATION_UPLOAD_IN_PROGRESS, curProgressNotification);
}
Log.d("Commons", "Received startcommand");
Contribution contribution = mediaFromIntent(intent);
contribution.setState(Contribution.STATE_QUEUED);
contribution.setContentProviderClient(contributionsProviderClient);
queueContribution(contribution);
contribution.save();
postMessage(ACTION_UPLOAD_FILE, contribution);
return START_REDELIVER_INTENT;
}

View file

@ -5,6 +5,7 @@ import java.util.*;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.*;
import android.os.RemoteException;
@ -34,7 +35,7 @@ public class Contribution extends Media {
private long transferred;
public String getEditSummary() {
return editSummary;
return editSummary != null ? editSummary : CommonsApplication.DEFAULT_EDIT_SUMMARY;
}
public Uri getContentUri() {
@ -142,6 +143,26 @@ public class Contribution extends Media {
this.imageUrl = imageUrl;
}
private Contribution() {
// Empty constructor for being constructed by our static methods
}
public static Contribution fromCursor(Cursor cursor) {
// Hardcoding column positions!
Contribution c = new Contribution();
c.contentUri = ContributionsContentProvider.uriForId(cursor.getInt(0));
c.filename = cursor.getString(1);
c.localUri = TextUtils.isEmpty(cursor.getString(2)) ? null : Uri.parse(cursor.getString(2));
c.imageUrl = cursor.getString(3);
c.timestamp = cursor.getLong(4) == 0 ? null : new Date(cursor.getLong(4));
c.state = cursor.getInt(5);
c.dataLength = cursor.getLong(6);
c.dateUploaded = cursor.getLong(7) == 0 ? null : new Date(cursor.getLong(7));
c.transferred = cursor.getLong(8);
return c;
}
public static class Table {
public static final String TABLE_NAME = "contributions";
@ -156,6 +177,19 @@ public class Contribution extends Media {
public static final String COLUMN_UPLOADED = "uploaded";
public static final String COLUMN_TRANSFERRED = "transferred"; // Currently transferred number of bytes
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
public static final String[] ALL_FIELDS = {
COLUMN_ID,
COLUMN_FILENAME,
COLUMN_LOCAL_URI,
COLUMN_IMAGE_URL,
COLUMN_TIMESTAMP,
COLUMN_STATE,
COLUMN_LENGTH,
COLUMN_UPLOADED,
COLUMN_TRANSFERRED
};
private static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
+ "_id INTEGER PRIMARY KEY,"

View file

@ -3,6 +3,7 @@ package org.wikimedia.commons.contributions;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.os.IBinder;
import android.provider.MediaStore;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
@ -17,10 +18,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.*;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.Menu;
@ -53,6 +51,18 @@ public class ContributionsActivity extends AuthenticatedActivity implements Load
super(WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
}
private UploadService uploadService;
private ServiceConnection uploadServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName componentName, IBinder binder) {
uploadService = ((UploadService.UploadServiceLocalBinder)binder).getService();
}
public void onServiceDisconnected(ComponentName componentName) {
// this should never happen
throw new RuntimeException("UploadService died but the rest of the process did not!");
}
};
private class ContributionAdapter extends CursorAdapter {
private final int COLUMN_FILENAME;
@ -127,17 +137,6 @@ public class ContributionsActivity extends AuthenticatedActivity implements Load
private DisplayImageOptions contributionDisplayOptions;
private String[] CONTRIBUTIONS_PROJECTION = {
Contribution.Table.COLUMN_ID,
Contribution.Table.COLUMN_FILENAME,
Contribution.Table.COLUMN_LOCAL_URI,
Contribution.Table.COLUMN_STATE,
Contribution.Table.COLUMN_UPLOADED,
Contribution.Table.COLUMN_LENGTH,
Contribution.Table.COLUMN_TRANSFERRED,
Contribution.Table.COLUMN_IMAGE_URL
};
private String CONTRIBUTION_SELECTION = "";
/*
This sorts in the following order:
@ -171,11 +170,25 @@ public class ContributionsActivity extends AuthenticatedActivity implements Load
.cacheOnDisc()
.resetViewBeforeLoading().build();
Cursor allContributions = getContentResolver().query(ContributionsContentProvider.BASE_URI, CONTRIBUTIONS_PROJECTION, CONTRIBUTION_SELECTION, null, CONTRIBUTION_SORT);
bindService(new Intent(this, UploadService.class), uploadServiceConnection, Context.BIND_AUTO_CREATE);
Cursor allContributions = getContentResolver().query(ContributionsContentProvider.BASE_URI, Contribution.Table.ALL_FIELDS, CONTRIBUTION_SELECTION, null, CONTRIBUTION_SORT);
contributionsAdapter = new ContributionAdapter(this, allContributions, 0);
contributionsList.setAdapter(contributionsAdapter);
getSupportLoaderManager().initLoader(0, null, this);
contributionsList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> adapterView, View view, int position, long item) {
Cursor cursor = (Cursor)adapterView.getItemAtPosition(position);
Contribution c = Contribution.fromCursor(cursor);
if(c.getState() == Contribution.STATE_FAILED) {
uploadService.queueContribution(c);
Log.d("Commons", "Restarting for" + c.toContentValues().toString());
}
Log.d("Commons", "You clicked on:" + c.toContentValues().toString());
}
});
}
@Override
@ -283,7 +296,7 @@ public class ContributionsActivity extends AuthenticatedActivity implements Load
}
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
return new CursorLoader(this, ContributionsContentProvider.BASE_URI, CONTRIBUTIONS_PROJECTION, CONTRIBUTION_SELECTION, null, CONTRIBUTION_SORT);
return new CursorLoader(this, ContributionsContentProvider.BASE_URI, Contribution.Table.ALL_FIELDS, CONTRIBUTION_SELECTION, null, CONTRIBUTION_SORT);
}
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {

View file

@ -25,10 +25,14 @@ public class ContributionsContentProvider extends ContentProvider{
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
uriMatcher.addURI(AUTHORITY, BASE_PATH, CONTRIBUTIONS);
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/*", CONTRIBUTIONS_ID);
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID);
}
public static Uri uriForId(int id) {
return Uri.parse(BASE_URI.toString() + "/" + id);
}
private DBOpenHelper dbOpenHelper;
@Override
public boolean onCreate() {