mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Merge remote-tracking branch 'ford/transcode'
Conflicts: src/org/wikimedia/commons/UploadService.java
This commit is contained in:
commit
252cc49e70
9 changed files with 315 additions and 30 deletions
|
|
@ -12,6 +12,8 @@
|
|||
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
|
||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<application
|
||||
android:name=".CommonsApplication"
|
||||
|
|
@ -34,6 +36,7 @@
|
|||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="audio/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
|
@ -53,4 +56,4 @@
|
|||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
|
|
|||
22
jni/Android.mk
Normal file
22
jni/Android.mk
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := libtranscode
|
||||
LOCAL_SRC_FILES := transcode.c
|
||||
LOCAL_SHARED_LIBRARIES := gstreamer_android
|
||||
LOCAL_LDLIBS := -landroid -llog
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
GSTREAMER_SDK_ROOT := $(GSTREAMER_SDK_ROOT_ANDROID)
|
||||
GSTREAMER_NDK_BUILD_PATH := $(GSTREAMER_SDK_ROOT)/share/gst-android/ndk-build/
|
||||
include $(GSTREAMER_NDK_BUILD_PATH)/plugins.mk
|
||||
GSTREAMER_PLUGINS := \
|
||||
$(GSTREAMER_PLUGINS_CORE) \
|
||||
$(GSTREAMER_PLUGINS_PLAYBACK) \
|
||||
debug \
|
||||
audioparsers id3demux isomp4 ogg vorbis wavparse \
|
||||
amrnb amrwbdec faad mad mpegaudioparse
|
||||
|
||||
include $(GSTREAMER_NDK_BUILD_PATH)/gstreamer.mk
|
||||
8
jni/README.gst-sdk
Normal file
8
jni/README.gst-sdk
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
* Get the GStreamer SDK for Android
|
||||
- http://docs.gstreamer.com/display/GstSDK/Installing+for+Android+development
|
||||
- user: test, pass: Jo6eem7e (these will go away once the SDK gets out of the
|
||||
beta stage)
|
||||
* Get the NDK
|
||||
* export GSTREAMER_SDK_ROOT_ANDROID=/path/to/unpacked/gstreamer/sdk
|
||||
* (in top-level source dir) <NDK path>/ndk-build
|
||||
* Continue building as usual
|
||||
148
jni/transcode.c
Normal file
148
jni/transcode.c
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#include <gst/gst.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
|
||||
static int init(void)
|
||||
{
|
||||
/* XXX: ZERO thread-safety guarantees here */
|
||||
static gboolean inited = 0;
|
||||
|
||||
if (inited)
|
||||
return 0;
|
||||
|
||||
gst_init(NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int transcode(const char *infile, const char *outfile,
|
||||
const char *profile, jobject cb_obj, JNIEnv *env)
|
||||
{
|
||||
GstElement *pipeline;
|
||||
GstBus *bus;
|
||||
GstMessage *msg;
|
||||
gchar pipeline_str[1024];
|
||||
int ret = 0;
|
||||
|
||||
init();
|
||||
|
||||
snprintf(pipeline_str, 1024,
|
||||
"filesrc location=\"%s\" ! "
|
||||
"progressreport silent=true format=percent update-freq=1 ! "
|
||||
"decodebin2 ! audioconvert ! vorbisenc ! oggmux ! "
|
||||
"filesink location=\"%s\"",
|
||||
infile, outfile);
|
||||
|
||||
pipeline = gst_parse_launch(pipeline_str, NULL);
|
||||
|
||||
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
|
||||
bus = gst_element_get_bus(pipeline);
|
||||
|
||||
for (;;) {
|
||||
msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE,
|
||||
GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_ELEMENT);
|
||||
|
||||
switch (GST_MESSAGE_TYPE(msg)) {
|
||||
case GST_MESSAGE_ELEMENT: {
|
||||
const GstStructure *s = gst_message_get_structure(msg);
|
||||
int percent;
|
||||
jclass cb_class;
|
||||
jmethodID cb_id;
|
||||
|
||||
if (!cb_obj)
|
||||
break;
|
||||
|
||||
if (!g_str_equal(gst_structure_get_name(s), "progress"))
|
||||
break;
|
||||
|
||||
gst_structure_get_int(s, "percent", &percent);
|
||||
|
||||
cb_class = (*env)->FindClass(env, "org/wikimedia/commons/Transcoder$TranscoderProgressCallback");
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "GStreamer", "Class not found");
|
||||
break;
|
||||
}
|
||||
|
||||
cb_id = (*env)->GetMethodID(env, cb_class, "transcodeProgressCb", "(I)V");
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "GStreamer", "Method not found");
|
||||
break;
|
||||
}
|
||||
|
||||
(*env)->CallVoidMethod(env, cb_obj, cb_id, percent);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "GStreamer", "Method call failed");
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case GST_MESSAGE_ERROR: {
|
||||
GError *err = NULL;
|
||||
gchar *debug_info = NULL;
|
||||
|
||||
gst_message_parse_error(msg, &err, &debug_info);
|
||||
|
||||
GST_ERROR_OBJECT(pipeline, "%s -- %s", err->message,
|
||||
debug_info ? debug_info : "no debug info");
|
||||
|
||||
g_error_free(err);
|
||||
g_free(debug_info);
|
||||
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
case GST_MESSAGE_EOS:
|
||||
goto done;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (msg != NULL)
|
||||
gst_message_unref (msg);
|
||||
|
||||
gst_object_unref (bus);
|
||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
gst_object_unref (pipeline);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
jint Java_org_wikimedia_commons_Transcoder_transcode(JNIEnv* env,
|
||||
jclass *klass, jstring infile, jstring outfile, jstring profile,
|
||||
jobject cb_obj)
|
||||
{
|
||||
const char *in;
|
||||
const char *out;
|
||||
const char *prof = NULL;
|
||||
|
||||
if (!infile || !outfile)
|
||||
return -1;
|
||||
|
||||
in = (*env)->GetStringUTFChars(env, infile, 0);
|
||||
out = (*env)->GetStringUTFChars(env, outfile, 0);
|
||||
|
||||
if (profile)
|
||||
prof = (*env)->GetStringUTFChars(env, profile, 0);
|
||||
|
||||
return transcode(in, out, prof, cb_obj, env);
|
||||
}
|
||||
|
||||
|
||||
#ifdef TEST
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 3)
|
||||
return -1;
|
||||
|
||||
transcode(argv[1], argv[2], NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -28,4 +28,10 @@
|
|||
<string name="upload_progress_notification_title_finishing">Finishing uploading %1$s</string>
|
||||
<string name="upload_failed_notification_title">Uploading %1$s failed</string>
|
||||
<string name="upload_failed_notification_subtitle">Tap to retry</string>
|
||||
|
||||
<string name="transcoding_progress_title_start">Starting %1$s Transcoding</string>
|
||||
<string name="transcoding_progress_title_in_progress">Transcoding %1$s</string>
|
||||
<string name="transcoding_progress_title_finishing">Finished Transcoding %1$s</string>
|
||||
<string name="transcoding_failed_notification_title">Failed Transcoding %1$s</string>
|
||||
<string name="transcoding_failed_notification_subtitle">Tap to try again</string>
|
||||
</resources>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package org.wikimedia.commons;
|
||||
|
||||
import java.io.IOException;
|
||||
import com.gst_sdk.GStreamer;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import javax.xml.transform.*;
|
||||
|
|
@ -39,6 +40,13 @@ public class CommonsApplication extends Application {
|
|||
public void onCreate() {
|
||||
// TODO Auto-generated method stub
|
||||
super.onCreate();
|
||||
System.loadLibrary("gstreamer_android");
|
||||
try {
|
||||
GStreamer.init(this);
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
api = createMWApi();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class ShareActivity extends AuthenticatedActivity {
|
|||
private EditText titleEdit;
|
||||
private EditText descEdit;
|
||||
|
||||
private Uri imageUri;
|
||||
private Uri mediaUri;
|
||||
|
||||
@Override
|
||||
protected void onAuthCookieAcquired(String authCookie) {
|
||||
|
|
@ -44,26 +44,29 @@ public class ShareActivity extends AuthenticatedActivity {
|
|||
final Context that = this;
|
||||
|
||||
if(intent.getAction().equals(Intent.ACTION_SEND)) {
|
||||
if(intent.getType().startsWith("image/")) {
|
||||
mediaUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
|
||||
final String mimeType = intent.getType();
|
||||
if(mimeType.startsWith("image/")) {
|
||||
ImageLoaderTask loader = new ImageLoaderTask(backgroundImageView);
|
||||
imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
loader.execute(imageUri);
|
||||
|
||||
uploadButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent uploadIntent = new Intent(getApplicationContext(), UploadService.class);
|
||||
uploadIntent.putExtra(UploadService.EXTRA_MEDIA_URI, imageUri);
|
||||
uploadIntent.putExtra(UploadService.EXTRA_TARGET_FILENAME, titleEdit.getText().toString());
|
||||
uploadIntent.putExtra(UploadService.EXTRA_DESCRIPTION, descEdit.getText().toString());
|
||||
uploadIntent.putExtra(UploadService.EXTRA_EDIT_SUMMARY, "Mobile upload from Wikimedia Commons Android app");
|
||||
startService(uploadIntent);
|
||||
Toast startingToast = Toast.makeText(that, R.string.uploading_started, Toast.LENGTH_LONG);
|
||||
startingToast.show();
|
||||
finish();
|
||||
}
|
||||
});
|
||||
loader.execute(mediaUri);
|
||||
}
|
||||
|
||||
uploadButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent uploadIntent = new Intent(getApplicationContext(), UploadService.class);
|
||||
uploadIntent.putExtra(UploadService.EXTRA_MEDIA_URI, mediaUri);
|
||||
uploadIntent.putExtra(UploadService.EXTRA_TARGET_FILENAME, titleEdit.getText().toString());
|
||||
uploadIntent.putExtra(UploadService.EXTRA_DESCRIPTION, descEdit.getText().toString());
|
||||
uploadIntent.putExtra(UploadService.EXTRA_MIMETYPE, mimeType);
|
||||
uploadIntent.putExtra(UploadService.EXTRA_EDIT_SUMMARY, "Mobile upload from Wikimedia Commons Android app");
|
||||
startService(uploadIntent);
|
||||
Toast startingToast = Toast.makeText(that, R.string.uploading_started, Toast.LENGTH_LONG);
|
||||
startingToast.show();
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
13
src/org/wikimedia/commons/Transcoder.java
Normal file
13
src/org/wikimedia/commons/Transcoder.java
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package org.wikimedia.commons;
|
||||
|
||||
public class Transcoder {
|
||||
public interface TranscoderProgressCallback {
|
||||
public void transcodeProgressCb(int percent);
|
||||
}
|
||||
|
||||
public static native int transcode(String infile, String outfile, String profile, TranscoderProgressCallback cb);
|
||||
|
||||
static {
|
||||
System.loadLibrary("transcode");
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import java.io.*;
|
|||
import java.util.Date;
|
||||
|
||||
import org.mediawiki.api.*;
|
||||
import org.wikimedia.commons.Transcoder.TranscoderProgressCallback;
|
||||
import org.wikimedia.commons.media.Media;
|
||||
|
||||
import de.mastacode.http.ProgressListener;
|
||||
|
|
@ -28,6 +29,7 @@ public class UploadService extends IntentService {
|
|||
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 CommonsApplication app;
|
||||
|
|
@ -46,6 +48,23 @@ public class UploadService extends IntentService {
|
|||
public static final int NOTIFICATION_DOWNLOAD_COMPLETE = 2;
|
||||
public static final int NOTIFICATION_UPLOAD_FAILED = 3;
|
||||
|
||||
public static final int NOTIFICATION_TRANSCODING_IN_PROGRESS = 4;
|
||||
public static final int NOTIFICATION_TRANSCODING_FAILED = 5;
|
||||
|
||||
private class TranscodeProgressListener implements TranscoderProgressCallback {
|
||||
|
||||
private Notification curNotification;
|
||||
public TranscodeProgressListener(Notification curNotification) {
|
||||
this.curNotification = curNotification;
|
||||
}
|
||||
@Override
|
||||
public void transcodeProgressCb(int percent) {
|
||||
curNotification.contentView.setProgressBar(R.id.uploadNotificationProgress, 100, percent, false);
|
||||
startForeground(NOTIFICATION_TRANSCODING_IN_PROGRESS, curNotification);
|
||||
Log.d("Commons", "Percentage: " + percent);
|
||||
}
|
||||
|
||||
}
|
||||
private class NotificationUpdateProgressListener implements ProgressListener {
|
||||
|
||||
Notification curNotification;
|
||||
|
|
@ -98,12 +117,20 @@ public class UploadService extends IntentService {
|
|||
app = (CommonsApplication)this.getApplicationContext();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
MWApi api = app.getApi();
|
||||
InputStream file;
|
||||
long length;
|
||||
InputStream file = null;
|
||||
long length = 0;
|
||||
ApiResult result;
|
||||
RemoteViews notificationView;
|
||||
|
||||
|
|
@ -112,21 +139,68 @@ public class UploadService extends IntentService {
|
|||
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);
|
||||
String notificationTag = mediaUri.toString();
|
||||
Date dateCreated = null;
|
||||
|
||||
try {
|
||||
file = this.getContentResolver().openInputStream(mediaUri);
|
||||
length = this.getContentResolver().openAssetFileDescriptor(mediaUri, "r").getLength();
|
||||
|
||||
Cursor cursor = this.getContentResolver().query(mediaUri,
|
||||
new String[] { MediaStore.Images.ImageColumns.DATE_TAKEN }, null, null, null);
|
||||
if(cursor.getCount() != 0) {
|
||||
cursor.moveToFirst();
|
||||
dateCreated = new Date(cursor.getLong(0));
|
||||
Log.d("Commons", "MimeType is " + mimeType);
|
||||
if(mimeType.startsWith("image/")) {
|
||||
file = this.getContentResolver().openInputStream(mediaUri);
|
||||
length = this.getContentResolver().openAssetFileDescriptor(mediaUri, "r").getLength();
|
||||
Cursor cursor = this.getContentResolver().query(mediaUri,
|
||||
new String[] { MediaStore.Images.ImageColumns.DATE_TAKEN }, null, null, null);
|
||||
if(cursor.getCount() != 0) {
|
||||
cursor.moveToFirst();
|
||||
dateCreated = new Date(cursor.getInt(0));
|
||||
}
|
||||
} else if (mimeType.startsWith("audio/")) {
|
||||
String srcPath = this.getRealPathFromURI(mediaUri);
|
||||
File destFile = File.createTempFile("org.wikimedia.commons", ".ogg");
|
||||
filename = filename + ".ogg";
|
||||
Log.d("Commons", "Path is " + srcPath);
|
||||
Log.d("Commons", "Dest is " + destFile.getAbsolutePath());
|
||||
|
||||
RemoteViews transcodeNotificationView = new RemoteViews(getPackageName(), R.layout.layout_upload_progress);
|
||||
transcodeNotificationView.setTextViewText(R.id.uploadNotificationTitle, String.format(getString(R.string.transcoding_progress_title_in_progress), filename));
|
||||
transcodeNotificationView.setProgressBar(R.id.uploadNotificationProgress, 100, 0, false);
|
||||
|
||||
Notification transcodeNotification = new NotificationCompat.Builder(this).setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setAutoCancel(true)
|
||||
.setOngoing(true)
|
||||
.setContent(transcodeNotificationView)
|
||||
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), 0))
|
||||
.setTicker(String.format(getString(R.string.transcoding_progress_title_in_progress), filename))
|
||||
.getNotification();
|
||||
|
||||
startForeground(NOTIFICATION_TRANSCODING_IN_PROGRESS, transcodeNotification);
|
||||
|
||||
int transcodeResult = Transcoder.transcode(srcPath, destFile.getAbsolutePath(), null, new TranscodeProgressListener(transcodeNotification));
|
||||
stopForeground(true);
|
||||
|
||||
if(transcodeResult != 0) {
|
||||
Notification failureNotification = new NotificationCompat.Builder(this).setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(PendingIntent.getService(getApplicationContext(), 0, intent, 0))
|
||||
.setTicker(String.format(getString(R.string.transcoding_failed_notification_title), filename))
|
||||
.setContentTitle(String.format(getString(R.string.transcoding_failed_notification_title), filename))
|
||||
.setContentText(getString(R.string.transcoding_failed_notification_subtitle))
|
||||
.getNotification();
|
||||
notificationManager.notify(NOTIFICATION_TRANSCODING_FAILED, failureNotification);
|
||||
return;
|
||||
}
|
||||
|
||||
file = new FileInputStream(destFile);
|
||||
length = destFile.length();
|
||||
dateCreated = new Date(destFile.lastModified());
|
||||
Log.d("Commons", "Transcode doen!");
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
notificationView = new RemoteViews(getPackageName(), R.layout.layout_upload_progress);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue