Mavenized (almost) everything - Transcode feature removed
59
commons/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.wikimedia.commons"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="8"
|
||||
android:targetSdkVersion="15" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
|
||||
<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"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Commons" >
|
||||
<activity
|
||||
android:name=".auth.LoginActivity"
|
||||
android:label="@string/title_activity_login"
|
||||
android:theme="@style/NoTitle" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ShareActivity"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/title_activity_share"
|
||||
android:theme="@style/NoTitle">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="audio/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service android:name="UploadService" >
|
||||
</service>
|
||||
<service
|
||||
android:name=".auth.WikiAccountAuthenticatorService"
|
||||
android:exported="true"
|
||||
android:process=":auth" >
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/authenticator" />
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
19
commons/androlog/androlog.properties
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Androlog configuration
|
||||
# this file must be pushed to /sdcard/androlog.properties:
|
||||
# adb push androlog.properties /sdcard/androlog.properties
|
||||
# more detail on: https://github.com/akquinet/androlog/wiki/Androlog-configuration
|
||||
androlog.active = true
|
||||
|
||||
#Configure the default log level:
|
||||
#androlog.default.level=INFO
|
||||
|
||||
#Configure specific loggers
|
||||
|
||||
#Enable the reporting
|
||||
#More info on: https://github.com/akquinet/androlog/wiki/Androlog-reporting
|
||||
#androlog.report.active=true
|
||||
#androlog.report.reporters=de.akquinet.android.androlog.reporter.MailReporter
|
||||
#androlog.reporter.mail.address=me@my.company.com
|
||||
#androlog.report.default.level=WARN
|
||||
#androlog.report.log.items=40
|
||||
#androlog.report.trigger.level=ERROR
|
||||
126
commons/assets/fontconfig/fonts.conf
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
<!-- /etc/fonts/fonts.conf file to configure system font access -->
|
||||
<fontconfig>
|
||||
|
||||
<!-- Font directory list -->
|
||||
|
||||
<dir prefix="xdg">fontconfig/fonts</dir>
|
||||
|
||||
<!-- Font cache directory list -->
|
||||
|
||||
<cachedir prefix="xdg">fontconfig</cachedir>
|
||||
|
||||
<!--
|
||||
Accept deprecated 'mono' alias, replacing it with 'monospace'
|
||||
-->
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>mono</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>monospace</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!--
|
||||
Accept alternate 'sans serif' spelling, replacing it with 'sans-serif'
|
||||
-->
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>sans serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>sans-serif</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!--
|
||||
Accept deprecated 'sans' alias, replacing it with 'sans-serif'
|
||||
-->
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>sans</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>sans-serif</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<config>
|
||||
<!--
|
||||
These are the default Unicode chars that are expected to be blank
|
||||
in fonts. All other blank chars are assumed to be broken and
|
||||
won't appear in the resulting charsets
|
||||
-->
|
||||
<blank>
|
||||
<int>0x0020</int> <!-- SPACE -->
|
||||
<int>0x00A0</int> <!-- NO-BREAK SPACE -->
|
||||
<int>0x00AD</int> <!-- SOFT HYPHEN -->
|
||||
<int>0x034F</int> <!-- COMBINING GRAPHEME JOINER -->
|
||||
<int>0x0600</int> <!-- ARABIC NUMBER SIGN -->
|
||||
<int>0x0601</int> <!-- ARABIC SIGN SANAH -->
|
||||
<int>0x0602</int> <!-- ARABIC FOOTNOTE MARKER -->
|
||||
<int>0x0603</int> <!-- ARABIC SIGN SAFHA -->
|
||||
<int>0x06DD</int> <!-- ARABIC END OF AYAH -->
|
||||
<int>0x070F</int> <!-- SYRIAC ABBREVIATION MARK -->
|
||||
<int>0x115F</int> <!-- HANGUL CHOSEONG FILLER -->
|
||||
<int>0x1160</int> <!-- HANGUL JUNGSEONG FILLER -->
|
||||
<int>0x1680</int> <!-- OGHAM SPACE MARK -->
|
||||
<int>0x17B4</int> <!-- KHMER VOWEL INHERENT AQ -->
|
||||
<int>0x17B5</int> <!-- KHMER VOWEL INHERENT AA -->
|
||||
<int>0x180E</int> <!-- MONGOLIAN VOWEL SEPARATOR -->
|
||||
<int>0x2000</int> <!-- EN QUAD -->
|
||||
<int>0x2001</int> <!-- EM QUAD -->
|
||||
<int>0x2002</int> <!-- EN SPACE -->
|
||||
<int>0x2003</int> <!-- EM SPACE -->
|
||||
<int>0x2004</int> <!-- THREE-PER-EM SPACE -->
|
||||
<int>0x2005</int> <!-- FOUR-PER-EM SPACE -->
|
||||
<int>0x2006</int> <!-- SIX-PER-EM SPACE -->
|
||||
<int>0x2007</int> <!-- FIGURE SPACE -->
|
||||
<int>0x2008</int> <!-- PUNCTUATION SPACE -->
|
||||
<int>0x2009</int> <!-- THIN SPACE -->
|
||||
<int>0x200A</int> <!-- HAIR SPACE -->
|
||||
<int>0x200B</int> <!-- ZERO WIDTH SPACE -->
|
||||
<int>0x200C</int> <!-- ZERO WIDTH NON-JOINER -->
|
||||
<int>0x200D</int> <!-- ZERO WIDTH JOINER -->
|
||||
<int>0x200E</int> <!-- LEFT-TO-RIGHT MARK -->
|
||||
<int>0x200F</int> <!-- RIGHT-TO-LEFT MARK -->
|
||||
<int>0x2028</int> <!-- LINE SEPARATOR -->
|
||||
<int>0x2029</int> <!-- PARAGRAPH SEPARATOR -->
|
||||
<int>0x202A</int> <!-- LEFT-TO-RIGHT EMBEDDING -->
|
||||
<int>0x202B</int> <!-- RIGHT-TO-LEFT EMBEDDING -->
|
||||
<int>0x202C</int> <!-- POP DIRECTIONAL FORMATTING -->
|
||||
<int>0x202D</int> <!-- LEFT-TO-RIGHT OVERRIDE -->
|
||||
<int>0x202E</int> <!-- RIGHT-TO-LEFT OVERRIDE -->
|
||||
<int>0x202F</int> <!-- NARROW NO-BREAK SPACE -->
|
||||
<int>0x205F</int> <!-- MEDIUM MATHEMATICAL SPACE -->
|
||||
<int>0x2060</int> <!-- WORD JOINER -->
|
||||
<int>0x2061</int> <!-- FUNCTION APPLICATION -->
|
||||
<int>0x2062</int> <!-- INVISIBLE TIMES -->
|
||||
<int>0x2063</int> <!-- INVISIBLE SEPARATOR -->
|
||||
<int>0x206A</int> <!-- INHIBIT SYMMETRIC SWAPPING -->
|
||||
<int>0x206B</int> <!-- ACTIVATE SYMMETRIC SWAPPING -->
|
||||
<int>0x206C</int> <!-- INHIBIT ARABIC FORM SHAPING -->
|
||||
<int>0x206D</int> <!-- ACTIVATE ARABIC FORM SHAPING -->
|
||||
<int>0x206E</int> <!-- NATIONAL DIGIT SHAPES -->
|
||||
<int>0x206F</int> <!-- NOMINAL DIGIT SHAPES -->
|
||||
<int>0x2800</int> <!-- BRAILLE PATTERN BLANK -->
|
||||
<int>0x3000</int> <!-- IDEOGRAPHIC SPACE -->
|
||||
<int>0x3164</int> <!-- HANGUL FILLER -->
|
||||
<int>0xFEFF</int> <!-- ZERO WIDTH NO-BREAK SPACE -->
|
||||
<int>0xFFA0</int> <!-- HALFWIDTH HANGUL FILLER -->
|
||||
<int>0xFFF9</int> <!-- INTERLINEAR ANNOTATION ANCHOR -->
|
||||
<int>0xFFFA</int> <!-- INTERLINEAR ANNOTATION SEPARATOR -->
|
||||
<int>0xFFFB</int> <!-- INTERLINEAR ANNOTATION TERMINATOR -->
|
||||
</blank>
|
||||
<!--
|
||||
Rescan configuration every 30 seconds when FcFontSetList is called
|
||||
-->
|
||||
<rescan>
|
||||
<int>30</int>
|
||||
</rescan>
|
||||
</config>
|
||||
|
||||
</fontconfig>
|
||||
|
||||
BIN
commons/assets/fontconfig/fonts/truetype/Ubuntu-R.ttf
Normal file
63
commons/commons.iml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/target/generated-sources/r" />
|
||||
<option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/target/generated-sources/aidl" />
|
||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/AndroidManifest.xml" />
|
||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/res" />
|
||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/assets" />
|
||||
<option name="LIBS_FOLDER_RELATIVE_PATH" value="/src/main/native" />
|
||||
<option name="USE_CUSTOM_APK_RESOURCE_FOLDER" value="false" />
|
||||
<option name="CUSTOM_APK_RESOURCE_FOLDER" value="/target/generated-sources/combined-resources/res" />
|
||||
<option name="USE_CUSTOM_COMPILER_MANIFEST" value="false" />
|
||||
<option name="CUSTOM_COMPILER_MANIFEST" value="" />
|
||||
<option name="APK_PATH" value="/target/commons.apk" />
|
||||
<option name="LIBRARY_PROJECT" value="false" />
|
||||
<option name="RUN_PROCESS_RESOURCES_MAVEN_TASK" value="false" />
|
||||
<option name="GENERATE_UNSIGNED_APK" value="false" />
|
||||
<option name="CUSTOM_DEBUG_KEYSTORE_PATH" value="" />
|
||||
<option name="PACK_TEST_CODE" value="false" />
|
||||
<option name="RUN_PROGUARD" value="false" />
|
||||
<option name="PROGUARD_CFG_PATH" value="/proguard-project.txt" />
|
||||
<resOverlayFolders>
|
||||
<path>/res-overlay</path>
|
||||
</resOverlayFolders>
|
||||
<includeSystemProguardFile>true</includeSystemProguardFile>
|
||||
<includeAssetsFromLibraries>true</includeAssetsFromLibraries>
|
||||
<additionalNativeLibs />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_5" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/target/idea-classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/idea-test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/r" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target/generated-sources/combined-assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target/generated-sources/combined-resources" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target/generated-sources/extracted-dependencies" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target/maven-archiver" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target/unpack" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: org.mediawiki:api:1.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.httpcomponents:fluent-hc:4.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.1.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.6" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpmime:4.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: in.yuvi:http.fluent:1.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.actionbarsherlock:actionbarsherlock:apklib:4.2.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.android:support-v4:r7" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: android:android:4.1_r2" level="project" />
|
||||
<orderEntry type="library" name="Maven: de.akquinet.android.androlog:androlog:1.0.5" level="project" />
|
||||
<orderEntry type="jdk" jdkName="Android 4.0.3 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="module" module-name="~apklib-com.actionbarsherlock_actionbarsherlock_4.2.0" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
3
commons/default.properties
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# File used by Eclipse to determine the target system
|
||||
# Project target.
|
||||
target=android-15
|
||||
22
commons/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
commons/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
|
||||
308
commons/jni/gst-build/gstreamer_android.c
Normal 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;
|
||||
}
|
||||
BIN
commons/jni/gst-build/gstreamer_android.o
Normal file
7
commons/jni/src/com/gst_sdk/GStreamer.java
Normal 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;
|
||||
}
|
||||
134
commons/jni/transcode.c
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#include <gst/gst.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
182
commons/pom.xml
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.wikimedia</groupId>
|
||||
<artifactId>commons-parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.wikimedia</groupId>
|
||||
<artifactId>commons</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>apk</packaging>
|
||||
<name>commons - Application</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.mediawiki</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.actionbarsherlock</groupId>
|
||||
<artifactId>actionbarsherlock</artifactId>
|
||||
<version>4.2.0</version>
|
||||
<type>apklib</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>android</groupId>
|
||||
<artifactId>android</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.akquinet.android.androlog</groupId>
|
||||
<artifactId>androlog</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
||||
<artifactId>android-maven-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
</plugin>
|
||||
<!-- Commenting out Native stuff for now
|
||||
<plugin>
|
||||
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
||||
<artifactId>android-maven-plugin</artifactId>
|
||||
<goals>
|
||||
<goal>ndk-build</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<ndk>
|
||||
<path>${env.ANDROID_NDK_HOME}</path>
|
||||
</ndk>
|
||||
<target>all</target>
|
||||
|
||||
</configuration>
|
||||
<extensions>true</extensions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<configuration>
|
||||
<filesets>
|
||||
<fileset>
|
||||
<directory>libs</directory>
|
||||
</fileset>
|
||||
<fileset>
|
||||
<directory>obj</directory>
|
||||
</fileset>
|
||||
</filesets>
|
||||
</configuration>
|
||||
</plugin>
|
||||
-->
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.pyx4me</groupId>
|
||||
<artifactId>proguard-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>process-classes-with-proguard</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>proguard</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<proguardVersion>4.4</proguardVersion>
|
||||
<maxMemory>256m</maxMemory>
|
||||
<injar>android-classes</injar>
|
||||
<libs>
|
||||
<lib>${rt.jar.path}</lib>
|
||||
<lib>${jsse.jar.path}</lib>
|
||||
</libs>
|
||||
<obfuscate>true</obfuscate>
|
||||
<addMavenDescriptor>false</addMavenDescriptor>
|
||||
<proguardInclude>${project.basedir}/proguard.conf</proguardInclude>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.sf.proguard</groupId>
|
||||
<artifactId>proguard</artifactId>
|
||||
<version>4.4</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jarsigner-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-application-apk</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
||||
<artifactId>android-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>zipalign-application-apk</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>zipalign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<zipalign>
|
||||
<verbose>true</verbose>
|
||||
<inputApk>${project.build.directory}/${project.artifactId}-${project.version}.apk</inputApk>
|
||||
<outputApk>${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk</outputApk>
|
||||
</zipalign>
|
||||
<sign>
|
||||
<debug>false</debug>
|
||||
</sign>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<artifacts>
|
||||
<artifact>
|
||||
<file>${project.build.directory}/proguard_map.txt</file>
|
||||
<type>map</type>
|
||||
<classifier>release</classifier>
|
||||
</artifact>
|
||||
</artifacts>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-signed-aligned</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>attach-artifact</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
BIN
commons/res/drawable-hdpi/commons_logo_large.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
commons/res/drawable-hdpi/ic_action_search.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
commons/res/drawable-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
commons/res/drawable-hdpi/icon.png
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
commons/res/drawable-ldpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
commons/res/drawable-ldpi/icon.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
commons/res/drawable-mdpi/ic_action_search.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
commons/res/drawable-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
commons/res/drawable-mdpi/icon.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
commons/res/drawable-xhdpi/ic_action_search.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
commons/res/drawable-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4 KiB |
4
commons/res/drawable/action_bar_translucent.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient android:angle="90" android:startColor="#60ffffff" android:endColor="#40ffffff"/>
|
||||
</shape>
|
||||
10
commons/res/drawable/actionbar_top_shadow.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:startColor="#70000000"
|
||||
android:endColor="#00000000"
|
||||
android:angle="270"
|
||||
>
|
||||
</gradient>
|
||||
</shape>
|
||||
BIN
commons/res/drawable/ic_download.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
10
commons/res/drawable/share_overlay_background_gradient.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:startColor="#ffffffff"
|
||||
android:endColor="#f4f4f4ff"
|
||||
android:type="linear"
|
||||
/>
|
||||
</shape>
|
||||
57
commons/res/layout/activity_login.xml
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/commonsLogo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/commons_logo_large" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginSubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/login_subtitle"
|
||||
android:textAllCaps="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_margin="16dip"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<EditText
|
||||
android:id="@+id/loginUsername"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/username"
|
||||
android:inputType="textNoSuggestions"
|
||||
>
|
||||
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/loginPassword"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/password"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/loginButton"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/login" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
117
commons/res/layout/activity_share.xml
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" >
|
||||
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backgroundImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
<!-- Hiding the shadow because we're not doing the Actionbar now
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginTop="48dp"
|
||||
android:background="@drawable/actionbar_top_shadow"
|
||||
android:orientation="vertical" >
|
||||
</LinearLayout>
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_horizontal"
|
||||
android:layout_marginBottom="64dip"
|
||||
android:layout_marginLeft="16dip"
|
||||
android:layout_marginRight="16dip"
|
||||
android:layout_marginTop="64dip"
|
||||
android:background="@drawable/share_overlay_background_gradient"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="8dip"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingTop="8dip" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<ImageView
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:src="@drawable/ic_download" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:layout_marginLeft="8dip"
|
||||
android:text="@string/upload_overlay_title"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textColor="#818181"
|
||||
android:textSize="20sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Title"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="#818181" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/titleEdit"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollHorizontally="false"
|
||||
android:singleLine="true"
|
||||
android:textColor="#363636" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="#dadada"
|
||||
android:orientation="vertical" >
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Description"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="#818181" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/descEdit"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textMultiLine"
|
||||
android:textColor="#363636" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="#dadada"
|
||||
android:orientation="vertical" >
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/uploadButton"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Upload" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
</FrameLayout>
|
||||
43
commons/res/layout/layout_upload_progress.xml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:padding="10dp" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/uploadNotificationIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:src="@drawable/ic_launcher"
|
||||
android:layout_marginRight="10dp" />
|
||||
|
||||
<LinearLayout android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/uploadNotificationTitle"
|
||||
style="@style/NotificationTitle"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Uploading image to commons"
|
||||
android:singleLine="true"
|
||||
android:marqueeRepeatLimit="marquee_forever"
|
||||
android:scrollHorizontally="true"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:ellipsize="marquee"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/uploadNotificationProgress"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="false"
|
||||
android:indeterminateOnly="false"
|
||||
android:progressDrawable="@android:drawable/progress_horizontal" >
|
||||
|
||||
</ProgressBar>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
61
commons/res/layout/login.xml
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<ListView
|
||||
android:id="@+id/listView1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
</ListView>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/commonsLogo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="20dp"
|
||||
android:src="@drawable/commons_logo_large" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_below="@+id/commonsLogo"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="20dp"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp" >
|
||||
|
||||
<EditText
|
||||
android:id="@+id/loginUsername"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/username" >
|
||||
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/loginPassword"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/password"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/loginButton"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="20dp"
|
||||
android:text="@string/login" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</FrameLayout>
|
||||
6
commons/res/menu/activity_login.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/menu_settings"
|
||||
android:title="@string/menu_settings"
|
||||
android:orderInCategory="100"
|
||||
android:showAsAction="never" />
|
||||
</menu>
|
||||
6
commons/res/menu/activity_share.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/menu_settings"
|
||||
android:title="@string/menu_settings"
|
||||
android:orderInCategory="100"
|
||||
android:showAsAction="never" />
|
||||
</menu>
|
||||
5
commons/res/values-v11/styles.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<resources>
|
||||
|
||||
<style name="AppTheme" parent="android:Theme.Holo.Light" />
|
||||
|
||||
</resources>
|
||||
5
commons/res/values-v14/styles.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<resources>
|
||||
|
||||
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar" />
|
||||
|
||||
</resources>
|
||||
7
commons/res/values-v9/styles.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<resources>
|
||||
|
||||
<style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" />
|
||||
|
||||
<style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />
|
||||
|
||||
</resources>
|
||||
37
commons/res/values/strings.xml
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<resources>
|
||||
|
||||
<string name="app_name">Wikimedia Commons</string>
|
||||
<string name="hello_world">Hello world!</string>
|
||||
<string name="menu_settings">Settings</string>
|
||||
<string name="title_activity_login">Sign in to Wikimedia Commons</string>
|
||||
<string name="login_subtitle">Login to upload media to commons</string>
|
||||
<string name="username">Username</string>
|
||||
<string name="password">Password</string>
|
||||
<string name="login">Log In</string>
|
||||
<string name="logging_in_title">Logging In</string>
|
||||
<string name="logging_in_message">Witty text here</string>
|
||||
<string name="title_activity_share">Wikimedia Commons</string>
|
||||
<string name="upload_overlay_title">Upload to Commons</string>
|
||||
<string name="login_success">Login success!</string>
|
||||
<string name="login_failed">Login failed!</string>
|
||||
<string name="uploading_title">Uploading photo</string>
|
||||
<string name="uploading_message">More witty text here!</string>
|
||||
<string name="uploading_success">Upload succeeded!</string>
|
||||
<string name="uploading_failed">Upload failed!</string>
|
||||
<string name="authentication_failed">Authentication failed!</string>
|
||||
<string name="uploading_started">Upload started!</string>
|
||||
|
||||
<string name="upload_completed_notification_title">%1$s uploaded!</string>
|
||||
<string name="upload_completed_notification_text">Tap to view your upload</string>
|
||||
<string name="upload_progress_notification_title_start">Starting %1$s upload</string>
|
||||
<string name="upload_progress_notification_title_in_progress">%1$s uploading</string>
|
||||
<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>
|
||||
18
commons/res/values/styles.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<resources>
|
||||
|
||||
<style name="AppTheme" parent="android:Theme.Light" />
|
||||
|
||||
<style name="NoTitle" parent="AppTheme">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="NotificationText">
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="NotificationTitle">
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
12
commons/res/values/theme.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Commons" parent="@style/Theme.Sherlock.Light">
|
||||
<item name="android:actionBarStyle">@style/ActionBar</item>
|
||||
</style>
|
||||
|
||||
<style name="ActionBar" parent="@style/Theme.Sherlock.Light">
|
||||
<item name="android:background">@drawable/action_bar_translucent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
6
commons/res/xml/authenticator.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="org.wikimedia.commons"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:smallIcon="@drawable/ic_launcher" />
|
||||
60
commons/src/com/gstreamer/GStreamer.java
Normal 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
107
commons/src/main/java/org/wikimedia/commons/ImageLoaderTask.java
Normal 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);
|
||||
}
|
||||
}
|
||||
127
commons/src/main/java/org/wikimedia/commons/ShareActivity.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
217
commons/src/main/java/org/wikimedia/commons/UploadService.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
64
commons/src/main/java/org/wikimedia/commons/media/Media.java
Normal 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;
|
||||
}
|
||||
}
|
||||
22
commons/src/main/native/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
commons/src/main/native/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
|
||||
308
commons/src/main/native/gst-build/gstreamer_android.c
Normal 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;
|
||||
}
|
||||
BIN
commons/src/main/native/gst-build/gstreamer_android.o
Normal file
7
commons/src/main/native/src/com/gst_sdk/GStreamer.java
Normal 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;
|
||||
}
|
||||
148
commons/src/main/native/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
|
||||