mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 12:53:55 +01:00
Initial cut of EventLogging support
This commit is contained in:
parent
27ad2a6443
commit
5271cc57d1
7 changed files with 175 additions and 4 deletions
|
|
@ -56,6 +56,10 @@ public class CommonsApplication extends Application {
|
||||||
private Account currentAccount = null; // Unlike a savings account...
|
private Account currentAccount = null; // Unlike a savings account...
|
||||||
public static final String API_URL = "https://test.wikipedia.org/w/api.php";
|
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 IMAGE_URL_BASE = "https://upload.wikimedia.org/wikipedia/test";
|
||||||
|
public static final String EVENTLOG_URL = "https://bits.wikimedia.org/event.gif";
|
||||||
|
|
||||||
|
public static final Object[] EVENT_UPLOAD_ATTEMPT = {"MobileAppUploadAttempts", 5241449};
|
||||||
|
public static final Object[] EVENT_LOGIN_ATTEMPT = {"MobileAppLoginAttempts", 5240393};
|
||||||
|
|
||||||
public static final String DEFAULT_EDIT_SUMMARY = "Uploaded using Android Commons app";
|
public static final String DEFAULT_EDIT_SUMMARY = "Uploaded using Android Commons app";
|
||||||
|
|
||||||
|
|
|
||||||
99
commons/src/main/java/org/wikimedia/commons/EventLog.java
Normal file
99
commons/src/main/java/org/wikimedia/commons/EventLog.java
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
package org.wikimedia.commons;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import de.akquinet.android.androlog.Log;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class EventLog {
|
||||||
|
private static class LogTask extends AsyncTask<LogBuilder, Void, Boolean> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(LogBuilder... logBuilders) {
|
||||||
|
|
||||||
|
boolean allSuccess = true;
|
||||||
|
|
||||||
|
// Going to simply use the default URLConnection.
|
||||||
|
// This should be as lightweight as possible, and doesn't really do any fancy stuff
|
||||||
|
for(LogBuilder logBuilder: logBuilders) {
|
||||||
|
HttpURLConnection conn;
|
||||||
|
try {
|
||||||
|
URL url = logBuilder.toUrl();
|
||||||
|
conn = (HttpURLConnection) url.openConnection();
|
||||||
|
int respCode = conn.getResponseCode();
|
||||||
|
if(respCode != 204) {
|
||||||
|
allSuccess = false;
|
||||||
|
}
|
||||||
|
Log.d("Commons", "EventLog hit " + url.toString());
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Probably just ignore for now. Can be much more robust with a service, etc later on.
|
||||||
|
// But in the interest of debugging
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return allSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LogBuilder {
|
||||||
|
private JSONObject data;
|
||||||
|
private long rev;
|
||||||
|
private String schema;
|
||||||
|
|
||||||
|
private LogBuilder(String schema, long revision) {
|
||||||
|
data = new JSONObject();
|
||||||
|
this.schema = schema;
|
||||||
|
this.rev = revision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogBuilder param(String key, Object value) {
|
||||||
|
try {
|
||||||
|
data.put(key, value);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private URL toUrl() {
|
||||||
|
JSONObject fullData = new JSONObject();
|
||||||
|
try {
|
||||||
|
fullData.put("schema", schema);
|
||||||
|
fullData.put("revision", rev);
|
||||||
|
fullData.put("wiki", "commonswiki");
|
||||||
|
fullData.put("isValid", true); // Hehe
|
||||||
|
fullData.put("event", data);
|
||||||
|
return new URL(CommonsApplication.EVENTLOG_URL + "?" + Utils.urlEncode(fullData.toString()) + ";");
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log() {
|
||||||
|
LogTask logTask = new LogTask();
|
||||||
|
Utils.executeAsyncTask(logTask, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogBuilder schema(String schema, long revision) {
|
||||||
|
return new LogBuilder(schema, revision);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogBuilder schema(Object[] schema_rev) {
|
||||||
|
if(schema_rev.length != 2) {
|
||||||
|
throw new IllegalArgumentException("Needs an object array with schema as first param and revision as second");
|
||||||
|
}
|
||||||
|
return schema((String)schema_rev[0], (Long)schema_rev[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,16 @@ public class ShareActivity extends AuthenticatedActivity {
|
||||||
|
|
||||||
private Uri mediaUri;
|
private Uri mediaUri;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
super.onBackPressed();
|
||||||
|
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT)
|
||||||
|
.param("username", app.getCurrentAccount().name)
|
||||||
|
.param("source", getIntent().getStringExtra(UploadService.EXTRA_SOURCE))
|
||||||
|
.param("result", "cancelled")
|
||||||
|
.log();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onAuthCookieAcquired(String authCookie) {
|
protected void onAuthCookieAcquired(String authCookie) {
|
||||||
super.onAuthCookieAcquired(authCookie);
|
super.onAuthCookieAcquired(authCookie);
|
||||||
|
|
@ -42,6 +52,12 @@ public class ShareActivity extends AuthenticatedActivity {
|
||||||
|
|
||||||
if(intent.getAction().equals(Intent.ACTION_SEND)) {
|
if(intent.getAction().equals(Intent.ACTION_SEND)) {
|
||||||
mediaUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
mediaUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||||
|
final String source;
|
||||||
|
if(intent.hasExtra(UploadService.EXTRA_SOURCE)) {
|
||||||
|
source = intent.getStringExtra(UploadService.EXTRA_SOURCE);
|
||||||
|
} else {
|
||||||
|
source = Contribution.SOURCE_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
final String mimeType = intent.getType();
|
final String mimeType = intent.getType();
|
||||||
if(mimeType.startsWith("image/")) {
|
if(mimeType.startsWith("image/")) {
|
||||||
|
|
@ -58,6 +74,7 @@ public class ShareActivity extends AuthenticatedActivity {
|
||||||
uploadIntent.putExtra(UploadService.EXTRA_DESCRIPTION, descEdit.getText().toString());
|
uploadIntent.putExtra(UploadService.EXTRA_DESCRIPTION, descEdit.getText().toString());
|
||||||
uploadIntent.putExtra(UploadService.EXTRA_MIMETYPE, mimeType);
|
uploadIntent.putExtra(UploadService.EXTRA_MIMETYPE, mimeType);
|
||||||
uploadIntent.putExtra(UploadService.EXTRA_EDIT_SUMMARY, "Mobile upload from Wikimedia Commons Android app");
|
uploadIntent.putExtra(UploadService.EXTRA_EDIT_SUMMARY, "Mobile upload from Wikimedia Commons Android app");
|
||||||
|
uploadIntent.putExtra(UploadService.EXTRA_SOURCE, source);
|
||||||
startService(uploadIntent);
|
startService(uploadIntent);
|
||||||
Toast startingToast = Toast.makeText(that, R.string.uploading_started, Toast.LENGTH_LONG);
|
Toast startingToast = Toast.makeText(that, R.string.uploading_started, Toast.LENGTH_LONG);
|
||||||
startingToast.show();
|
startingToast.show();
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
public static final int ACTION_UPLOAD_FILE = 1;
|
public static final int ACTION_UPLOAD_FILE = 1;
|
||||||
|
|
||||||
public static final String ACTION_START_SERVICE = EXTRA_PREFIX + ".upload";
|
public static final String ACTION_START_SERVICE = EXTRA_PREFIX + ".upload";
|
||||||
|
public static final String EXTRA_SOURCE = EXTRA_PREFIX + ".source";
|
||||||
|
|
||||||
private NotificationManager notificationManager;
|
private NotificationManager notificationManager;
|
||||||
private ContentProviderClient contributionsProviderClient;
|
private ContentProviderClient contributionsProviderClient;
|
||||||
|
|
@ -118,6 +119,7 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
String description = intent.getStringExtra(EXTRA_DESCRIPTION);
|
String description = intent.getStringExtra(EXTRA_DESCRIPTION);
|
||||||
String editSummary = intent.getStringExtra(EXTRA_EDIT_SUMMARY);
|
String editSummary = intent.getStringExtra(EXTRA_EDIT_SUMMARY);
|
||||||
String mimeType = intent.getStringExtra(EXTRA_MIMETYPE);
|
String mimeType = intent.getStringExtra(EXTRA_MIMETYPE);
|
||||||
|
String source = intent.getStringExtra(EXTRA_SOURCE);
|
||||||
Date dateCreated = null;
|
Date dateCreated = null;
|
||||||
|
|
||||||
Long length = null;
|
Long length = null;
|
||||||
|
|
@ -144,6 +146,7 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
Removed Audio implementationf or now
|
Removed Audio implementationf or now
|
||||||
} */
|
} */
|
||||||
Contribution contribution = new Contribution(mediaUri, null, filename, description, length, dateCreated, null, app.getCurrentAccount().name, editSummary);
|
Contribution contribution = new Contribution(mediaUri, null, filename, description, length, dateCreated, null, app.getCurrentAccount().name, editSummary);
|
||||||
|
contribution.setSource(source);
|
||||||
return contribution;
|
return contribution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,7 +274,14 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
|
|
||||||
String resultStatus = result.getString("/api/upload/@result");
|
String resultStatus = result.getString("/api/upload/@result");
|
||||||
if(!resultStatus.equals("Success")) {
|
if(!resultStatus.equals("Success")) {
|
||||||
|
String errorCode = result.getString("/api/error/@code");
|
||||||
showFailedNotification(contribution);
|
showFailedNotification(contribution);
|
||||||
|
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT)
|
||||||
|
.param("username", app.getCurrentAccount().name)
|
||||||
|
.param("source", contribution.getSource())
|
||||||
|
.param("result", errorCode)
|
||||||
|
.param("filename", contribution.getFilename())
|
||||||
|
.log();
|
||||||
} else {
|
} else {
|
||||||
Date dateUploaded = null;
|
Date dateUploaded = null;
|
||||||
dateUploaded = Utils.parseMWDate(result.getString("/api/upload/imageinfo/@timestamp"));
|
dateUploaded = Utils.parseMWDate(result.getString("/api/upload/imageinfo/@timestamp"));
|
||||||
|
|
@ -282,6 +292,13 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
contribution.setState(Contribution.STATE_COMPLETED);
|
contribution.setState(Contribution.STATE_COMPLETED);
|
||||||
contribution.setDateUploaded(dateUploaded);
|
contribution.setDateUploaded(dateUploaded);
|
||||||
contribution.save();
|
contribution.save();
|
||||||
|
|
||||||
|
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT)
|
||||||
|
.param("username", app.getCurrentAccount().name)
|
||||||
|
.param("source", contribution.getSource()) //FIXME
|
||||||
|
.param("filename", contribution.getFilename())
|
||||||
|
.param("result", "success")
|
||||||
|
.log();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ package org.wikimedia.commons;
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import org.apache.commons.codec.EncoderException;
|
||||||
import org.apache.commons.codec.binary.Hex;
|
import org.apache.commons.codec.binary.Hex;
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
import org.apache.commons.codec.net.URLCodec;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
import javax.xml.transform.TransformerConfigurationException;
|
import javax.xml.transform.TransformerConfigurationException;
|
||||||
|
|
@ -90,6 +92,16 @@ public class Utils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final URLCodec urlCodec = new URLCodec();
|
||||||
|
|
||||||
|
public static String urlEncode(String url) {
|
||||||
|
try {
|
||||||
|
return urlCodec.encode(url);
|
||||||
|
} catch (EncoderException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static long countBytes(InputStream stream) throws IOException {
|
public static long countBytes(InputStream stream) throws IOException {
|
||||||
long count = 0;
|
long count = 0;
|
||||||
BufferedInputStream bis = new BufferedInputStream(stream);
|
BufferedInputStream bis = new BufferedInputStream(stream);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import android.net.*;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import org.wikimedia.commons.CommonsApplication;
|
import org.wikimedia.commons.CommonsApplication;
|
||||||
|
import org.wikimedia.commons.EventLog;
|
||||||
import org.wikimedia.commons.Media;
|
import org.wikimedia.commons.Media;
|
||||||
|
|
||||||
public class Contribution extends Media {
|
public class Contribution extends Media {
|
||||||
|
|
@ -21,8 +22,15 @@ public class Contribution extends Media {
|
||||||
public static final int STATE_QUEUED = 2;
|
public static final int STATE_QUEUED = 2;
|
||||||
public static final int STATE_IN_PROGRESS = 3;
|
public static final int STATE_IN_PROGRESS = 3;
|
||||||
|
|
||||||
|
public static final String SOURCE_CAMERA = "camera";
|
||||||
|
public static final String SOURCE_GALLERY = "gallery";
|
||||||
|
public static final String SOURCE_EXTERNAL = "external";
|
||||||
|
|
||||||
private ContentProviderClient client;
|
private ContentProviderClient client;
|
||||||
private Uri contentUri;
|
private Uri contentUri;
|
||||||
|
private String source;
|
||||||
|
|
||||||
|
public EventLog.LogBuilder event;
|
||||||
|
|
||||||
public long getTransferred() {
|
public long getTransferred() {
|
||||||
return transferred;
|
return transferred;
|
||||||
|
|
@ -132,6 +140,7 @@ public class Contribution extends Media {
|
||||||
cv.put(Table.COLUMN_TIMESTAMP, getTimestamp().getTime());
|
cv.put(Table.COLUMN_TIMESTAMP, getTimestamp().getTime());
|
||||||
cv.put(Table.COLUMN_STATE, getState());
|
cv.put(Table.COLUMN_STATE, getState());
|
||||||
cv.put(Table.COLUMN_TRANSFERRED, transferred);
|
cv.put(Table.COLUMN_TRANSFERRED, transferred);
|
||||||
|
cv.put(Table.COLUMN_SOURCE, source);
|
||||||
return cv;
|
return cv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,9 +168,17 @@ public class Contribution extends Media {
|
||||||
c.dataLength = cursor.getLong(6);
|
c.dataLength = cursor.getLong(6);
|
||||||
c.dateUploaded = cursor.getLong(7) == 0 ? null : new Date(cursor.getLong(7));
|
c.dateUploaded = cursor.getLong(7) == 0 ? null : new Date(cursor.getLong(7));
|
||||||
c.transferred = cursor.getLong(8);
|
c.transferred = cursor.getLong(8);
|
||||||
|
c.source = cursor.getString(9);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSource(String source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class Table {
|
public static class Table {
|
||||||
|
|
@ -176,6 +193,7 @@ public class Contribution extends Media {
|
||||||
public static final String COLUMN_LENGTH = "length";
|
public static final String COLUMN_LENGTH = "length";
|
||||||
public static final String COLUMN_UPLOADED = "uploaded";
|
public static final String COLUMN_UPLOADED = "uploaded";
|
||||||
public static final String COLUMN_TRANSFERRED = "transferred"; // Currently transferred number of bytes
|
public static final String COLUMN_TRANSFERRED = "transferred"; // Currently transferred number of bytes
|
||||||
|
public static final String COLUMN_SOURCE = "source";
|
||||||
|
|
||||||
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
|
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
|
||||||
public static final String[] ALL_FIELDS = {
|
public static final String[] ALL_FIELDS = {
|
||||||
|
|
@ -187,7 +205,8 @@ public class Contribution extends Media {
|
||||||
COLUMN_STATE,
|
COLUMN_STATE,
|
||||||
COLUMN_LENGTH,
|
COLUMN_LENGTH,
|
||||||
COLUMN_UPLOADED,
|
COLUMN_UPLOADED,
|
||||||
COLUMN_TRANSFERRED
|
COLUMN_TRANSFERRED,
|
||||||
|
COLUMN_SOURCE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -200,7 +219,8 @@ public class Contribution extends Media {
|
||||||
+ "timestamp INTEGER,"
|
+ "timestamp INTEGER,"
|
||||||
+ "state INTEGER,"
|
+ "state INTEGER,"
|
||||||
+ "length INTEGER,"
|
+ "length INTEGER,"
|
||||||
+ "transferred INTEGER"
|
+ "transferred INTEGER,"
|
||||||
|
+ "source STRING"
|
||||||
+ ");";
|
+ ");";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,7 @@ public class ContributionsActivity extends AuthenticatedActivity implements Load
|
||||||
Log.d("Commons", "Type is " + data.getType() + " Uri is " + data.getData());
|
Log.d("Commons", "Type is " + data.getType() + " Uri is " + data.getData());
|
||||||
shareIntent.setType("image/*"); //FIXME: Find out appropriate mime type
|
shareIntent.setType("image/*"); //FIXME: Find out appropriate mime type
|
||||||
shareIntent.putExtra(Intent.EXTRA_STREAM, data.getData());
|
shareIntent.putExtra(Intent.EXTRA_STREAM, data.getData());
|
||||||
|
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_GALLERY);
|
||||||
startActivity(shareIntent);
|
startActivity(shareIntent);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -236,6 +237,7 @@ public class ContributionsActivity extends AuthenticatedActivity implements Load
|
||||||
Log.d("Commons", "Uri is " + lastGeneratedCaptureURI);
|
Log.d("Commons", "Uri is " + lastGeneratedCaptureURI);
|
||||||
shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type
|
shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type
|
||||||
shareIntent.putExtra(Intent.EXTRA_STREAM, lastGeneratedCaptureURI);
|
shareIntent.putExtra(Intent.EXTRA_STREAM, lastGeneratedCaptureURI);
|
||||||
|
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_CAMERA);
|
||||||
startActivity(shareIntent);
|
startActivity(shareIntent);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue