Mavenized (almost) everything - Transcode feature removed

This commit is contained in:
YuviPanda 2013-01-25 03:00:36 +05:30
parent 0a493dc8a1
commit 3a313f3fb3
444 changed files with 1703 additions and 34416 deletions

View file

@ -0,0 +1,60 @@
package com.gstreamer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
import android.content.res.AssetManager;
public class GStreamer {
private static native void nativeInit(Context context) throws Exception;
public static void init(Context context) throws Exception {
nativeInit(context);
copyFonts(context);
}
private static void copyFonts(Context context) {
AssetManager assetManager = context.getAssets();
File filesDir = context.getFilesDir();
File fontsFCDir = new File (filesDir, "fontconfig");
File fontsDir = new File (fontsFCDir, "fonts");
File fontsCfg = new File (fontsFCDir, "fonts.conf");
fontsDir.mkdirs();
try {
/* Copy the config file */
copyFile (assetManager, "fontconfig/fonts.conf", fontsCfg);
/* Copy the fonts */
for(String filename : assetManager.list("fontconfig/fonts/truetype")) {
File font = new File(fontsDir, filename);
copyFile (assetManager, "fontconfig/fonts/truetype/" + filename, font);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void copyFile(AssetManager assetManager, String assetPath, File outFile) throws IOException {
InputStream in;
OutputStream out;
byte[] buffer = new byte[1024];
int read;
if (outFile.exists())
return;
in = assetManager.open(assetPath);
out = new FileOutputStream (outFile);
while((read = in.read(buffer)) != -1){
out.write(buffer, 0, read);
}
in.close();
out.flush();
out.close();
}
}

View file

@ -0,0 +1,119 @@
package org.wikimedia.commons;
import java.io.IOException;
import java.io.StringWriter;
import javax.xml.transform.*;
import android.accounts.*;
import android.app.Application;
import android.os.AsyncTask;
import android.os.Build;
import org.mediawiki.api.*;
import org.w3c.dom.Node;
import org.wikimedia.commons.auth.WikiAccountAuthenticator;
import org.apache.http.HttpVersion;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreProtocolPNames;
public class CommonsApplication extends Application {
private MWApi api;
private Account currentAccount = null; // Unlike a savings account...
public static final String API_URL = "http://test.wikipedia.org/w/api.php";
public static MWApi createMWApi() {
DefaultHttpClient client = new DefaultHttpClient();
// Because WMF servers support only HTTP/1.0. Biggest difference that
// this makes is support for Chunked Transfer Encoding.
// I have this here so if any 1.1 features start being used, it
// throws up.
client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
HttpVersion.HTTP_1_0);
return new MWApi(API_URL, client);
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
api = createMWApi();
}
public MWApi getApi() {
return api;
}
public Account getCurrentAccount() {
if(currentAccount == null) {
AccountManager accountManager = AccountManager.get(this);
Account[] allAccounts = accountManager.getAccountsByType(WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
if(allAccounts.length != 0) {
currentAccount = allAccounts[0];
}
}
return currentAccount;
}
public Boolean revalidateAuthToken() {
AccountManager accountManager = AccountManager.get(this);
Account curAccount = getCurrentAccount();
if(curAccount == null) {
return false; // This should never happen
}
accountManager.invalidateAuthToken(WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE, api.getAuthCookie());
try {
String authCookie = accountManager.blockingGetAuthToken(curAccount, "", false);
api.setAuthCookie(authCookie);
return true;
} catch (OperationCanceledException e) {
e.printStackTrace();
return false;
} catch (AuthenticatorException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public static String getStringFromDOM(Node dom) {
javax.xml.transform.Transformer transformer = null;
try {
transformer = TransformerFactory.newInstance().newTransformer();
} catch (TransformerConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerFactoryConfigurationError e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
StringWriter outputStream = new StringWriter();
javax.xml.transform.dom.DOMSource domSource = new javax.xml.transform.dom.DOMSource(dom);
javax.xml.transform.stream.StreamResult strResult = new javax.xml.transform.stream.StreamResult(outputStream);
try {
transformer.transform(domSource, strResult);
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return outputStream.toString();
}
static public <T> void executeAsyncTask(AsyncTask<T, ?, ?> task,
T... params) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
else {
task.execute(params);
}
}
}

View file

@ -0,0 +1,107 @@
package org.wikimedia.commons;
import java.net.*;
import java.io.*;
import android.content.Context;
import android.database.Cursor;
import android.graphics.*;
import android.net.Uri;
import android.os.*;
import android.provider.MediaStore;
import android.util.Log;
import android.view.WindowManager;
import android.widget.*;
class ImageLoaderTask extends AsyncTask<Uri, String, Bitmap> {
ImageView view;
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float) height / (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
return inSampleSize;
}
private int getOrientation(Uri photoUri) {
/* it's on the external media. */
Cursor cursor = view.getContext().getContentResolver().query(photoUri,
new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);
if (cursor.getCount() != 1) {
return -1;
}
cursor.moveToFirst();
return cursor.getInt(0);
}
public Bitmap getBitmap(Uri imageUri) throws FileNotFoundException {
// FIXME: Use proper window width, not device width. But should do for now!
WindowManager wm = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
int reqHeight = wm.getDefaultDisplay().getHeight();
int reqWidth = wm.getDefaultDisplay().getWidth();
int orientation = getOrientation(imageUri);
if(orientation == 90 || orientation == 270) {
// Swap height and width if this is rotated
int temp = reqHeight;
reqHeight = reqWidth;
reqWidth = temp;
}
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream bitmapStream = view.getContext().getContentResolver().openInputStream(imageUri);
BitmapFactory.decodeStream(bitmapStream, null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
// Re-get the InputStream!
bitmapStream = view.getContext().getContentResolver().openInputStream(imageUri);
Bitmap bitmap = BitmapFactory.decodeStream(bitmapStream, null, options);
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
ImageLoaderTask(ImageView view) {
this.view = view;
}
@Override
protected Bitmap doInBackground(Uri... params) {
Uri url = params[0];
Bitmap bitmap;
try {
bitmap = getBitmap(url);
} catch (IOException e) {
throw new RuntimeException(e);
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
view.setImageBitmap(result);
}
}

View file

@ -0,0 +1,127 @@
package org.wikimedia.commons;
import java.io.*;
import org.wikimedia.commons.auth.AuthenticatedActivity;
import org.wikimedia.commons.auth.WikiAccountAuthenticator;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.ImageView;
import android.support.v4.app.NavUtils;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.Window;
import android.widget.*;
import android.view.*;
public class ShareActivity extends AuthenticatedActivity {
public ShareActivity() {
super(WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
}
private CommonsApplication app;
private ImageView backgroundImageView;
private Button uploadButton;
private EditText titleEdit;
private EditText descEdit;
private Uri mediaUri;
@Override
protected void onAuthCookieAcquired(String authCookie) {
super.onAuthCookieAcquired(authCookie);
app.getApi().setAuthCookie(authCookie);
Intent intent = getIntent();
final Context that = this;
if(intent.getAction().equals(Intent.ACTION_SEND)) {
mediaUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
final String mimeType = intent.getType();
if(mimeType.startsWith("image/")) {
ImageLoaderTask loader = new ImageLoaderTask(backgroundImageView);
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();
}
});
}
}
@Override
protected void onAuthFailure() {
super.onAuthFailure();
Toast failureToast = Toast.makeText(this, R.string.authentication_failed, Toast.LENGTH_LONG);
failureToast.show();
finish();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Actionbar overlay on top of imageview (should be called before .setcontentview)
requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
setContentView(R.layout.activity_share);
app = (CommonsApplication)this.getApplicationContext();
backgroundImageView = (ImageView)findViewById(R.id.backgroundImage);
titleEdit = (EditText)findViewById(R.id.titleEdit);
descEdit = (EditText)findViewById(R.id.descEdit);
uploadButton = (Button)findViewById(R.id.uploadButton);
requestAuthToken();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.activity_share, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -0,0 +1,217 @@
package org.wikimedia.commons;
import java.io.*;
import java.util.Date;
import org.mediawiki.api.*;
import org.wikimedia.commons.media.Media;
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.text.method.DateTimeKeyListener;
import android.util.Log;
import android.view.View;
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.uploader";
public static final String EXTRA_MEDIA_URI = EXTRA_PREFIX + ".media_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 CommonsApplication app;
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;
String notificationProgressTitle;
String notificationFinishingTitle;
private int lastPercent = 0;
public NotificationUpdateProgressListener(Notification curNotification, String notificationTag, String notificationProgressTitle, String notificationFinishingTitle) {
this.curNotification = curNotification;
this.notificationTag = notificationTag;
this.notificationProgressTitle = notificationProgressTitle;
this.notificationFinishingTitle = notificationFinishingTitle;
}
@Override
public void onProgress(long transferred, long total) {
RemoteViews curView = curNotification.contentView;
if(!notificationTitleChanged) {
curView.setTextViewText(R.id.uploadNotificationTitle, notificationProgressTitle);
notificationTitleChanged = false;
startForeground(NOTIFICATION_DOWNLOAD_IN_PROGRESS, curNotification);
}
int percent =(int) ((double)transferred / (double)total * 100);
if(percent > lastPercent) {
curNotification.contentView.setProgressBar(R.id.uploadNotificationProgress, 100, percent, false);
startForeground(NOTIFICATION_DOWNLOAD_IN_PROGRESS, curNotification);
lastPercent = percent;
}
if(percent == 100) {
// Completed!
curView.setTextViewText(R.id.uploadNotificationTitle, notificationFinishingTitle);
startForeground(NOTIFICATION_DOWNLOAD_IN_PROGRESS, curNotification);
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("Commons", "ZOMG I AM BEING KILLED HALP!");
}
@Override
public void onCreate() {
super.onCreate();
notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
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 = null;
long length = 0;
ApiResult result;
RemoteViews notificationView;
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);
String notificationTag = mediaUri.toString();
Date dateCreated = null;
try {
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.getLong(0));
}
} else if (mimeType.startsWith("audio/")) {
/* Removed Audio implementationf or now */
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException 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), filename));
notificationView.setProgressBar(R.id.uploadNotificationProgress, 100, 0, false);
Log.d("Commons", "Before execution!");
Notification progressNotification = new NotificationCompat.Builder(this).setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.setAutoCancel(true)
.setContent(notificationView)
.setOngoing(true)
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), 0))
.setTicker(String.format(getString(R.string.upload_progress_notification_title_in_progress), filename))
.getNotification();
this.startForeground(NOTIFICATION_DOWNLOAD_IN_PROGRESS, progressNotification);
Log.d("Commons", "Just before");
NotificationUpdateProgressListener notificationUpdater = new NotificationUpdateProgressListener(progressNotification, notificationTag,
String.format(getString(R.string.upload_progress_notification_title_in_progress), filename),
String.format(getString(R.string.upload_progress_notification_title_finishing), filename)
);
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;
}
}
Media media = new Media(mediaUri, filename, description, editSummary, app.getCurrentAccount().name, dateCreated);
result = api.upload(filename, file, length, media.getPageContents(), editSummary, notificationUpdater);
} catch (IOException e) {
Log.d("Commons", "I have a network fuckup");
stopForeground(true);
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.upload_failed_notification_title), filename))
.setContentTitle(String.format(getString(R.string.upload_failed_notification_title), filename))
.setContentText(getString(R.string.upload_failed_notification_subtitle))
.getNotification();
notificationManager.notify(NOTIFICATION_UPLOAD_FAILED, failureNotification);
return;
}
Log.d("Commons", "Response is" + CommonsApplication.getStringFromDOM(result.getDocument()));
stopForeground(true);
String descUrl = result.getString("/api/upload/imageinfo/@descriptionurl");
Intent openUploadedPageIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(descUrl));
Notification doneNotification = new NotificationCompat.Builder(this)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(String.format(getString(R.string.upload_completed_notification_title), filename))
.setContentText(getString(R.string.upload_completed_notification_text))
.setTicker(String.format(getString(R.string.upload_completed_notification_title), filename))
.setContentIntent(PendingIntent.getActivity(this, 0, openUploadedPageIntent, 0))
.getNotification();
notificationManager.notify(notificationTag, NOTIFICATION_DOWNLOAD_COMPLETE, doneNotification);
}
}

View file

@ -0,0 +1,135 @@
package org.wikimedia.commons.auth;
import java.io.IOException;
import org.wikimedia.commons.CommonsApplication;
import com.actionbarsherlock.app.*;
import android.accounts.*;
import android.os.AsyncTask;
import android.os.Bundle;
public class AuthenticatedActivity extends SherlockActivity {
String accountType;
CommonsApplication app;
public AuthenticatedActivity(String accountType) {
this.accountType = accountType;
}
private class GetAuthCookieTask extends AsyncTask<Void, String, String> {
private Account account;
private AccountManager accountManager;
public GetAuthCookieTask(Account account, AccountManager accountManager) {
this.account = account;
this.accountManager = accountManager;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(result != null) {
onAuthCookieAcquired(result);
} else {
onAuthFailure();
}
}
@Override
protected String doInBackground(Void... params) {
try {
return accountManager.blockingGetAuthToken(account, "", false);
} catch (OperationCanceledException e) {
e.printStackTrace();
return null;
} catch (AuthenticatorException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
private class AddAccountTask extends AsyncTask<Void, String, String> {
private AccountManager accountManager;
public AddAccountTask(AccountManager accountManager) {
this.accountManager = accountManager;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(result != null) {
Account[] allAccounts =accountManager.getAccountsByType(accountType);
Account curAccount = allAccounts[0];
GetAuthCookieTask getCookieTask = new GetAuthCookieTask(curAccount, accountManager);
getCookieTask.execute();
} else {
onAuthFailure();
}
}
@Override
protected String doInBackground(Void... params) {
AccountManagerFuture<Bundle> resultFuture = accountManager.addAccount(accountType, null, null, null, AuthenticatedActivity.this, null, null);
Bundle result;
try {
result = resultFuture.getResult();
} catch (OperationCanceledException e) {
e.printStackTrace();
return null;
} catch (AuthenticatorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
if(result.containsKey(AccountManager.KEY_ACCOUNT_NAME)) {
return result.getString(AccountManager.KEY_ACCOUNT_NAME);
} else {
return null;
}
}
}
protected void requestAuthToken() {
AccountManager accountManager = AccountManager.get(this);
Account curAccount = app.getCurrentAccount();
if(curAccount == null) {
AddAccountTask addAccountTask = new AddAccountTask(accountManager);
// This AsyncTask blocks until the Login Activity returns
// And since in Android 4.x+ only one background thread runs all AsyncTasks
// And since LoginActivity can't return until it's own AsyncTask (that does the login)
// returns, we have a deadlock!
// Fixed by explicitly asking this to be executed in parallel
// See: https://groups.google.com/forum/?fromgroups=#!topic/android-developers/8M0RTFfO7-M
CommonsApplication.executeAsyncTask(addAccountTask);
} else {
GetAuthCookieTask task = new GetAuthCookieTask(curAccount, accountManager);
task.execute();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (CommonsApplication)this.getApplicationContext();
}
protected void onAuthCookieAcquired(String authCookie) {
}
protected void onAuthFailure() {
}
}

View file

@ -0,0 +1,143 @@
package org.wikimedia.commons.auth;
import java.io.IOException;
import org.wikimedia.commons.CommonsApplication;
import org.wikimedia.commons.R;
import org.wikimedia.commons.R.id;
import org.wikimedia.commons.R.layout;
import org.wikimedia.commons.R.menu;
import org.wikimedia.commons.R.string;
import android.os.AsyncTask;
import android.os.Bundle;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.ProgressDialog;
import android.util.Log;
import android.view.*;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import android.support.v4.app.NavUtils;
public class LoginActivity extends AccountAuthenticatorActivity {
public static final String PARAM_USERNAME = "org.wikimedia.commons.login.username";
private CommonsApplication app;
Button loginButton;
EditText usernameEdit;
EditText passwordEdit;
private class LoginTask extends AsyncTask<String, String, String> {
Activity context;
ProgressDialog dialog;
String username;
String password;
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Log.d("Commons", "Login done!");
if (result.equals("Success")) {
dialog.dismiss();
Toast successToast = Toast.makeText(context, R.string.login_success, Toast.LENGTH_SHORT);
successToast.show();
Account account = new Account(username, WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
boolean accountCreated = AccountManager.get(context).addAccountExplicitly(account, password, null);
Bundle extras = context.getIntent().getExtras();
if (extras != null) {
if (accountCreated) { // Pass the new account back to the account manager
AccountAuthenticatorResponse response = extras.getParcelable(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
Bundle authResult = new Bundle();
authResult.putString(AccountManager.KEY_ACCOUNT_NAME, username);
authResult.putString(AccountManager.KEY_ACCOUNT_TYPE, WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
response.onResult(authResult);
}
}
context.finish();
} else {
Toast failureToast = Toast.makeText(context, R.string.login_failed, Toast.LENGTH_LONG);
dialog.dismiss();
failureToast.show();
}
}
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(context);
dialog.setIndeterminate(true);
dialog.setTitle(getString(R.string.logging_in_title));
dialog.setMessage(getString(R.string.logging_in_message));
dialog.show();
}
LoginTask(Activity context) {
this.context = context;
}
@Override
protected String doInBackground(String... params) {
username = params[0];
password = params[1];
try {
return app.getApi().login(username, password);
} catch (IOException e) {
// Do something better!
return "Failure";
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (CommonsApplication) this.getApplicationContext();
setContentView(R.layout.activity_login);
loginButton = (Button) findViewById(R.id.loginButton);
usernameEdit = (EditText) findViewById(R.id.loginUsername);
passwordEdit = (EditText) findViewById(R.id.loginPassword);
final Activity that = this;
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String username = usernameEdit.getText().toString();
// Because Mediawiki is upercase-first-char-then-case-sensitive :)
String canonicalUsername = username.substring(0,1).toUpperCase() + username.substring(1);
String password = passwordEdit.getText().toString();
Log.d("Commons", "Login to start!");
LoginTask task = new LoginTask(that);
task.execute(canonicalUsername, password);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_login, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -0,0 +1,113 @@
package org.wikimedia.commons.auth;
import java.io.IOException;
import org.mediawiki.api.ApiResult;
import org.mediawiki.api.MWApi;
import org.wikimedia.commons.CommonsApplication;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
public static final String COMMONS_ACCOUNT_TYPE = "org.wikimedia.commons";
private Context context;
public WikiAccountAuthenticator(Context context) {
super(context);
this.context = context;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
// TODO Auto-generated method stub
final Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
// TODO Auto-generated method stub
return null;
}
private String getAuthCookie(String username, String password) throws IOException {
MWApi api = CommonsApplication.createMWApi();
String result = api.login(username, password);
if(result.equals("Success")) {
return api.getAuthCookie();
} else {
return null;
}
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
// Extract the username and password from the Account Manager, and ask
// the server for an appropriate AuthToken.
final AccountManager am = AccountManager.get(context);
final String password = am.getPassword(account);
if (password != null) {
String authCookie;
try {
authCookie = getAuthCookie(account.name, password);
} catch (IOException e) {
// Network error!
e.printStackTrace();
throw new NetworkErrorException(e);
}
if (authCookie != null) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, COMMONS_ACCOUNT_TYPE);
result.putString(AccountManager.KEY_AUTHTOKEN, authCookie);
return result;
}
}
// If we get here, then we couldn't access the user's password - so we
// need to re-prompt them for their credentials. We do that by creating
// an intent to display our AuthenticatorActivity panel.
final Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra(LoginActivity.PARAM_USERNAME, account.name);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
// TODO Auto-generated method stub
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
final Bundle result = new Bundle();
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
return result;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}
}

View file

@ -0,0 +1,23 @@
package org.wikimedia.commons.auth;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class WikiAccountAuthenticatorService extends Service{
private static WikiAccountAuthenticator wikiAccountAuthenticator = null;
@Override
public IBinder onBind(Intent intent) {
if (!intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
return null;
}
if(wikiAccountAuthenticator == null) {
wikiAccountAuthenticator = new WikiAccountAuthenticator(this);
}
return wikiAccountAuthenticator.getIBinder();
}
}

View file

@ -0,0 +1,64 @@
package org.wikimedia.commons.media;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import android.net.*;
public class Media {
private Uri mediaUri;
private String fileName;
private String editSummary;
private String mimeType;
private String description;
private String userName;
private Date dateCreated;
private Date dateUploaded;
public Media(Uri mediaUri, String fileName, String description, String editSummary, String userName, Date dateCreated) {
this.mediaUri = mediaUri;
this.fileName = fileName;
this.description = description;
this.editSummary = editSummary;
this.userName = userName;
this.dateCreated = dateCreated;
}
public Uri getMediaUri() {
return mediaUri;
}
public String getFileName() {
return fileName;
}
public String getEditSummary() {
return editSummary;
}
public String getPageContents() {
StringBuffer buffer = new StringBuffer();
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd");
buffer
.append("== {{int:filedesc}} ==\n")
.append("{{Information")
.append("|Description=").append(description)
.append("|source=").append("{{own}}")
.append("|author=[[User:").append(userName).append("]]");
if(dateCreated != null) {
buffer
.append("|date={{According to EXIF data|").append(isoFormat.format(dateCreated)).append("}}");
}
buffer
.append("}}").append("\n")
.append("== {{int:license-header}} ==\n")
.append("{{self|cc-by-sa-3.0}}")
;
return buffer.toString();
}
public String getMimeType() {
return mimeType;
}
}

View 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

View 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

View file

@ -0,0 +1,308 @@
#include <jni.h>
#include <gst/gst.h>
#include <gio/gio.h>
#include <android/log.h>
#define CAT_FMT "%s:%d:%s"
static GstClockTime _priv_gst_info_start_time;
/* Declaration of static plugins */
GST_PLUGIN_STATIC_DECLARE(coreelements);
GST_PLUGIN_STATIC_DECLARE(coreindexers);
GST_PLUGIN_STATIC_DECLARE(adder);
GST_PLUGIN_STATIC_DECLARE(app);
GST_PLUGIN_STATIC_DECLARE(audioconvert);
GST_PLUGIN_STATIC_DECLARE(audiorate);
GST_PLUGIN_STATIC_DECLARE(audioresample);
GST_PLUGIN_STATIC_DECLARE(audiotestsrc);
GST_PLUGIN_STATIC_DECLARE(ffmpegcolorspace);
GST_PLUGIN_STATIC_DECLARE(gdp);
GST_PLUGIN_STATIC_DECLARE(gio);
GST_PLUGIN_STATIC_DECLARE(pango);
GST_PLUGIN_STATIC_DECLARE(typefindfunctions);
GST_PLUGIN_STATIC_DECLARE(videorate);
GST_PLUGIN_STATIC_DECLARE(videoscale);
GST_PLUGIN_STATIC_DECLARE(videotestsrc);
GST_PLUGIN_STATIC_DECLARE(volume);
GST_PLUGIN_STATIC_DECLARE(autodetect);
GST_PLUGIN_STATIC_DECLARE(videofilter);
GST_PLUGIN_STATIC_DECLARE(uridecodebin);
GST_PLUGIN_STATIC_DECLARE(playback);
GST_PLUGIN_STATIC_DECLARE(debug);
GST_PLUGIN_STATIC_DECLARE(audioparsers);
GST_PLUGIN_STATIC_DECLARE(id3demux);
GST_PLUGIN_STATIC_DECLARE(isomp4);
GST_PLUGIN_STATIC_DECLARE(ogg);
GST_PLUGIN_STATIC_DECLARE(vorbis);
GST_PLUGIN_STATIC_DECLARE(wavparse);
GST_PLUGIN_STATIC_DECLARE(amrnb);
GST_PLUGIN_STATIC_DECLARE(amrwbdec);
GST_PLUGIN_STATIC_DECLARE(faad);
GST_PLUGIN_STATIC_DECLARE(mad);
GST_PLUGIN_STATIC_DECLARE(mpegaudioparse);
/* Declaration of static gio modules */
/* Call this function to register static plugins */
void
gst_android_register_static_plugins (void)
{
GST_PLUGIN_STATIC_REGISTER(coreelements);
GST_PLUGIN_STATIC_REGISTER(coreindexers);
GST_PLUGIN_STATIC_REGISTER(adder);
GST_PLUGIN_STATIC_REGISTER(app);
GST_PLUGIN_STATIC_REGISTER(audioconvert);
GST_PLUGIN_STATIC_REGISTER(audiorate);
GST_PLUGIN_STATIC_REGISTER(audioresample);
GST_PLUGIN_STATIC_REGISTER(audiotestsrc);
GST_PLUGIN_STATIC_REGISTER(ffmpegcolorspace);
GST_PLUGIN_STATIC_REGISTER(gdp);
GST_PLUGIN_STATIC_REGISTER(gio);
GST_PLUGIN_STATIC_REGISTER(pango);
GST_PLUGIN_STATIC_REGISTER(typefindfunctions);
GST_PLUGIN_STATIC_REGISTER(videorate);
GST_PLUGIN_STATIC_REGISTER(videoscale);
GST_PLUGIN_STATIC_REGISTER(videotestsrc);
GST_PLUGIN_STATIC_REGISTER(volume);
GST_PLUGIN_STATIC_REGISTER(autodetect);
GST_PLUGIN_STATIC_REGISTER(videofilter);
GST_PLUGIN_STATIC_REGISTER(uridecodebin);
GST_PLUGIN_STATIC_REGISTER(playback);
GST_PLUGIN_STATIC_REGISTER(debug);
GST_PLUGIN_STATIC_REGISTER(audioparsers);
GST_PLUGIN_STATIC_REGISTER(id3demux);
GST_PLUGIN_STATIC_REGISTER(isomp4);
GST_PLUGIN_STATIC_REGISTER(ogg);
GST_PLUGIN_STATIC_REGISTER(vorbis);
GST_PLUGIN_STATIC_REGISTER(wavparse);
GST_PLUGIN_STATIC_REGISTER(amrnb);
GST_PLUGIN_STATIC_REGISTER(amrwbdec);
GST_PLUGIN_STATIC_REGISTER(faad);
GST_PLUGIN_STATIC_REGISTER(mad);
GST_PLUGIN_STATIC_REGISTER(mpegaudioparse);
}
/* Call this function to load GIO modules */
void
gst_android_load_gio_modules (void)
{
}
void
gst_debug_logcat (GstDebugCategory * category, GstDebugLevel level,
const gchar * file, const gchar * function, gint line,
GObject * object, GstDebugMessage * message, gpointer unused)
{
GstClockTime elapsed;
gint android_log_level;
gchar *tag;
if (level > gst_debug_category_get_threshold (category))
return;
elapsed = GST_CLOCK_DIFF (_priv_gst_info_start_time,
gst_util_get_timestamp ());
switch (level) {
case GST_LEVEL_ERROR:
android_log_level = ANDROID_LOG_ERROR;
break;
case GST_LEVEL_WARNING:
android_log_level = ANDROID_LOG_WARN;
break;
case GST_LEVEL_INFO:
android_log_level = ANDROID_LOG_INFO;
break;
case GST_LEVEL_DEBUG:
android_log_level = ANDROID_LOG_DEBUG;
break;
default:
android_log_level = ANDROID_LOG_VERBOSE;
break;
}
tag = g_strdup_printf ("GStreamer+%s",
gst_debug_category_get_name (category));
__android_log_print (android_log_level, tag,
"%" GST_TIME_FORMAT " " CAT_FMT " %s\n", GST_TIME_ARGS (elapsed),
file, line, function, gst_debug_message_get (message));
g_free (tag);
}
static gboolean
get_application_dirs (JNIEnv * env, jobject context, gchar ** cache_dir,
gchar ** files_dir)
{
jclass context_class;
jmethodID get_cache_dir_id, get_files_dir_id;
jclass file_class;
jmethodID get_absolute_path_id;
jobject dir;
jstring abs_path;
const gchar *abs_path_str;
*cache_dir = *files_dir = NULL;
context_class = (*env)->GetObjectClass (env, context);
if (!context_class) {
return FALSE;
}
get_cache_dir_id =
(*env)->GetMethodID (env, context_class, "getCacheDir",
"()Ljava/io/File;");
get_files_dir_id =
(*env)->GetMethodID (env, context_class, "getFilesDir",
"()Ljava/io/File;");
if (!get_cache_dir_id || !get_files_dir_id) {
return FALSE;
}
file_class = (*env)->FindClass (env, "java/io/File");
get_absolute_path_id =
(*env)->GetMethodID (env, file_class, "getAbsolutePath",
"()Ljava/lang/String;");
if (!get_absolute_path_id) {
return FALSE;
}
dir = (*env)->CallObjectMethod (env, context, get_cache_dir_id);
if ((*env)->ExceptionCheck (env)) {
return FALSE;
}
if (dir) {
abs_path = (*env)->CallObjectMethod (env, dir, get_absolute_path_id);
if ((*env)->ExceptionCheck (env)) {
return FALSE;
}
abs_path_str = (*env)->GetStringUTFChars (env, abs_path, NULL);
if ((*env)->ExceptionCheck (env)) {
return FALSE;
}
*cache_dir = abs_path ? g_strdup (abs_path_str) : NULL;
(*env)->ReleaseStringUTFChars (env, abs_path, abs_path_str);
(*env)->DeleteLocalRef (env, abs_path);
(*env)->DeleteLocalRef (env, dir);
}
dir = (*env)->CallObjectMethod (env, context, get_files_dir_id);
if ((*env)->ExceptionCheck (env)) {
return FALSE;
}
if (dir) {
abs_path = (*env)->CallObjectMethod (env, dir, get_absolute_path_id);
if ((*env)->ExceptionCheck (env)) {
return FALSE;
}
abs_path_str = (*env)->GetStringUTFChars (env, abs_path, NULL);
if ((*env)->ExceptionCheck (env)) {
return FALSE;
}
*files_dir = files_dir ? g_strdup (abs_path_str) : NULL;
(*env)->ReleaseStringUTFChars (env, abs_path, abs_path_str);
(*env)->DeleteLocalRef (env, abs_path);
(*env)->DeleteLocalRef (env, dir);
}
(*env)->DeleteLocalRef (env, file_class);
(*env)->DeleteLocalRef (env, context_class);
return TRUE;
}
static void
gst_android_init (JNIEnv * env, jclass klass, jobject context)
{
gchar *cache_dir;
gchar *files_dir;
gchar *registry;
GError *error = NULL;
if (gst_is_initialized ()) {
__android_log_print (ANDROID_LOG_INFO, "GStreamer",
"GStreamer already initialized");
return;
}
if (!get_application_dirs (env, context, &cache_dir, &files_dir))
return;
if (cache_dir) {
g_setenv ("TMP", cache_dir, TRUE);
g_setenv ("TMPDIR", cache_dir, TRUE);
g_setenv ("XDG_RUNTIME_DIR", cache_dir, TRUE);
g_setenv ("XDG_CACHE_DIR", cache_dir, TRUE);
registry = g_build_filename (cache_dir, "registry.bin", NULL);
g_setenv ("GST_REGISTRY", registry, TRUE);
g_free (registry);
g_setenv ("GST_REUSE_PLUGIN_SCANNER", "no", TRUE);
/* FIXME: Should probably also set GST_PLUGIN_SCANNER and GST_PLUGIN_SYSTEM_PATH */
}
if (files_dir) {
g_setenv ("HOME", files_dir, TRUE);
g_setenv ("XDG_DATA_DIRS", files_dir, TRUE);
g_setenv ("XDG_CONFIG_DIRS", files_dir, TRUE);
}
g_free (cache_dir);
g_free (files_dir);
/* Disable this for releases if performance is important
* or increase the threshold to get more information */
gst_debug_set_active (TRUE);
gst_debug_set_default_threshold (GST_LEVEL_WARNING);
gst_debug_remove_log_function (gst_debug_log_default);
gst_debug_add_log_function ((GstLogFunction) gst_debug_logcat, NULL);
/* get time we started for debugging messages */
_priv_gst_info_start_time = gst_util_get_timestamp ();
if (!gst_init_check (NULL, NULL, &error)) {
gchar *message = g_strdup_printf ("GStreamer initialization failed: %s",
error && error->message ? error->message : "(no message)");
jclass exception_class = (*env)->FindClass (env, "java/lang/Exception");
__android_log_print (ANDROID_LOG_ERROR, "GStreamer", message);
(*env)->ThrowNew (env, exception_class, message);
g_free (message);
return;
}
gst_android_register_static_plugins ();
gst_android_load_gio_modules();
__android_log_print (ANDROID_LOG_INFO, "GStreamer",
"GStreamer initialization complete");
}
static JNINativeMethod native_methods[] = {
{"init", "(Landroid/content/Context;)V", (void *) gst_android_init}
};
jint
JNI_OnLoad (JavaVM * vm, void *reserved)
{
JNIEnv *env = NULL;
if ((*vm)->GetEnv (vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
__android_log_print (ANDROID_LOG_ERROR, "GStreamer",
"Could not retrieve JNIEnv");
return 0;
}
jclass klass = (*env)->FindClass (env, "com/gst_sdk/GStreamer");
if (!klass) {
__android_log_print (ANDROID_LOG_ERROR, "GStreamer",
"Could not retrieve class com.gst_sdk.GStreamer");
return 0;
}
if ((*env)->RegisterNatives (env, klass, native_methods,
G_N_ELEMENTS (native_methods))) {
__android_log_print (ANDROID_LOG_ERROR, "GStreamer",
"Could not register native methods for com.gst_sdk_GStreamer");
return 0;
}
return JNI_VERSION_1_4;
}

Binary file not shown.

View file

@ -0,0 +1,7 @@
package com.gst_sdk;
import android.content.Context;
public class GStreamer {
public static native void init(Context context) throws Exception;
}

View 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