diff options
author | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com> | 2013-02-13 09:08:37 +0100 |
---|---|---|
committer | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com> | 2013-02-14 09:29:47 +0100 |
commit | 27438946427ea006a2dfb6369fdb742abcf9ea05 (patch) | |
tree | fa3ad8f3b0618cc14b437413452906de273d017f | |
parent | 2ef46bbfada155ae65e999f633f7edfda751438a (diff) | |
parent | f71e67d47f681b55a8b68d051e607e8478958638 (diff) |
Change-Id: I4b5bf0cbceebc4823c444617d19cd6dd9d6b2e02
48 files changed, 4334 insertions, 0 deletions
diff --git a/.commit-template b/.commit-template new file mode 100644 index 0000000..d40f63b --- /dev/null +++ b/.commit-template @@ -0,0 +1,34 @@ +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# +# You MUST wrap all lines at 72 characters. +# Please see http://techbase.kde.org/Policies/Commit_Policy +# +# ==[ Subject: One line only short meaningful description for logs ]===| + +# ==[ Blank: Follow the Subject with a blank line, do NOT remove ]=====| + +# ==[ Details: Describe what changed and explain why it changed]=======| + + +# ==[ Fields: Uncomment and edit where applicable ]====================| +# +# --[ Close bug in bugs.kde.org as fixed ]-----------------------------| +#BUG: <bug number> +#FIXED-IN: <optional release version> +# +# --[ Add to release changelog optionally close wish in bugs.kde.org ]-| +#FEATURE: <optional bug number> +#FIXED-IN: <optional release version> +# +# --[ Copy commit message to a bug or wish in bugs.kde.org ]-----------| +#CCBUG: <bug number> +# +# --[ Copy commit message to an email address ]------------------------| +#CCMAIL: <email> +# +# --[ Close a review on git.reviewboard.kde.org as submitted ]---------| +#REVIEW: <review number> +# +# --[ Notify Commit Digest team of something interesting ]-------------| +#DIGEST: diff --git a/Ministro/AndroidManifest.xml b/Ministro/AndroidManifest.xml new file mode 100644 index 0000000..9b7d55c --- /dev/null +++ b/Ministro/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.kde.necessitas.ministro" android:versionName="8.9" android:versionCode="8"> + <application android:icon="@drawable/icon" android:label="@string/app_name"> + <activity android:name=".MinistroActivity" + android:label="@string/app_name" + android:configChanges="orientation|locale|fontScale|keyboard|keyboardHidden" > + <intent-filter> + <action android:name="android.intent.action.DEFAULT" /> + <category android:name="android.intent.category.VIEW" /> + </intent-filter> + </activity> + <activity android:name="org.kde.necessitas.ministro.MinistroConfigActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="org.kde.necessitas.ministro.MinistroConfigActivity" /> + <category android:name="android.intent.category.VIEW" /> + </intent-filter> + </activity> + <service android:name="org.kde.necessitas.ministro.MinistroService"> + <intent-filter> + <action android:name="org.kde.necessitas.ministro.IMinistro" /> + </intent-filter> + </service> + </application> + <uses-sdk android:minSdkVersion="4" /> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> +</manifest> diff --git a/Ministro/jni/Android.mk b/Ministro/jni/Android.mk new file mode 100644 index 0000000..93e8289 --- /dev/null +++ b/Ministro/jni/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := ministro +LOCAL_CFLAGS := -Werror +LOCAL_SRC_FILES := chmode.c extract.cpp +LOCAL_LDLIBS := -lm -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/Ministro/jni/Application.mk b/Ministro/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/Ministro/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/Ministro/jni/chmode.c b/Ministro/jni/chmode.c new file mode 100644 index 0000000..ed73182 --- /dev/null +++ b/Ministro/jni/chmode.c @@ -0,0 +1,27 @@ +/* + Copyright (c) 2011, BogDan Vatra <bog_dan_ro@yahoo.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <jni.h> +#include <sys/stat.h> + +jint Java_org_kde_necessitas_ministro_MinistroActivity_nativeChmode(JNIEnv * env, jobject obj, jstring filePath, jint mode) +{ + const char *file = (*env)->GetStringUTFChars(env, filePath, 0); + int res = chmod(file, mode); + (*env)->ReleaseStringUTFChars(env, filePath, file); + return res; +} diff --git a/Ministro/jni/extract.cpp b/Ministro/jni/extract.cpp new file mode 100644 index 0000000..a125bfc --- /dev/null +++ b/Ministro/jni/extract.cpp @@ -0,0 +1,178 @@ +/* + Copyright (c) 2011, BogDan Vatra <bog_dan_ro@yahoo.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <jni.h> +#include <android/log.h> +#include <extract.h> +#include <alloca.h> + +#define LOG_TAG "extractSyleInfo" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) +#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA_ARGS__) + +const char * const NinePatchDrawableClassName = "android/graphics/drawable/NinePatchDrawable"; +const char * const NinePatchFieldIDName = "mNinePatch"; +static jfieldID m_ninePatchFieldID=0; + +const char * const NinePatchClassName = "android/graphics/NinePatch"; +const char * const ChunkFieldIDName = "mChunk"; +static jfieldID m_chunkFieldID=0; + +const char * const ClipStateClassName = "android/graphics/drawable/ClipDrawable$ClipState"; +const char * const ClipStateDrawableFieldIDName = "mDrawable"; +static jfieldID m_clipStateDrawableFieldID=0; + +bool setup(JNIEnv * env, jobject /*obj*/) +{ + jclass ninePatchClass = env->FindClass(NinePatchClassName); + if (!ninePatchClass) + { + LOGF("Unable to find class '%s'", NinePatchClassName); + return JNI_FALSE; + } + + m_chunkFieldID = env->GetFieldID(ninePatchClass, ChunkFieldIDName, "[B"); + if(!m_chunkFieldID) + { + LOGF("Unable to find field '%s'", ChunkFieldIDName); + return JNI_FALSE; + } + + jclass ninePatchDrawableClass = env->FindClass(NinePatchDrawableClassName); + if (!ninePatchDrawableClass) + { + LOGF("Unable to find class '%s'", NinePatchDrawableClassName); + return JNI_FALSE; + } + + m_ninePatchFieldID = env->GetFieldID(ninePatchDrawableClass, NinePatchFieldIDName, "Landroid/graphics/NinePatch;"); + if(!m_ninePatchFieldID) + { + LOGF("Unable to find field '%s'", NinePatchFieldIDName); + return JNI_FALSE; + } + + jclass clipStateDrawableClass = env->FindClass(ClipStateClassName); + if (!clipStateDrawableClass) + { + LOGF("Unable to find class '%s'", ClipStateClassName); + return JNI_FALSE; + } + + m_clipStateDrawableFieldID = env->GetFieldID(clipStateDrawableClass, ClipStateDrawableFieldIDName, "Landroid/graphics/drawable/Drawable;"); + if(!m_ninePatchFieldID) + { + LOGF("Unable to find field '%s'", NinePatchFieldIDName); + return JNI_FALSE; + } + + return JNI_TRUE; +} + +static void printChunkInformation(Res_png_9patch* chunk) +{ + LOGI("printChunkInformation x:%d , y:%d",chunk->numXDivs, chunk->numYDivs); + for (int x = 0; x < chunk->numXDivs; x ++) + LOGI("X CHUNK %d %d", x, chunk->xDivs[x]); + for (int y = 0; y < chunk->numYDivs; y ++) + LOGI("Y CHUNK %d %d", y, chunk->yDivs[y]); + LOGI("----"); +} + +extern "C" JNIEXPORT jintArray JNICALL Java_org_kde_necessitas_ministro_ExtractStyle_extractChunkInfo(JNIEnv * env, jobject obj, jbyteArray chunkObj) +{ + size_t chunkSize = env->GetArrayLength(chunkObj); + void* storage = alloca(chunkSize); + env->GetByteArrayRegion(chunkObj, 0, chunkSize, + reinterpret_cast<jbyte*>(storage)); + + if (!env->ExceptionCheck()) + { + // need to deserialize the chunk + Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage); + Res_png_9patch::deserialize(chunk); +// printChunkInformation(chunk); + jintArray result; + size_t size = 3+chunk->numXDivs+chunk->numYDivs+chunk->numColors; + result = env->NewIntArray(size); + if (!result) + return 0; + + jint *data = (jint*)malloc(sizeof(jint)*size); + size_t pos = 0; + data[pos++]=chunk->numXDivs; + data[pos++]=chunk->numYDivs; + data[pos++]=chunk->numColors; + for (int x = 0; x <chunk->numXDivs; x ++) + data[pos++]=chunk->xDivs[x]; + for (int y = 0; y <chunk->numYDivs; y ++) + data[pos++]=chunk->yDivs[y]; + for (int c = 0; c <chunk->numColors; c ++) + data[pos++]=chunk->colors[c]; + env->SetIntArrayRegion(result, 0, size, data); + free(data); + return result; + } + return 0; +} + +extern "C" JNIEXPORT jintArray JNICALL Java_org_kde_necessitas_ministro_ExtractStyle_extract9PatchInfo(JNIEnv * env, jobject obj, jobject ninePatchObject) +{ + if (!m_ninePatchFieldID || !m_chunkFieldID) + if (!setup(env, obj)) + return 0; + return Java_org_kde_necessitas_ministro_ExtractStyle_extractChunkInfo(env, obj + ,reinterpret_cast<jbyteArray>(env->GetObjectField( + env->GetObjectField(ninePatchObject, m_ninePatchFieldID) + , m_chunkFieldID)) ); +} + +extern "C" JNIEXPORT jobject JNICALL Java_org_kde_necessitas_ministro_ExtractStyle_getClipStateDrawableObject(JNIEnv * env, jobject obj, jobject clipStateObject) +{ + if (!m_ninePatchFieldID || !m_chunkFieldID || !m_clipStateDrawableFieldID) + if (!setup(env, obj)) + return 0; + return env->GetObjectField(clipStateObject, m_clipStateDrawableFieldID); +} + +// The following part was shamelessly stolen from ResourceTypes.cpp Android's sources +static void deserializeInternal(const void* inData, Res_png_9patch* outData) { + char* patch = (char*) inData; + if (inData != outData) { + memmove(&outData->wasDeserialized, patch, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + memmove(&outData->paddingLeft, patch + 12, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + } + outData->wasDeserialized = true; + char* data = (char*)outData; + data += sizeof(Res_png_9patch); + outData->xDivs = (int32_t*) data; + data += outData->numXDivs * sizeof(int32_t); + outData->yDivs = (int32_t*) data; + data += outData->numYDivs * sizeof(int32_t); + outData->colors = (uint32_t*) data; +} + +Res_png_9patch* Res_png_9patch::deserialize(const void* inData) +{ + if (sizeof(void*) != sizeof(int32_t)) { + LOGE("Cannot deserialize on non 32-bit system\n"); + return NULL; + } + deserializeInternal(inData, (Res_png_9patch*) inData); + return (Res_png_9patch*) inData; +} diff --git a/Ministro/jni/extract.h b/Ministro/jni/extract.h new file mode 100644 index 0000000..498ba7a --- /dev/null +++ b/Ministro/jni/extract.h @@ -0,0 +1,101 @@ +#ifndef EXTRACT_H +#define EXTRACT_H + +#include <cstdint> +#include <cstring> +// shamelessly stolen from ResourceTypes.h Android's sources +/** + * This chunk specifies how to split an image into segments for + * scaling. + * + * There are J horizontal and K vertical segments. These segments divide + * the image into J*K regions as follows (where J=4 and K=3): + * + * F0 S0 F1 S1 + * +-----+----+------+-------+ + * S2| 0 | 1 | 2 | 3 | + * +-----+----+------+-------+ + * | | | | | + * | | | | | + * F2| 4 | 5 | 6 | 7 | + * | | | | | + * | | | | | + * +-----+----+------+-------+ + * S3| 8 | 9 | 10 | 11 | + * +-----+----+------+-------+ + * + * Each horizontal and vertical segment is considered to by either + * stretchable (marked by the Sx labels) or fixed (marked by the Fy + * labels), in the horizontal or vertical axis, respectively. In the + * above example, the first is horizontal segment (F0) is fixed, the + * next is stretchable and then they continue to alternate. Note that + * the segment list for each axis can begin or end with a stretchable + * or fixed segment. + * + * The relative sizes of the stretchy segments indicates the relative + * amount of stretchiness of the regions bordered by the segments. For + * example, regions 3, 7 and 11 above will take up more horizontal space + * than regions 1, 5 and 9 since the horizontal segment associated with + * the first set of regions is larger than the other set of regions. The + * ratios of the amount of horizontal (or vertical) space taken by any + * two stretchable slices is exactly the ratio of their corresponding + * segment lengths. + * + * xDivs and yDivs point to arrays of horizontal and vertical pixel + * indices. The first pair of Divs (in either array) indicate the + * starting and ending points of the first stretchable segment in that + * axis. The next pair specifies the next stretchable segment, etc. So + * in the above example xDiv[0] and xDiv[1] specify the horizontal + * coordinates for the regions labeled 1, 5 and 9. xDiv[2] and + * xDiv[3] specify the coordinates for regions 3, 7 and 11. Note that + * the leftmost slices always start at x=0 and the rightmost slices + * always end at the end of the image. So, for example, the regions 0, + * 4 and 8 (which are fixed along the X axis) start at x value 0 and + * go to xDiv[0] and slices 2, 6 and 10 start at xDiv[1] and end at + * xDiv[2]. + * + * The array pointed to by the colors field lists contains hints for + * each of the regions. They are ordered according left-to-right and + * top-to-bottom as indicated above. For each segment that is a solid + * color the array entry will contain that color value; otherwise it + * will contain NO_COLOR. Segments that are completely transparent + * will always have the value TRANSPARENT_COLOR. + * + * The PNG chunk type is "npTc". + */ +struct Res_png_9patch +{ + Res_png_9patch() : wasDeserialized(false), xDivs(NULL), + yDivs(NULL), colors(NULL) { } + + int8_t wasDeserialized; + int8_t numXDivs; + int8_t numYDivs; + int8_t numColors; + + // These tell where the next section of a patch starts. + // For example, the first patch includes the pixels from + // 0 to xDivs[0]-1 and the second patch includes the pixels + // from xDivs[0] to xDivs[1]-1. + // Note: allocation/free of these pointers is left to the caller. + int32_t* xDivs; + int32_t* yDivs; + + int32_t paddingLeft, paddingRight; + int32_t paddingTop, paddingBottom; + + enum { + // The 9 patch segment is not a solid color. + NO_COLOR = 0x00000001, + + // The 9 patch segment is completely transparent. + TRANSPARENT_COLOR = 0x00000000 + }; + // Note: allocation/free of this pointer is left to the caller. + uint32_t* colors; + + // Deserialize/Unmarshall the patch data + static Res_png_9patch* deserialize(const void* data); +}; + +#endif diff --git a/Ministro/res/drawable-hdpi/icon.png b/Ministro/res/drawable-hdpi/icon.png Binary files differnew file mode 100644 index 0000000..e9050a4 --- /dev/null +++ b/Ministro/res/drawable-hdpi/icon.png diff --git a/Ministro/res/drawable-ldpi/icon.png b/Ministro/res/drawable-ldpi/icon.png Binary files differnew file mode 100644 index 0000000..585728a --- /dev/null +++ b/Ministro/res/drawable-ldpi/icon.png diff --git a/Ministro/res/drawable-mdpi/icon.png b/Ministro/res/drawable-mdpi/icon.png Binary files differnew file mode 100644 index 0000000..3896146 --- /dev/null +++ b/Ministro/res/drawable-mdpi/icon.png diff --git a/Ministro/res/drawable/logo.png b/Ministro/res/drawable/logo.png Binary files differnew file mode 100644 index 0000000..8366c4f --- /dev/null +++ b/Ministro/res/drawable/logo.png diff --git a/Ministro/res/layout/main.xml b/Ministro/res/layout/main.xml new file mode 100644 index 0000000..09e59c7 --- /dev/null +++ b/Ministro/res/layout/main.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > + +<ImageView android:layout_width="fill_parent" android:src="@drawable/logo" android:visibility="visible" android:layout_height="fill_parent" android:id="@+id/image" android:scaleType="fitXY"></ImageView> + +</LinearLayout> diff --git a/Ministro/res/layout/repoconfig.xml b/Ministro/res/layout/repoconfig.xml new file mode 100644 index 0000000..f730e0e --- /dev/null +++ b/Ministro/res/layout/repoconfig.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dip" + android:text="@string/repositories_prompt" + /> + <Spinner + android:id="@+id/repositories" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:drawSelectorOnTop="true" + android:prompt="@string/repositories_prompt" + /> + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10mm" + android:text="@string/check_frequency" + /> + <Spinner + android:id="@+id/check_frequency" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:drawSelectorOnTop="true" + android:prompt="@string/check_frequency" + /> +</LinearLayout> diff --git a/Ministro/res/values-de/mstrings.xml b/Ministro/res/values-de/mstrings.xml new file mode 100644 index 0000000..b61f268 --- /dev/null +++ b/Ministro/res/values-de/mstrings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Bitte wählen Sie eine Quelle</string> + <string name="incompatible_ministo_api">Ministro ist nicht kompatibel mit ihrer Applikation. Bitte aktualisieren Sie Minstro-Service.</string> + <string name="ministro_network_access_msg">Ministor benötigt Internetzugang. Bitte schalten Sie Wi-Fi oder mobiles Internet an.</string> + <string name="settings_msg">Einstellungen</string> + <string name="wait_for_network_connection_msg">Warte auf Internet-Verbindung</string> + <string name="download_app_libs_msg"> %1$s benötigt eine zusätzliche Bibliothek.\nMöchten Sie diese nun herunterladen?</string> + <string name="start_downloading_msg">Beginne den Download...</string> + <string name="downloading_qt_libraries_msg">Lade Qt Bibliotheken herunter</string> + <string name="checking_libraries_msg">Überprüfe Bibliotheken. Bitte warten...</string> + <string name="extracting_SSL_msg">Extrahiere SSL Wurzelzertifikate. Bitte warten...</string> + <string name="new_qt_libs_msg">Neue Qt Bibliotheken gefunden</string> + <string name="new_qt_libs_tap_msg">Neue Qt Bibliotheken wurden gefunden. Zum Aktualisieren antippen.</string> + <string name="ministro_repository_msg">Ministro wird %1$s Quelle benutzen</string> + <string name="ministro_repository_changed_msg">Ministro Quelle geändert</string> + <string name="ministro_update_msg">Ministro Update</string> + <string name="ministro_repository_changed_tap_msg">Ministro Quelle wurde geändert. Zum Aktualisieren antippen.</string> + <string name="invalid_parameters">Ungültige Parameter</string> + <string name="dependencies_error">Ministro kann die Abhängigkeiten ihrer Applikation nicht zur verfügen stellen.</string> +</resources> diff --git a/Ministro/res/values-el/strings.xml b/Ministro/res/values-el/strings.xml new file mode 100644 index 0000000..f8c5c6c --- /dev/null +++ b/Ministro/res/values-el/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Επιλέξτε την τοποθεσία</string> + <string name="incompatible_ministo_api">Η εφαρμογή Ministro δεν είναι συμβατή με την εφαρμογή. Παρακαλώ αναβαθμίστε την εφαρμογή Ministro.</string> + <string name="ministro_network_access_msg">Η εφαρμογή Ministro απαιτεί την ύπαρξη σύνδεσης διαδικτύου. Ξεκινήστε την σύνδεση για το κατέβασμα των δεδομένων.</string> + <string name="settings_msg">Ρυθμίσεις</string> + <string name="wait_for_network_connection_msg">Αναμονή για την σύνδεση διαδικτύου</string> + <string name="download_app_libs_msg"> %1$s απαιτεί πρόσθετες βιβλιοθήκες για να ξεκινήσει.\nΝα πραγματοποιηθεί το κατέβασμα των δεδομένων?</string> + <string name="start_downloading_msg">Εκκίνηση κατεβάσματος δεδομένων...</string> + <string name="downloading_qt_libraries_msg">Κατεβαίνουν οι βιβλιοθήκες της Qt</string> + <string name="checking_libraries_msg">Έλεγχος βιβλιοθηκών. Παρακαλώ περιμένετε...</string> + <string name="extracting_SSL_msg">Αποσυμπίσεση των πιστοποιητικών SSL. Παρακαλώ περιμένετε...</string> + <string name="new_qt_libs_msg">Βρέθηκαν νέες βιβλιοθήκες της Qt</string> + <string name="new_qt_libs_tap_msg">Βρέθηκαν νέες βιβλιοθήκες της Qt, πατήστε για αναβάθμιση.</string> + <string name="ministro_repository_msg">Η εφαρμογή Ministro θα χρησιμοποιήσει την τοποθεσία %1$s</string> + <string name="ministro_repository_changed_msg">Η τοποθεσία της εφαρμογής Ministro έχει αλλάξει</string> + <string name="ministro_update_msg">Αναβάθμιση της εφαρμογής Ministro</string> + <string name="ministro_repository_changed_tap_msg">Η τοποθεσία της εφαρμογής Ministro έχει αλλάξει,πατήστε για αναβάθμιση.</string> + <string name="invalid_parameters">Μή εγκυρες παράμετροι</string> + <string name="dependencies_error">Η εφαρμογή Ministro δεν ικανοποιεί τις απαιτήσεις της εφαρμογής σας</string> +</resources> diff --git a/Ministro/res/values-es/strings.xml b/Ministro/res/values-es/strings.xml new file mode 100644 index 0000000..52159cf --- /dev/null +++ b/Ministro/res/values-es/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Elegir un repositorio</string> + <string name="incompatible_ministo_api">Ministro no es compatible con la aplicación. Actualizar el servicio Ministro.</string> + <string name="ministro_network_access_msg">Ministro requiere el acceso de red. Habilitar la red móvil o Wi-Fi para descargar los datos.</string> + <string name="settings_msg">Impostaciones</string> + <string name="wait_for_network_connection_msg">Conectando a la red...</string> + <string name="download_app_libs_msg"> %1$s requiere librerias extra para la ejecución.\nDescargarlas ahora?</string> + <string name="start_downloading_msg">Iniciando descarga...</string> + <string name="downloading_qt_libraries_msg">Descarga de las librerias Qt</string> + <string name="checking_libraries_msg">Controlando las librerias. Esperar</string> + <string name="extracting_SSL_msg">Extracción de los certificados de root SSL. Esperar</string> + <string name="new_qt_libs_msg">Encontradas nuevas librerias Qt</string> + <string name="new_qt_libs_tap_msg">Encontradas nuevas librerias Qt. Presionar para actualizar.</string> + <string name="ministro_repository_msg">Ministro utilizará el repositorio %1$s</string> + <string name="ministro_repository_changed_msg">Modificar el repositorio de Ministro</string> + <string name="ministro_update_msg">Actualizando Ministro</string> + <string name="ministro_repository_changed_tap_msg">Modificado repositorio de Ministro. Presionar para actualizar.</string> + <string name="invalid_parameters">Parámetros no validos</string> + <string name="dependencies_error">Ministro no puede satisfacer las dependencias de la aplicación</string> +</resources> diff --git a/Ministro/res/values-et/strings.xml b/Ministro/res/values-et/strings.xml new file mode 100644 index 0000000..525f858 --- /dev/null +++ b/Ministro/res/values-et/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Vali varamu</string> + <string name="incompatible_ministo_api">Ministro ei ühildu selle programmiga. Palun uuenda Ministro teenust.</string> + <string name="ministro_network_access_msg">Ministro vajab võrgu ligipääsu. Aktiveeri mobiilne internet või WiFi.</string> + <string name="settings_msg">Seaded</string> + <string name="wait_for_network_connection_msg">Ootan võrguühendust</string> + <string name="download_app_libs_msg"> %1$s vajab lisateeke.\nSoovite need kohe alla laadida?</string> + <string name="start_downloading_msg">Alustan allalaadimist...</string> + <string name="downloading_qt_libraries_msg">Laadin alla Qt teeke</string> + <string name="checking_libraries_msg">Kontrollin teeke. Palun oota...</string> + <string name="extracting_SSL_msg">Eraldan välja SSL juurserte. Palun oota...</string> + <string name="new_qt_libs_msg">Uued Qt teegid leitud</string> + <string name="new_qt_libs_tap_msg">Uued Qt teegid leitud, puuduta uuenduse alustamiseks.</string> + <string name="ministro_repository_msg">Uued Qt teegid leitud, puuduta uuenduse alustamiseks.</string> + <string name="ministro_repository_changed_msg">Ministro varamu on muutunud.</string> + <string name="ministro_update_msg">Ministro uuendus</string> + <string name="ministro_repository_changed_tap_msg">Ministro varamu on muutunud, puuduta uuenduse alustamiseks.</string> + <string name="invalid_parameters">Vigased parameetrid</string> + <string name="dependencies_error">Ministro ei suuda selle programmi sõltuvusi lahendada.</string> +</resources> diff --git a/Ministro/res/values-fa/strings.xml b/Ministro/res/values-fa/strings.xml new file mode 100644 index 0000000..3072a56 --- /dev/null +++ b/Ministro/res/values-fa/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">مخزن را انتخاب کن</string> + <string name="incompatible_ministo_api">سرویس Ministro با برنامهتان سازگار نیست. لطفا سرویس Ministro را بروز کنید.</string> + <string name="ministro_network_access_msg">سرویس Ministro نیاز به دسترسی به شبکه اینترنت دارد. شبکه اینترنت موبایل یا شبکه اینترنت بیسیم را برای دانلود دادهها فعال کنید.</string> + <string name="settings_msg">تنظیمات</string> + <string name="wait_for_network_connection_msg">انتظار برای اتصال به شبکه</string> + <string name="download_app_libs_msg"> %1$s برای اجرا شدن به کتابخانههای اضافی نیاز دارد.\nآیا اکنون آنها را دانلود میکنید؟</string> + <string name="start_downloading_msg">آغاز دانلود کردن...</string> + <string name="downloading_qt_libraries_msg">دانلود کردن کتابخانههای Qt</string> + <string name="checking_libraries_msg">بررسی کتابخانهها. لطفا منتظر بمانید...</string> + <string name="extracting_SSL_msg">استخراج کردن اعتبارنامه ریشه SSL. لطفا منتظر بمانید...</string> + <string name="new_qt_libs_msg">کتابخانههای جدید Qt یافت شدند</string> + <string name="new_qt_libs_tap_msg">کتابخانههای جدید Qt یافت شدهاند برای بروزرسانی tap کنید.</string> + <string name="ministro_repository_msg">سرویس Ministro مخزن %1$s را استفاده خواهد کرد</string> + <string name="ministro_repository_changed_msg">مخزن سرویس Ministro تغییر کرده است.</string> + <string name="ministro_update_msg">بروزرسانی سرویس Ministro</string> + <string name="ministro_repository_changed_tap_msg">مخزن سرویس Ministro تغییر کرده است برای بروزرسانی tap کنید.</string> + <string name="invalid_parameters">پارامترهای نامعتبر</string> + <string name="dependencies_error">سرویس Ministro نمیتواند وابستگیهای برنامهتان را برآورده کند</string> +</resources> diff --git a/Ministro/res/values-fr/strings.xml b/Ministro/res/values-fr/strings.xml new file mode 100644 index 0000000..1b4c264 --- /dev/null +++ b/Ministro/res/values-fr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Choisissez un dépôt</string> + <string name="incompatible_ministo_api">Ministro n\'est pas compatible avec votre application. Veuillez mettre à jour votre service Ministro.</string> + <string name="ministro_network_access_msg">Ministro requires network access. Enable mobile network or Wi-Fi to download data.</string> + <string name="settings_msg">Préférences</string> + <string name="wait_for_network_connection_msg">Attente de la connection réseau</string> + <string name="download_app_libs_msg"> %1$s a besoin de librairies supplémentaires.\nVoulez-vous les télécharger maintenant?</string> + <string name="start_downloading_msg">Début du téléchargement ...</string> + <string name="downloading_qt_libraries_msg">Téléchargement des librairies Qt</string> + <string name="checking_libraries_msg">Vérification des librairies. Veuillez attendre...</string> + <string name="extracting_SSL_msg">Extraction des certificats racines SSL. Veuillez attendre...</string> + <string name="new_qt_libs_msg">Nouvelle version des librairies Qt trouvée</string> + <string name="new_qt_libs_tap_msg">Nouvelle version des librairies Qt trouvée, tapez pour mettre à jour.</string> + <string name="ministro_repository_msg">Ministro usera le dépôt %1$s</string> + <string name="ministro_repository_changed_msg">Dépôt Ministro changé</string> + <string name="ministro_update_msg">Mise à jour Ministro</string> + <string name="ministro_repository_changed_tap_msg">Dépôt Ministro changé, tapez pour mettre à jour.</string> + <string name="invalid_parameters">Parametres invalides</string> + <string name="dependencies_error">Ministro ne peut pas satisfaire les dépendances de votre application</string> +</resources> diff --git a/Ministro/res/values-id/strings.xml b/Ministro/res/values-id/strings.xml new file mode 100644 index 0000000..c0930c1 --- /dev/null +++ b/Ministro/res/values-id/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Pilih indeks</string> + <string name="incompatible_ministo_api">Ministro tidak cocok dengan aplikasi Anda. Silakan meningkatkan layanan Ministro.</string> + <string name="ministro_network_access_msg">Ministro memerlukan akses jaringan. Aktifkan jaringan bergerak atau Wi-Fi untuk mengunduh data.</string> + <string name="settings_msg">tata latar</string> + <string name="wait_for_network_connection_msg">Menunggu koneksi jaringan </string> + <string name="download_app_libs_msg"> %1$s memerlukan ekstra daftar pustaka untuk dioperasikan.\nUnduh sekarang?</string> + <string name="start_downloading_msg">Mulai mengunduh ...</string> + <string name="downloading_qt_libraries_msg">Mengunduh daftar pustaka Qt </string> + <string name="checking_libraries_msg">mengecek daftar pustaka. Harap tunggu...</string> + <string name="extracting_SSL_msg">Mengekstrak certifikat akar SSL root. Harap tunggu...</string> + <string name="new_qt_libs_msg">Ditemukan daftar pustaka Qt aktual</string> + <string name="new_qt_libs_tap_msg">Telah ditemukan daftar pustaka Qt aktual tekan untuk memperbarui.</string> + <string name="ministro_repository_msg">Ministro akan menggunakan indeks %1$s</string> + <string name="ministro_repository_changed_msg">Indeks Ministro berubah</string> + <string name="ministro_update_msg">Ministro aktual</string> + <string name="ministro_repository_changed_tap_msg">Indeks Ministro berubah tekan untuk memperbarui.</string> + <string name="invalid_parameters">Parameter tidak berfungsi</string> + <string name="dependencies_error">Ministro tidak dapat memenuhi dependensi aplikasi Anda</string> +</resources> diff --git a/Ministro/res/values-it/strings.xml b/Ministro/res/values-it/strings.xml new file mode 100644 index 0000000..d9fdffe --- /dev/null +++ b/Ministro/res/values-it/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Scegliere un repository</string> + <string name="incompatible_ministo_api">Ministro non è compatibile con l\'applicazione. Aggiornare il servizio Ministro.</string> + <string name="ministro_network_access_msg">Ministro richiede l\'accesso di rete. Abilitare rete mobile o Wi-Fi per scaricare i dati.</string> + <string name="settings_msg">Impostazioni</string> + <string name="wait_for_network_connection_msg">In attesa della connessione di rete</string> + <string name="download_app_libs_msg"> %1$s richiede librerie extra per l\'esecuzione.\nScaricarle ora?</string> + <string name="start_downloading_msg">Inizio scaricamento in corso</string> + <string name="downloading_qt_libraries_msg">Scaricamento delle librerie Qt</string> + <string name="checking_libraries_msg">Controllo delle librerie in corso. Attendere</string> + <string name="extracting_SSL_msg">Estrazione dei certificati di root SSL. Attendere</string> + <string name="new_qt_libs_msg">Trovate nuove librerie Qt</string> + <string name="new_qt_libs_tap_msg">Trovate nuove librerie Qt. Premere per aggiornare.</string> + <string name="ministro_repository_msg">Ministro userà il repository %1$s</string> + <string name="ministro_repository_changed_msg">Modificato repository di Ministro</string> + <string name="ministro_update_msg">Aggiornamento di Ministro</string> + <string name="ministro_repository_changed_tap_msg">Modificato repository di Ministro. Premere per aggiornare.</string> + <string name="invalid_parameters">Parametri non validi</string> + <string name="dependencies_error">Ministro non può soddisfare le dipendenze dell\'applicazione</string> +</resources> diff --git a/Ministro/res/values-ja/strings.xml b/Ministro/res/values-ja/strings.xml new file mode 100644 index 0000000..ab21299 --- /dev/null +++ b/Ministro/res/values-ja/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">リポジトリの選択</string> + <string name="incompatible_ministo_api">Ministroはあなたのアプリケーションと互換性がありません。 Ministroサービスをアップグレードしてください。</string> + <string name="ministro_network_access_msg">Ministroはネットワークアクセスを必要とします。 モバイルネットワークかWi-Fiでデータのダウンロードを許可してください。</string> + <string name="settings_msg">セッティング</string> + <string name="wait_for_network_connection_msg">ネットワークの接続待ち</string> + <string name="download_app_libs_msg"> %1$sは拡張ライブラリを必要とします。\n今ダウンロードしますか?</string> + <string name="start_downloading_msg">ダウンロード開始 ...</string> + <string name="downloading_qt_libraries_msg">Qtライブラリーダウンロード中</string> + <string name="checking_libraries_msg">ライブラリーを確認中です。 お待ちください...</string> + <string name="extracting_SSL_msg">SSLルート証明書を抽出中。 お待ちください...</string> + <string name="new_qt_libs_msg">新しいQtライブラリーが見つかりました</string> + <string name="new_qt_libs_tap_msg">新しいQtライブラリーが見つかったので更新をタップしてください。</string> + <string name="ministro_repository_msg">Ministroは%1$sのリポジトリを使用します。</string> + <string name="ministro_repository_changed_msg">Ministroリポジトリが変更されました</string> + <string name="ministro_update_msg">Ministro更新</string> + <string name="ministro_repository_changed_tap_msg">Ministroのリポジトリが変更されたので更新をタップしてください。</string> + <string name="invalid_parameters">無効なパラメータ</string> + <string name="dependencies_error">Ministroはあなたのアプリケーションの依存関係を解決できません</string> +</resources> diff --git a/Ministro/res/values-ms/strings.xml b/Ministro/res/values-ms/strings.xml new file mode 100644 index 0000000..4cc61ae --- /dev/null +++ b/Ministro/res/values-ms/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Pilih satu repositori</string> + <string name="incompatible_ministo_api">Ministro tidak serasi dengan aplikasi anda. Sila naik taraf servis Ministro.</string> + <string name="ministro_network_access_msg">Ministro memerlukan akses rangkaian. Guna rangkaian mudah alih atau rangkaian tanpa talian untuk muat turun data.</string> + <string name="settings_msg">Tetapan</string> + <string name="wait_for_network_connection_msg">Menunggu sambungan rangkaian</string> + <string name="download_app_libs_msg"> %1$s memerlukan pustaka tambahan untuk jalan.\nAdakah anda ingin muat turun sekarang?</string> + <string name="start_downloading_msg">Mula muat turun ...</string> + <string name="downloading_qt_libraries_msg">Muat turun pustaka Qt</string> + <string name="checking_libraries_msg">Semak pustaka. Sila tunggu...</string> + <string name="extracting_SSL_msg">Mengekstrak sijil akar SSL. Sila tunggu...</string> + <string name="new_qt_libs_msg">Pustaka baru Qt dijumpa</string> + <string name="new_qt_libs_tap_msg">Pustaka baru Qt telah dijumpai ketuk untuk kemas kini</string> + <string name="ministro_repository_msg">Ministro akan menggunakan %1$s repositori.</string> + <string name="ministro_repository_changed_msg">Repositori Ministro telah berubah</string> + <string name="ministro_update_msg">Kemas kini Ministro</string> + <string name="ministro_repository_changed_tap_msg">Repositori Ministro telah berubah ketuk untuk kemas kini.</string> + <string name="invalid_parameters">Parameter-parameter tidak sah.</string> + <string name="dependencies_error">Ministro tidak dapat memuaskan kebergantungan aplikasi anda</string> +</resources> diff --git a/Ministro/res/values-nb/strings.xml b/Ministro/res/values-nb/strings.xml new file mode 100644 index 0000000..d9f3f26 --- /dev/null +++ b/Ministro/res/values-nb/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Velg et kodelager</string> + <string name="incompatible_ministo_api">Ministro er ikke kompatibel med din applikasjon. Vennligst oppgrader tjenesten Ministro.</string> + <string name="ministro_network_access_msg">Ministro krever nettverkstilgang. Tillat datatrafikk i mobilnett eller skru på trådløst nettverk for å laste ned data.</string> + <string name="settings_msg">Innstillinger</string> + <string name="wait_for_network_connection_msg">Venter på nettverkstilkobling</string> + <string name="download_app_libs_msg"> %1$s trenger flere biblioteker for å kjøre.\nVil du laste ned disse nå?</string> + <string name="start_downloading_msg">Start nedlasting ...</string> + <string name="downloading_qt_libraries_msg">Laster ned Qt biblioteker</string> + <string name="checking_libraries_msg">Sjekker biblioteker. Vennligst vent...</string> + <string name="extracting_SSL_msg">Trekker ut SSL rotsertifikater. Vennligst vent...</string> + <string name="new_qt_libs_msg">Fant nye Qt biblioteker</string> + <string name="new_qt_libs_tap_msg">Fant nye Qt biblioteker, trykk for å oppdatere</string> + <string name="ministro_repository_msg">Ministro vil bruke %1$s kodelager</string> + <string name="ministro_repository_changed_msg">Ministro kodelager endret</string> + <string name="ministro_update_msg">Ministro oppdatering</string> + <string name="ministro_repository_changed_tap_msg">Ministro kodelager endret, trykk for å oppdatere</string> + <string name="invalid_parameters">Ugyldige parametere</string> + <string name="dependencies_error">Din applikasjon har avhengigheter Ministro ikke kan tilfredsstille</string> +</resources> diff --git a/Ministro/res/values-nl/strings.xml b/Ministro/res/values-nl/strings.xml new file mode 100644 index 0000000..6b3a3bd --- /dev/null +++ b/Ministro/res/values-nl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Kies een repository</string> + <string name="incompatible_ministo_api">Ministro is niet compatibel met uw applicatie. Update uw Ministro service.</string> + <string name="ministro_network_access_msg">Ministro heeft netwerk-toegang nodig. Schakel mobiele data netwerk of Wi-Fi in.</string> + <string name="settings_msg">Instelling</string> + <string name="wait_for_network_connection_msg">Wachten op netwerkverbinding</string> + <string name="download_app_libs_msg"> %1$s heeft extra libraries nodig.\nWilt u deze nu downloaden?</string> + <string name="start_downloading_msg">Start downloaden ...</string> + <string name="downloading_qt_libraries_msg">Qt libraries aan het downloaden</string> + <string name="checking_libraries_msg">Libraries aan het controleren. Even wachten...</string> + <string name="extracting_SSL_msg">SSL certificaten extracten. Even wachten...</string> + <string name="new_qt_libs_msg">Nieuwe Qt libs gevonden</string> + <string name="new_qt_libs_tap_msg">Nieuwe Qt libs zijn gevonden, raak aan om te updaten.</string> + <string name="ministro_repository_msg">Ministro gebruikt %1$s repository</string> + <string name="ministro_repository_changed_msg">Ministro repository is veranderd</string> + <string name="ministro_update_msg">Ministro update</string> + <string name="ministro_repository_changed_tap_msg">Ministro repository is veranderd, raak aan om te updaten.</string> + <string name="invalid_parameters">Ongeldige parameters</string> + <string name="dependencies_error">Ministro kan niet alle libraries die uw applicatie nodig heeft vinden.</string> +</resources> diff --git a/Ministro/res/values-pl/strings.xml b/Ministro/res/values-pl/strings.xml new file mode 100644 index 0000000..ee5a86b --- /dev/null +++ b/Ministro/res/values-pl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Wybierz repozytorium</string> + <string name="incompatible_ministo_api">Usługa Ministro nie jest zgodna z tą aplikacją. Uaktualnij Ministro.</string> + <string name="ministro_network_access_msg">Usługa Ministro wymaga połączenia z Internetem. Aktywuj Wi-Fi lub mobilny Internet, aby pobrać dane.</string> + <string name="settings_msg">Ustawienia</string> + <string name="wait_for_network_connection_msg">Oczekiwanie na połączenie z Internetem</string> + <string name="download_app_libs_msg">%1$s wymaga dodatkowych bibliotek do uruchomienia.\nCzy chcesz je pobrać teraz?</string> + <string name="start_downloading_msg">Rozpoczęcie pobierania...</string> + <string name="downloading_qt_libraries_msg">Pobieranie bibliotek Qt</string> + <string name="checking_libraries_msg">Weryfikacja bibliotek. Proszę czekać...</string> + <string name="extracting_SSL_msg">Wyodrębnianie certyfikatów SSL. Proszę czekać...</string> + <string name="new_qt_libs_msg">Dostępne nowe biblioteki Qt</string> + <string name="new_qt_libs_tap_msg">Dostępna nowa wersja bibliotek Qt. Dotknij, aby zaktualizować.</string> + <string name="ministro_repository_msg">Ministro będzie używał repozytorium „%1$s”</string> + <string name="ministro_repository_changed_msg">Zmienione repozytorium Ministro</string> + <string name="ministro_update_msg">Aktualizacja Ministro</string> + <string name="ministro_repository_changed_tap_msg">Zmienione repozytorium Ministro. Dotknij, aby zaktualizować.</string> + <string name="invalid_parameters">Nieprawidłowe parametry</string> + <string name="dependencies_error">Ministro nie jest w stanie odnaleźć wszystkich zależności dla aplikacji</string> +</resources> diff --git a/Ministro/res/values-pt-rBR/strings.xml b/Ministro/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000..a216729 --- /dev/null +++ b/Ministro/res/values-pt-rBR/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Escolha um repositório</string> + <string name="incompatible_ministo_api">O Ministro não é compatível com sua aplicação. Por favor atualize o serviço Ministro.</string> + <string name="ministro_network_access_msg">O Ministro requer acesso à rede. Habilite a rede móvel ou o Wi-Fi para baixar os dados.</string> + <string name="settings_msg">Configurações</string> + <string name="wait_for_network_connection_msg">Aguardando conexão de rede</string> + <string name="download_app_libs_msg"> %1$s precisa de bibliotecas extras para executar.\nVocê gostaria de baixá-las agora?</string> + <string name="start_downloading_msg">Iniciando o download ...</string> + <string name="downloading_qt_libraries_msg">Baixando as bibliotecas do Qt</string> + <string name="checking_libraries_msg">Verificando bibliotecas. Por favor aguarde...</string> + <string name="extracting_SSL_msg">Extraindo os certificados raiz SSL. Por favor aguarde...</string> + <string name="new_qt_libs_msg">Novas bibliotecas Qt encontradas</string> + <string name="new_qt_libs_tap_msg">Novas bibliotecas Qt foram encontradas, toque para atualizar.</string> + <string name="ministro_repository_msg">O Ministro utilizará o repositório %1$s</string> + <string name="ministro_repository_changed_msg">Repositório do Ministro modificado</string> + <string name="ministro_update_msg">Atualização do Ministro</string> + <string name="ministro_repository_changed_tap_msg">O repositório do Ministro foi modificado, toque para atualizar.</string> + <string name="invalid_parameters">Parâmetros inválidos</string> + <string name="dependencies_error">O Ministro não pode satisfazer as dependências da sua aplicação</string> +</resources> diff --git a/Ministro/res/values-ro/strings.xml b/Ministro/res/values-ro/strings.xml new file mode 100644 index 0000000..92a5577 --- /dev/null +++ b/Ministro/res/values-ro/strings.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Alegeţi un depozit</string> + <string name="incompatible_ministo_api">Ministro nu este compatibil cu aplicaţia dumneavoastră. Vă rugăm să actualizaţi serviciul Ministro.</string> + <string name="ministro_network_access_msg">Ministro necesită acces la reţea. Activaţi reţeaua mobilă sau Wi-Fi pentru a descărca datele.</string> + <string name="settings_msg">Setări</string> + <string name="wait_for_network_connection_msg">Aştept conexiunea de reţea</string> + <string name="download_app_libs_msg">%1$s are nevoie de biblioteci suplimentare pentru a rula.\nDoriţi să le descărcaţi acum?</string> + <string name="start_downloading_msg">Încep descărcarea ...</string> + <string name="downloading_qt_libraries_msg">Descarc bibliotecile</string> + <string name="checking_libraries_msg">Verific bibliotecile. Vă rugăm aşteptaţi ...</string> + <string name="extracting_SSL_msg">Extrag certificate SSL. Vă rugăm aşteptaţi ...</string> + <string name="new_qt_libs_msg">Au fost găsite biblioteci noi</string> + <string name="new_qt_libs_tap_msg">Au fost găsite biblioteci noi, apăsaţi pentru a le actualiza.</string> + <string name="ministro_repository_msg">Ministro va folosi depozitul %1$s</string> + <string name="ministro_repository_changed_msg">Depozitul Ministro s-a schimbat</string> + <string name="ministro_update_msg">Actualizare Ministro</string> + <string name="ministro_repository_changed_tap_msg">Ministro depozitul sa schimbat, apăsaţi pentru actualizare.</string> + <string name="invalid_parameters">Parametrii sunt invalizi</string> + <string name="dependencies_error">Ministro nu poate găsi toate dependenţele aplicaţiei</string> + <string name="extracting_look_n_feel_msg">Extrag informaţiile de tematică. Vă rugăm aşteptaţi ...</string> +</resources> diff --git a/Ministro/res/values-rs/strings.xml b/Ministro/res/values-rs/strings.xml new file mode 100644 index 0000000..496ede2 --- /dev/null +++ b/Ministro/res/values-rs/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Izaberite spremište.</string> + <string name="incompatible_ministo_api">Ministro nije kompatibilan sa Vašom aplikacijom. Molimo ažurirajte Ministro servis.</string> + <string name="ministro_network_access_msg">Ministro zahteva pristup mreži. Omogućite mobilnu ili bežičnu mrežu radi preuzimanja podataka.</string> + <string name="settings_msg">Podešavanja</string> + <string name="wait_for_network_connection_msg">Čekam konekciju sa mrežom</string> + <string name="download_app_libs_msg"> %1$s zahteva dodatne biblioteke da bi se pokrenuo. Želite li sada da ih preuzmete?</string> + <string name="start_downloading_msg">Preuzimanje započeto ...</string> + <string name="downloading_qt_libraries_msg">Preuzimanje Qt biblioteka</string> + <string name="checking_libraries_msg">Proveravanje biblioteka. Molimo sačekajte...</string> + <string name="extracting_SSL_msg">Ekstraktovanje SSL sertifikata. Molimo sačekajte...</string> + <string name="new_qt_libs_msg">Pronađene nove Qt biblioteke</string> + <string name="new_qt_libs_tap_msg">Nove Qt biblioteke su pronađene, klik za ažuriranje.</string> + <string name="ministro_repository_msg">Ministro će koristiti %1$s spremište</string> + <string name="ministro_repository_changed_msg">Ministro spremište promenjeno</string> + <string name="ministro_update_msg">Ministro ažuriranje</string> + <string name="ministro_repository_changed_tap_msg">Ministro spremište promenjeno, klik za ažuriranje.</string> + <string name="invalid_parameters">Pogrešni parametri</string> + <string name="dependencies_error">Ministro ne može da pronađe zavisnosti Vaše aplikacije</string> +</resources> diff --git a/Ministro/res/values-ru/strings.xml b/Ministro/res/values-ru/strings.xml new file mode 100644 index 0000000..0b7bd4a --- /dev/null +++ b/Ministro/res/values-ru/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Выберите репозиторий</string> + <string name="incompatible_ministo_api">Ministro не совместим с вашим приложением. Обновите сервис Ministro.</string> + <string name="ministro_network_access_msg">Ministro требуется сетевой доступ. Включите мобильную сеть или Wi-Fi для загрузки данных.</string> + <string name="settings_msg">Настройка</string> + <string name="wait_for_network_connection_msg">Ожидание сетевого подключения</string> + <string name="download_app_libs_msg"> %1$s необходимы дополнительные библиотеки для работы.\nЗагрузить их прямо сейчас?</string> + <string name="start_downloading_msg">Началась загрузка ...</string> + <string name="downloading_qt_libraries_msg">Загрузка библиотек Qt</string> + <string name="checking_libraries_msg">Проверка библиотек. Подождите...</string> + <string name="extracting_SSL_msg">Извлечение корневых сертификатов SSL. Подождите...</string> + <string name="new_qt_libs_msg">Найдены новые библиотеки Qt</string> + <string name="new_qt_libs_tap_msg">Нажмите для поиска новых библиотек Qt.</string> + <string name="ministro_repository_msg">Ministro будет использовать репозиторий %1$s</string> + <string name="ministro_repository_changed_msg">Репозиторий Ministro изменён</string> + <string name="ministro_update_msg">Обновление Ministro</string> + <string name="ministro_repository_changed_tap_msg">Нажмите для изменения репозитория Ministro.</string> + <string name="invalid_parameters">Неверные параметры</string> + <string name="dependencies_error">Ministro не может удовлетворить зависимости вашего приложения</string> +</resources> diff --git a/Ministro/res/values-zh-rCN/strings.xml b/Ministro/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000..6e0e864 --- /dev/null +++ b/Ministro/res/values-zh-rCN/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">选择一个资源库</string> + <string name="incompatible_ministo_api">Ministro与您的应用程序不相容。请升级Ministro服务。</string> + <string name="ministro_network_access_msg">Ministro需要连接网络。请启用移动网络或无线网络下载数据。</string> + <string name="settings_msg">设置</string> + <string name="wait_for_network_connection_msg">等待网络连接</string> + <string name="download_app_libs_msg"> %1$s 需要额外的程式库运行。\n您想现在下载它吗?</string> + <string name="start_downloading_msg">开始下载。。。</string> + <string name="downloading_qt_libraries_msg">下载Qt程式库</string> + <string name="checking_libraries_msg">检查程式库。请稍候。。。</string> + <string name="extracting_SSL_msg">提取SSL根证书。请稍候。。。</string> + <string name="new_qt_libs_msg">找到新的Qt程式库</string> + <string name="new_qt_libs_tap_msg">已找到新的Qt程式库 轻敲进行更新。</string> + <string name="ministro_repository_msg">Ministro将使用 %1$s 资源库</string> + <string name="ministro_repository_changed_msg">Ministro资源库已更改</string> + <string name="ministro_update_msg">Ministro更新</string> + <string name="ministro_repository_changed_tap_msg">Ministro资源库已更改 轻敲进行更新。</string> + <string name="invalid_parameters">参数无效</string> + <string name="dependencies_error">Ministro不能满足您的应用程序依赖需求</string> +</resources> diff --git a/Ministro/res/values-zh-rTW/strings.xml b/Ministro/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000..83a19fa --- /dev/null +++ b/Ministro/res/values-zh-rTW/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">選擇一個資源庫</string> + <string name="incompatible_ministo_api">Ministro與您的應用程序不相容。請升級Ministro服務。</string> + <string name="ministro_network_access_msg">Ministro需要連接網絡。請啟用移動網絡或無線網絡下載數據。</string> + <string name="settings_msg">設置</string> + <string name="wait_for_network_connection_msg">等待網絡連接</string> + <string name="download_app_libs_msg"> %1$s 需要額外的程式庫運行。\n您想現在下載它嗎?</string> + <string name="start_downloading_msg">開始下載。。。</string> + <string name="downloading_qt_libraries_msg">下載Qt程式庫</string> + <string name="checking_libraries_msg">檢查程式庫。請稍候。。。</string> + <string name="extracting_SSL_msg">提取SSL根證書。請稍候。。。</string> + <string name="new_qt_libs_msg">找到新的Qt程式庫</string> + <string name="new_qt_libs_tap_msg">已找到新的Qt程式庫 輕敲進行更新。</string> + <string name="ministro_repository_msg">Ministro將使用 %1$s 資源庫</string> + <string name="ministro_repository_changed_msg">Ministro資源庫已更改</string> + <string name="ministro_update_msg">Ministro更新</string> + <string name="ministro_repository_changed_tap_msg">Ministro資源庫已更改 輕敲進行更新。</string> + <string name="invalid_parameters">參數無效</string> + <string name="dependencies_error">Ministro不能滿足您的應用程序依賴需求</string> +</resources> diff --git a/Ministro/res/values/arrays.xml b/Ministro/res/values/arrays.xml new file mode 100644 index 0000000..2c2d00b --- /dev/null +++ b/Ministro/res/values/arrays.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + <string-array name="repositories"> + <item>stable</item> + <item>testing</item> + <item>unstable</item> + </string-array> + <string-array name="check_frequency"> + <item>1</item> + <item>7</item> + <item>30</item> + </string-array> +</resources> diff --git a/Ministro/res/values/strings.xml b/Ministro/res/values/strings.xml new file mode 100644 index 0000000..934af00 --- /dev/null +++ b/Ministro/res/values/strings.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + <string name="app_name">Ministro</string> + <string name="repositories_prompt">Choose a repository</string> + <string name="incompatible_ministo_api">Ministro is not compatible with your application. Please upgrade Ministro service.</string> + <string name="ministro_network_access_msg">Ministro requires network access. Enable mobile network or Wi-Fi to download data.</string> + <string name="settings_msg">Settings</string> + <string name="wait_for_network_connection_msg">Waiting for network connection</string> + <string name="download_app_libs_msg"> %1$s needs extra libraries to run.\nDo you want to download them now?</string> + <string name="start_downloading_msg">Start downloading ...</string> + <string name="downloading_qt_libraries_msg">Downloading libraries</string> + <string name="checking_libraries_msg">Checking libraries. Please wait...</string> + <string name="extracting_SSL_msg">Extracting SSL root certificates. Please wait...</string> + <string name="new_qt_libs_msg">New libs have been found</string> + <string name="new_qt_libs_tap_msg">New libs have been found tap to update.</string> + <string name="ministro_repository_msg">Ministro will use %1$s repository</string> + <string name="ministro_repository_changed_msg">Ministro repository changed</string> + <string name="ministro_update_msg">Ministro update</string> + <string name="ministro_repository_changed_tap_msg">Ministro repository changed tap to update.</string> + <string name="invalid_parameters">Invalid parameters</string> + <string name="dependencies_error">Ministro can not satisfy your application dependencies</string> + <string name="check_frequency">Check for updates frequency (days)</string> + <string name="extracting_look_n_feel_msg">Extracting the device look&feel information. Please wait...</string> + <string name="invalid_qt_version">Invalid Qt version</string> + <string name="ministro_disk_space_msg">Not enough space to download required libraries.\nPlease free at least an additional %1$s of space.</string> +</resources> diff --git a/Ministro/src/org/kde/necessitas/ministro/ExtractStyle.java b/Ministro/src/org/kde/necessitas/ministro/ExtractStyle.java new file mode 100644 index 0000000..7d02707 --- /dev/null +++ b/Ministro/src/org/kde/necessitas/ministro/ExtractStyle.java @@ -0,0 +1,1291 @@ +/* + Copyright (c) 2011, BogDan Vatra <bog_dan_ro@yahoo.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package org.kde.necessitas.ministro; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.ArrayList; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.xmlpull.v1.XmlPullParser; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ClipDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.GradientDrawable.Orientation; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.NinePatchDrawable; +import android.graphics.drawable.StateListDrawable; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Xml; +import android.view.inputmethod.EditorInfo; + + +public class ExtractStyle { + + native static int[] extractChunkInfo(byte[] chunkData); + native static int[] extract9PatchInfo(Object ninePatchDrawable); + native static Object getClipStateDrawableObject(Object clipStateObject); + + Class<?> styleableClass = getStylableClass(); + final int[] EMPTY_STATE_SET = {}; + final int[] ENABLED_STATE_SET = {android.R.attr.state_enabled}; + final int[] FOCUSED_STATE_SET = {android.R.attr.state_focused}; + final int[] SELECTED_STATE_SET = {android.R.attr.state_selected}; + final int[] PRESSED_STATE_SET = {android.R.attr.state_pressed}; + final int[] WINDOW_FOCUSED_STATE_SET = {android.R.attr.state_window_focused}; + final int[] ENABLED_FOCUSED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, FOCUSED_STATE_SET); + final int[] ENABLED_SELECTED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, SELECTED_STATE_SET); + final int[] ENABLED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] FOCUSED_SELECTED_STATE_SET = stateSetUnion(FOCUSED_STATE_SET, SELECTED_STATE_SET); + final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] ENABLED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_STATE_SET, SELECTED_STATE_SET); + final int[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] PRESSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] PRESSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, SELECTED_STATE_SET); + final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] PRESSED_FOCUSED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, FOCUSED_STATE_SET); + final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] PRESSED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_STATE_SET, SELECTED_STATE_SET); + final int[] PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] PRESSED_ENABLED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, ENABLED_STATE_SET); + final int[] PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] PRESSED_ENABLED_SELECTED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, SELECTED_STATE_SET); + final int[] PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] PRESSED_ENABLED_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, FOCUSED_STATE_SET); + final int[] PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + final int[] PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, SELECTED_STATE_SET); + final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET); + + + final int View_background = getField(styleableClass,"View_background"); + final int View_padding = getField(styleableClass,"View_padding"); + final int View_paddingLeft = getField(styleableClass,"View_paddingLeft"); + final int View_paddingTop = getField(styleableClass,"View_paddingTop"); + final int View_paddingRight = getField(styleableClass,"View_paddingRight"); + final int View_paddingBottom = getField(styleableClass,"View_paddingBottom"); + final int View_scrollX = getField(styleableClass,"View_scrollX"); + final int View_scrollY = getField(styleableClass,"View_scrollY"); + final int View_id = getField(styleableClass,"View_id"); + final int View_tag = getField(styleableClass,"View_tag"); + final int View_fitsSystemWindows = getField(styleableClass,"View_fitsSystemWindows"); + final int View_focusable = getField(styleableClass,"View_focusable"); + final int View_focusableInTouchMode = getField(styleableClass,"View_focusableInTouchMode"); + final int View_clickable = getField(styleableClass,"View_clickable"); + final int View_longClickable = getField(styleableClass,"View_longClickable"); + final int View_saveEnabled = getField(styleableClass,"View_saveEnabled"); + final int View_duplicateParentState = getField(styleableClass,"View_duplicateParentState"); + final int View_visibility = getField(styleableClass,"View_visibility"); + final int View_drawingCacheQuality = getField(styleableClass,"View_drawingCacheQuality"); + final int View_contentDescription = getField(styleableClass,"View_contentDescription"); + final int View_soundEffectsEnabled = getField(styleableClass,"View_soundEffectsEnabled"); + final int View_hapticFeedbackEnabled = getField(styleableClass,"View_hapticFeedbackEnabled"); + final int View_scrollbars = getField(styleableClass,"View_scrollbars"); + final int View_fadingEdge = getField(styleableClass,"View_fadingEdge"); + final int View_scrollbarStyle = getField(styleableClass,"View_scrollbarStyle"); + final int View_isScrollContainer = getField(styleableClass,"View_isScrollContainer"); + final int View_keepScreenOn = getField(styleableClass,"View_keepScreenOn"); + final int View_filterTouchesWhenObscured = getField(styleableClass,"View_filterTouchesWhenObscured"); + final int View_nextFocusLeft = getField(styleableClass,"View_nextFocusLeft"); + final int View_nextFocusRight = getField(styleableClass,"View_nextFocusRight"); + final int View_nextFocusUp = getField(styleableClass,"View_nextFocusUp"); + final int View_nextFocusDown = getField(styleableClass,"View_nextFocusDown"); + final int View_minWidth = getField(styleableClass,"View_minWidth"); + final int View_minHeight = getField(styleableClass,"View_minHeight"); + final int View_onClick = getField(styleableClass,"View_onClick"); + final int View_overScrollMode = getField(styleableClass,"View_overScrollMode"); + + + final int TextAppearance_textColorHighlight = getField(styleableClass,"TextAppearance_textColorHighlight"); + final int TextAppearance_textColor = getField(styleableClass,"TextAppearance_textColor"); + final int TextAppearance_textColorHint = getField(styleableClass,"TextAppearance_textColorHint"); + final int TextAppearance_textColorLink = getField(styleableClass,"TextAppearance_textColorLink"); + final int TextAppearance_textSize = getField(styleableClass,"TextAppearance_textSize"); + final int TextAppearance_typeface = getField(styleableClass,"TextAppearance_typeface"); + final int TextAppearance_textStyle = getField(styleableClass,"TextAppearance_textStyle"); + final int TextAppearance_textAllCaps = getField(styleableClass,"TextAppearance_textAllCaps"); + final int TextView_editable = getField(styleableClass,"TextView_editable"); + final int TextView_inputMethod = getField(styleableClass,"TextView_inputMethod"); + final int TextView_numeric = getField(styleableClass,"TextView_numeric"); + final int TextView_digits = getField(styleableClass,"TextView_digits"); + final int TextView_phoneNumber = getField(styleableClass,"TextView_phoneNumber"); + final int TextView_autoText = getField(styleableClass,"TextView_autoText"); + final int TextView_capitalize = getField(styleableClass,"TextView_capitalize"); + final int TextView_bufferType = getField(styleableClass,"TextView_bufferType"); + final int TextView_selectAllOnFocus = getField(styleableClass,"TextView_selectAllOnFocus"); + final int TextView_autoLink = getField(styleableClass,"TextView_autoLink"); + final int TextView_linksClickable = getField(styleableClass,"TextView_linksClickable"); + final int TextView_drawableLeft = getField(styleableClass,"TextView_drawableLeft"); + final int TextView_drawableTop = getField(styleableClass,"TextView_drawableTop"); + final int TextView_drawableRight = getField(styleableClass,"TextView_drawableRight"); + final int TextView_drawableBottom = getField(styleableClass,"TextView_drawableBottom"); + final int TextView_drawableStart = getField(styleableClass,"TextView_drawableStart"); + final int TextView_drawableEnd = getField(styleableClass,"TextView_drawableEnd"); + final int TextView_drawablePadding = getField(styleableClass,"TextView_drawablePadding"); + final int TextView_textCursorDrawable = getField(styleableClass,"TextView_textCursorDrawable"); + final int TextView_maxLines = getField(styleableClass,"TextView_maxLines"); + final int TextView_maxHeight = getField(styleableClass,"TextView_maxHeight"); + final int TextView_lines = getField(styleableClass,"TextView_lines"); + final int TextView_height = getField(styleableClass,"TextView_height"); + final int TextView_minLines = getField(styleableClass,"TextView_minLines"); + final int TextView_minHeight = getField(styleableClass,"TextView_minHeight"); + final int TextView_maxEms = getField(styleableClass,"TextView_maxEms"); + final int TextView_maxWidth = getField(styleableClass,"TextView_maxWidth"); + final int TextView_ems = getField(styleableClass,"TextView_ems"); + final int TextView_width = getField(styleableClass,"TextView_width"); + final int TextView_minEms = getField(styleableClass,"TextView_minEms"); + final int TextView_minWidth = getField(styleableClass,"TextView_minWidth"); + final int TextView_gravity = getField(styleableClass,"TextView_gravity"); + final int TextView_hint = getField(styleableClass,"TextView_hint"); + final int TextView_text = getField(styleableClass,"TextView_text"); + final int TextView_scrollHorizontally = getField(styleableClass,"TextView_scrollHorizontally"); + final int TextView_singleLine = getField(styleableClass,"TextView_singleLine"); + final int TextView_ellipsize = getField(styleableClass,"TextView_ellipsize"); + final int TextView_marqueeRepeatLimit = getField(styleableClass,"TextView_marqueeRepeatLimit"); + final int TextView_includeFontPadding = getField(styleableClass,"TextView_includeFontPadding"); + final int TextView_cursorVisible = getField(styleableClass,"TextView_cursorVisible"); + final int TextView_maxLength = getField(styleableClass,"TextView_maxLength"); + final int TextView_textScaleX = getField(styleableClass,"TextView_textScaleX"); + final int TextView_freezesText = getField(styleableClass,"TextView_freezesText"); + final int TextView_shadowColor = getField(styleableClass,"TextView_shadowColor"); + final int TextView_shadowDx = getField(styleableClass,"TextView_shadowDx"); + final int TextView_shadowDy = getField(styleableClass,"TextView_shadowDy"); + final int TextView_shadowRadius = getField(styleableClass,"TextView_shadowRadius"); + final int TextView_enabled = getField(styleableClass,"TextView_enabled"); + final int TextView_textColorHighlight = getField(styleableClass,"TextView_textColorHighlight"); + final int TextView_textColor = getField(styleableClass,"TextView_textColor"); + final int TextView_textColorHint = getField(styleableClass,"TextView_textColorHint"); + final int TextView_textColorLink = getField(styleableClass,"TextView_textColorLink"); + final int TextView_textSize = getField(styleableClass,"TextView_textSize"); + final int TextView_typeface = getField(styleableClass,"TextView_typeface"); + final int TextView_textStyle = getField(styleableClass,"TextView_textStyle"); + final int TextView_password = getField(styleableClass,"TextView_password"); + final int TextView_lineSpacingExtra = getField(styleableClass,"TextView_lineSpacingExtra"); + final int TextView_lineSpacingMultiplier = getField(styleableClass,"TextView_lineSpacingMultiplier"); + final int TextView_inputType = getField(styleableClass,"TextView_inputType"); + final int TextView_imeOptions = getField(styleableClass,"TextView_imeOptions"); + final int TextView_imeActionLabel = getField(styleableClass,"TextView_imeActionLabel"); + final int TextView_imeActionId = getField(styleableClass,"TextView_imeActionId"); + final int TextView_privateImeOptions = getField(styleableClass,"TextView_privateImeOptions"); + final int TextView_textSelectHandleLeft = getField(styleableClass,"TextView_textSelectHandleLeft"); + final int TextView_textSelectHandleRight = getField(styleableClass,"TextView_textSelectHandleRight"); + final int TextView_textSelectHandle = getField(styleableClass,"TextView_textSelectHandle"); + final int TextView_textIsSelectable = getField(styleableClass,"TextView_textIsSelectable"); + final int TextView_textAllCaps = getField(styleableClass,"TextView_textAllCaps"); + + final int ImageView_src = getField(styleableClass,"ImageView_src"); + final int ImageView_baselineAlignBottom = getField(styleableClass,"ImageView_baselineAlignBottom"); + final int ImageView_adjustViewBounds = getField(styleableClass,"ImageView_adjustViewBounds"); + final int ImageView_maxWidth = getField(styleableClass,"ImageView_maxWidth"); + final int ImageView_maxHeight = getField(styleableClass,"ImageView_maxHeight"); + final int ImageView_scaleType = getField(styleableClass,"ImageView_scaleType"); + final int ImageView_tint = getField(styleableClass,"ImageView_tint"); + final int ImageView_cropToPadding = getField(styleableClass,"ImageView_cropToPadding"); + + final Resources.Theme m_theme; + final String m_extractPath; + Context m_context; + + + class FakeCanvas extends Canvas { + int[] chunkData= null; + class Size { + public int s,e; + Size(int start, int end) + { + s=start; + e=end; + } + } + public boolean isHardwareAccelerated() { + return true; + } + public void drawPatch(Bitmap bmp, byte[] chunks, RectF dst, Paint paint) { + chunkData=extractChunkInfo(chunks); + } + } + + + + private int[] stateSetUnion(final int[] stateSet1, final int[] stateSet2) + { + try + { + final int stateSet1Length = stateSet1.length; + final int stateSet2Length = stateSet2.length; + final int[] newSet = new int[stateSet1Length + stateSet2Length]; + int k = 0; + int i = 0; + int j = 0; + // This is a merge of the two input state sets and assumes that the + // input sets are sorted by the order imposed by ViewDrawableStates. + int[] viewDrawableStatesState=(int[]) styleableClass.getDeclaredField("ViewDrawableStates").get(null); + for (int viewState : viewDrawableStatesState) + { + if (i < stateSet1Length && stateSet1[i] == viewState) + { + newSet[k++] = viewState; + i++; + } else if (j < stateSet2Length && stateSet2[j] == viewState) { + newSet[k++] = viewState; + j++; + } + if (k > 1) { + assert(newSet[k - 1] > newSet[k - 2]); + } + } + return newSet; + } + catch(Exception e) + { + e.printStackTrace(); + } + return null; + } + + private Class<?> getStylableClass() { + try { + return Class.forName("android.R$styleable"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + int getField(Class<?> clazz, String fieldName) + { + try { + return clazz.getDeclaredField(fieldName).getInt(null); + } catch (Exception e) { + e.printStackTrace(); + } + return -1; + } + + JSONObject getColorStateList(ColorStateList colorList) + { + JSONObject json = new JSONObject(); + try + { + json.put("EMPTY_STATE_SET", colorList.getColorForState(EMPTY_STATE_SET, 0)); + json.put("WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(WINDOW_FOCUSED_STATE_SET, 0)); + json.put("SELECTED_STATE_SET", colorList.getColorForState(SELECTED_STATE_SET, 0)); + json.put("SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(SELECTED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("FOCUSED_STATE_SET", colorList.getColorForState(FOCUSED_STATE_SET, 0)); + json.put("FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(FOCUSED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(FOCUSED_SELECTED_STATE_SET, 0)); + json.put("FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("ENABLED_STATE_SET", colorList.getColorForState(ENABLED_STATE_SET, 0)); + json.put("ENABLED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("ENABLED_SELECTED_STATE_SET", colorList.getColorForState(ENABLED_SELECTED_STATE_SET, 0)); + json.put("ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("ENABLED_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_STATE_SET, 0)); + json.put("ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("ENABLED_FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_SELECTED_STATE_SET, 0)); + json.put("ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("PRESSED_STATE_SET", colorList.getColorForState(PRESSED_STATE_SET, 0)); + json.put("PRESSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("PRESSED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_SELECTED_STATE_SET, 0)); + json.put("PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("PRESSED_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_STATE_SET, 0)); + json.put("PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("PRESSED_FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_SELECTED_STATE_SET, 0)); + json.put("PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("PRESSED_ENABLED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_STATE_SET, 0)); + json.put("PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("PRESSED_ENABLED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_SELECTED_STATE_SET, 0)); + json.put("PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("PRESSED_ENABLED_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_STATE_SET, 0)); + json.put("PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET, 0)); + json.put("PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET, 0)); + json.put("PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0)); + } catch (JSONException e) { + e.printStackTrace(); + } + + return json; + } + + final int [] DrawableStates ={android.R.attr.state_active, android.R.attr.state_checked + , android.R.attr.state_enabled, android.R.attr.state_focused + , android.R.attr.state_pressed, android.R.attr.state_selected + , android.R.attr.state_window_focused, 16908288}; + final String[] DrawableStatesLabels = {"active", "checked", "enabled", "focused", "pressed", "selected", "window_focused", "backdroud"}; + final String[] DisableDrawableStatesLabels = {"inactive", "unchecked", "disabled", "not_focused", "no_pressed", "unselected", "window_not_focused", "backdroud"}; + + String getFileName(String file, String[] states) + { + for (String state: states) + file+="__"+state; + return file; + } + + String getStatesName(String[] states) + { + String statesName=""; + for (String state: states) + { + if (statesName.length()>0) + statesName+="__"; + statesName += state; + } + return statesName; + } + + void addDrawableItemIfNotExists(JSONObject json, ArrayList<Integer> list, Drawable item, String[] states, String filename) + { + for(Integer it : list) + { + if (it.equals(item.hashCode())) + return; + } + list.add(item.hashCode()); + try { + json.put(getStatesName(states), getDrawable(item, getFileName(filename, states))); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + void addSolution(String filename, JSONObject json, int c, Drawable drawable, ArrayList<Integer> drawableList, int u) + { + int pos=0; + int states[] = new int[c]; + String [] statesText = new String[c]; + + for(int n= 0;u > 0;++n, u>>= 1) + if((u & 1) > 0) + { + statesText[pos]=DrawableStatesLabels[n]; + states[pos++]=DrawableStates[n]; + } + drawable.setState(states); + addDrawableItemIfNotExists(json, drawableList, drawable.getCurrent(), statesText, filename); + } + + int bitCount(int u) + { + int n; + for(n= 0;u > 0;++n, u&= (u - 1)); + return n; + } + + Object getStateListDrawable_old(Drawable drawable, String filename) + { + JSONObject json = new JSONObject(); + ArrayList<Integer> drawableList= new ArrayList<Integer>(); + drawable.setState(EMPTY_STATE_SET); + try { + json.put("empty", getDrawable(drawable.getCurrent(), filename)); + drawableList.add(drawable.getCurrent().hashCode()); + } catch (JSONException e) { + e.printStackTrace(); + } + for (int c = 1;c<=DrawableStates.length;c++) + for(int u= 0;u < 1 << DrawableStates.length;u++) + if(bitCount(u) == c) + addSolution(filename, json, c, drawable, drawableList, u); + return json; + } + + JSONObject getStatesList(int [] states) throws JSONException + { + JSONObject json = new JSONObject(); + for (int s : states) + { + boolean found=false; + for (int d = 0;d<DrawableStates.length;d++) + { + if (s==DrawableStates[d]) + { + json.put(DrawableStatesLabels[d], true); + found=true; + break; + } + else if (s==-DrawableStates[d]) + { + json.put(DrawableStatesLabels[d], false); + + found=true; + break; + } + } + if (!found) + { + json.put("unhandled_state_"+s,s>0); + } + } + return json; + } + + String getStatesName(int [] states) + { + String statesName=""; + for (int s : states) + { + boolean found=false; + for (int d = 0;d<DrawableStates.length;d++) + { + if (s==DrawableStates[d]) + { + if (statesName.length()>0) + statesName+="__"; + statesName+=DrawableStatesLabels[d]; + found=true; + break; + } + else if (s==-DrawableStates[d]) + { + if (statesName.length()>0) + statesName+="__"; + statesName+=DisableDrawableStatesLabels[d]; + found=true; + break; + } + } + if (!found) + { + if (statesName.length()>0) + statesName+=";"; + statesName+=s; + } + } + if (statesName.length()>0) + return statesName; + return "empty"; + } + + private JSONObject getLayerDrawable(Object drawable, String filename) + { + JSONObject json = new JSONObject(); + LayerDrawable layers = (LayerDrawable) drawable; + final int nr=layers.getNumberOfLayers(); + try { + JSONArray array =new JSONArray(); + for (int i = 0; i < nr; i++) + { + JSONObject layerJsonObject=getDrawable(layers.getDrawable(i), filename+"__"+layers.getId(i)); + layerJsonObject.put("id", layers.getId(i)); + array.put(layerJsonObject); + } + json.put("type", "layer"); + Rect padding = new Rect(); + if (layers.getPadding(padding)) + json.put("padding", getJsonRect(padding)); + json.put("layers", array); + } catch (JSONException e) { + e.printStackTrace(); + } + return json; + } + + private JSONObject getStateListDrawable(Object drawable, String filename) + { + JSONObject json = new JSONObject(); + try { + StateListDrawable stateList = (StateListDrawable) drawable; + final int numStates = (Integer) StateListDrawable.class.getMethod("getStateCount").invoke(stateList); + JSONArray array =new JSONArray(); + for (int i = 0; i < numStates; i++) + { + JSONObject stateJson = new JSONObject(); + final Drawable d = (Drawable) StateListDrawable.class.getMethod("getStateDrawable", Integer.TYPE).invoke(stateList, i); + final int [] states = (int[]) StateListDrawable.class.getMethod("getStateSet", Integer.TYPE).invoke(stateList, i); + stateJson.put("states", getStatesList(states)); + stateJson.put("drawable", getDrawable(d, filename+"__"+getStatesName(states))); + array.put(stateJson); + } + json.put("type", "stateslist"); + Rect padding = new Rect(); + if (stateList.getPadding(padding)) + json.put("padding", getJsonRect(padding)); + json.put("stateslist", array); + } catch (Exception e) { + e.printStackTrace(); + } + return json; + } + + private JSONObject getGradientDrawable(GradientDrawable drawable) { + JSONObject json = new JSONObject(); + try { + json.put("type", "gradient"); + Object obj=drawable.getConstantState(); + Class<?> gradientStateClass=obj.getClass(); + json.put("shape",gradientStateClass.getField("mShape").getInt(obj)); + json.put("gradient",gradientStateClass.getField("mGradient").getInt(obj)); + GradientDrawable.Orientation orientation=(Orientation) gradientStateClass.getField("mOrientation").get(obj); + json.put("orientation",orientation.name()); + int [] intArray=(int[]) gradientStateClass.getField("mColors").get(obj); + json.put("colors",getJsonArray(intArray, 0, intArray.length)); + json.put("positions",getJsonArray((float[]) gradientStateClass.getField("mPositions").get(obj))); + json.put("solidColor",gradientStateClass.getField("mSolidColor").getInt(obj)); + json.put("strokeWidth",gradientStateClass.getField("mStrokeWidth").getInt(obj)); + json.put("strokeColor",gradientStateClass.getField("mStrokeColor").getInt(obj)); + json.put("strokeDashWidth",gradientStateClass.getField("mStrokeDashWidth").getFloat(obj)); + json.put("strokeDashGap",gradientStateClass.getField("mStrokeDashGap").getFloat(obj)); + json.put("radius",gradientStateClass.getField("mRadius").getFloat(obj)); + float [] floatArray=(float[]) gradientStateClass.getField("mRadiusArray").get(obj); + if (floatArray!=null) + json.put("radiusArray",getJsonArray(floatArray)); + Rect rc= (Rect) gradientStateClass.getField("mPadding").get(obj); + if (rc!=null) + json.put("padding",getJsonRect(rc)); + json.put("width",gradientStateClass.getField("mWidth").getInt(obj)); + json.put("height",gradientStateClass.getField("mHeight").getInt(obj)); + json.put("innerRadiusRatio",gradientStateClass.getField("mInnerRadiusRatio").getFloat(obj)); + json.put("thicknessRatio",gradientStateClass.getField("mThicknessRatio").getFloat(obj)); + json.put("innerRadius",gradientStateClass.getField("mInnerRadius").getInt(obj)); + json.put("thickness",gradientStateClass.getField("mThickness").getInt(obj)); + } catch (Exception e) { + e.printStackTrace(); + } + return json; + } + + private JSONObject getJsonRect(Rect rect) throws JSONException + { + JSONObject jsonRect = new JSONObject(); + jsonRect.put("left", rect.left); + jsonRect.put("top", rect.top); + jsonRect.put("right", rect.right); + jsonRect.put("bottom", rect.bottom); + return jsonRect; + + } + + private JSONArray getJsonArray(int[] array, int pos, int len) + { + JSONArray a = new JSONArray(); + final int l = pos+len; + for (int i=pos; i<l;i++) + a.put(array[i]); + return a; + } + + private JSONArray getJsonArray(float[] array) throws JSONException + { + JSONArray a = new JSONArray(); + if (array != null) + for (float val: array) + a.put(val); + return a; + } + + private JSONObject getJsonChunkInfo(int[] chunkData) throws JSONException + { + JSONObject jsonRect = new JSONObject(); + jsonRect.put("xdivs", getJsonArray(chunkData, 3, chunkData[0])); + jsonRect.put("ydivs", getJsonArray(chunkData, 3+chunkData[0], chunkData[1])); + jsonRect.put("colors", getJsonArray(chunkData, 3+chunkData[0]+chunkData[1], chunkData[2])); + return jsonRect; + } + + private JSONObject findPatchesMarings(Drawable d) throws JSONException + { + if (Build.VERSION.SDK_INT>=11) + { + FakeCanvas fc = new FakeCanvas(); + d.draw(fc); + return getJsonChunkInfo(fc.chunkData); + } + return getJsonChunkInfo(extract9PatchInfo(d)); + } + + public JSONObject getDrawable(Object drawable, String filename) + { + JSONObject json = new JSONObject(); + Bitmap bmp = null; + if (drawable instanceof Bitmap) + bmp = (Bitmap) drawable; + else + { + if (drawable instanceof BitmapDrawable) + bmp = ((BitmapDrawable)drawable).getBitmap(); + else + { + if (drawable instanceof LayerDrawable) + { + return getLayerDrawable(drawable, filename); + } + if (drawable instanceof StateListDrawable) + { + return getStateListDrawable(drawable, filename); + } + if (drawable instanceof GradientDrawable) + { + return getGradientDrawable((GradientDrawable) drawable); + } + if (drawable instanceof ClipDrawable) + { + try { + json.put("type", "clipDrawable"); + json.put("drawable", getDrawable(getClipStateDrawableObject(((ClipDrawable)drawable).getConstantState()), filename)); + Rect padding = new Rect(); + if (((Drawable) drawable).getPadding(padding)) + json.put("padding", getJsonRect(padding)); + } catch (Exception e) { + e.printStackTrace(); + } + return json; + } + if (drawable instanceof ColorDrawable) + { + bmp = Bitmap.createBitmap(1, 1, Config.ARGB_8888); + Drawable d = (Drawable) drawable; + d.setBounds(0, 0, 1, 1); + d.draw(new Canvas(bmp)); + Rect padding = new Rect(); + try { + json.put("type", "color"); + json.put("color", bmp.getPixel(0, 0)); + if (d.getPadding(padding)) + json.put("padding", getJsonRect(padding)); + } catch (JSONException e) { + e.printStackTrace(); + } + return json; } + else + { + Drawable d = (Drawable) drawable; + int w=d.getIntrinsicWidth(); + int h=d.getIntrinsicHeight(); + d.setLevel(10000); + if (w<1 || h< 1) + { + w=100; + h=100; + } + bmp = Bitmap.createBitmap(w, h, Config.ARGB_8888); + d.setBounds(0, 0, w, h); + d.draw(new Canvas(bmp)); + if (drawable instanceof NinePatchDrawable) + { + NinePatchDrawable npd = (NinePatchDrawable) drawable; + try { + json.put("type", "9patch"); + json.put("drawable", getDrawable(bmp, filename)); + Rect padding = new Rect(); + if (npd.getPadding(padding)) + json.put("padding", getJsonRect(padding)); + json.put("chunkInfo", findPatchesMarings(d)); + return json; + } catch (JSONException e) { + e.printStackTrace(); + } + } + } + } + } + FileOutputStream out; + try { + filename = m_extractPath+filename+".png"; + out = new FileOutputStream(filename); + bmp.compress(Bitmap.CompressFormat.PNG, 100, out); + out.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + try { + json.put("type", "image"); + json.put("path", filename); + json.put("width", bmp.getWidth()); + json.put("height", bmp.getHeight()); + MinistroActivity.nativeChmode(filename, 0644); + } catch (JSONException e) { + e.printStackTrace(); + } + return json; + } + + public void extractViewInformations(String styleName, int styleId, JSONObject json, String qtClassName, AttributeSet attribSet) + { + try { + int[] viewAttrs; + viewAttrs = (int[]) styleableClass.getDeclaredField("View").get(null); + TypedArray a =m_theme.obtainStyledAttributes(attribSet, viewAttrs, styleId, 0); + + if (null != qtClassName) + json.put("qtClass", qtClassName); + + final int N = a.getIndexCount(); + for (int i = 0; i < N; i++) { + int attr = a.getIndex(i); + if (attr == View_background) + json.put("View_background", getDrawable(a.getDrawable(attr), styleName + "_View_background")); + else if (attr == View_padding) + json.put("View_padding", a.getDimensionPixelSize(attr, -1)); + else if (attr == View_paddingLeft) + json.put("View_paddingLeft", a.getDimensionPixelSize(attr, -1)); + else if (attr == View_paddingTop) + json.put("View_paddingTop", a.getDimensionPixelSize(attr, -1)); + else if (attr == View_paddingRight) + json.put("View_paddingRight", a.getDimensionPixelSize(attr, -1)); + else if (attr == View_paddingBottom) + json.put("View_paddingBottom", a.getDimensionPixelSize(attr, -1)); + else if (attr == View_scrollX) + json.put("View_paddingBottom", a.getDimensionPixelOffset(attr, 0)); + else if (attr == View_scrollY) + json.put("View_scrollY", a.getDimensionPixelOffset(attr, 0)); + else if (attr == View_id) + json.put("View_id", a.getResourceId(attr, -1)); + else if (attr == View_tag) + json.put("View_tag", a.getText(attr)); + else if (attr == View_fitsSystemWindows) + json.put("View_fitsSystemWindows", a.getBoolean(attr, false)); + else if (attr == View_focusable) + json.put("View_focusable", a.getBoolean(attr, false)); + else if (attr == View_focusableInTouchMode) + json.put("View_focusableInTouchMode", a.getBoolean(attr, false)); + else if (attr == View_clickable) + json.put("View_clickable", a.getBoolean(attr, false)); + else if (attr == View_longClickable) + json.put("View_longClickable", a.getBoolean(attr, false)); + else if (attr == View_saveEnabled) + json.put("View_saveEnabled", a.getBoolean(attr, true)); + else if (attr == View_duplicateParentState) + json.put("View_duplicateParentState", a.getBoolean(attr, false)); + else if (attr == View_visibility) + json.put("View_visibility", a.getInt(attr, 0)); + else if (attr == View_drawingCacheQuality) + json.put("View_drawingCacheQuality", a.getInt(attr, 0)); + else if (attr == View_drawingCacheQuality) + json.put("View_contentDescription", a.getString(attr)); + else if (attr == View_soundEffectsEnabled) + json.put("View_soundEffectsEnabled", a.getBoolean(attr, true)); + else if (attr == View_hapticFeedbackEnabled) + json.put("View_hapticFeedbackEnabled", a.getBoolean(attr, true)); + else if (attr == View_scrollbars) + json.put("View_scrollbars", a.getInt(attr, 0)); + else if (attr == View_fadingEdge) + json.put("View_fadingEdge", a.getInt(attr, 0)); + else if (attr == View_scrollbarStyle) + json.put("View_scrollbarStyle", a.getInt(attr, 0)); + else if (attr == View_isScrollContainer) + json.put("View_isScrollContainer", a.getBoolean(attr, false)); + else if (attr == View_keepScreenOn) + json.put("View_keepScreenOn", a.getBoolean(attr, false)); + else if (attr == View_filterTouchesWhenObscured) + json.put("View_filterTouchesWhenObscured", a.getBoolean(attr, false)); + else if (attr == View_nextFocusLeft) + json.put("View_nextFocusLeft", a.getResourceId(attr, -1)); + else if (attr == View_nextFocusRight) + json.put("View_nextFocusRight", a.getResourceId(attr, -1)); + else if (attr == View_nextFocusUp) + json.put("View_nextFocusUp", a.getResourceId(attr, -1)); + else if (attr == View_nextFocusDown) + json.put("View_nextFocusDown", a.getResourceId(attr, -1)); + else if (attr == View_minWidth) + json.put("View_minWidth", a.getDimensionPixelSize(attr, 0)); + else if (attr == View_minHeight) + json.put("View_minHeight", a.getDimensionPixelSize(attr, 0)); + else if (attr == View_onClick) + json.put("View_onClick", a.getString(attr)); + else if (attr == View_overScrollMode) + json.put("View_overScrollMode", a.getInt(attr, 1)); + } + a.recycle(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public JSONObject extractTextAppearanceInformations(String styleName, String qtClass, AttributeSet attribSet, int textAppearance ) + { + JSONObject json = new JSONObject(); + try + { + int textColorHighlight = 0; // + ColorStateList textColor = null; // + ColorStateList textColorHint = null; // + ColorStateList textColorLink = null; // + int textSize = 15; // + int typefaceIndex = -1; // + int styleIndex = -1; + boolean allCaps = false; + + Class<?> attrClass= Class.forName("android.R$attr"); + int styleId = attrClass.getDeclaredField(styleName).getInt(null); + + extractViewInformations(styleName, styleId, json, qtClass, attribSet); + + int[] textViewAttrs=(int[]) styleableClass.getDeclaredField("TextView").get(null); + TypedArray a =m_theme.obtainStyledAttributes(null, textViewAttrs, styleId, 0); + + TypedArray appearance = null; + if (-1==textAppearance) + textAppearance = a.getResourceId(styleableClass.getDeclaredField("TextView_textAppearance").getInt(null), -1); + + if (textAppearance != -1) + appearance = m_theme.obtainStyledAttributes(textAppearance, (int[]) styleableClass.getDeclaredField("TextAppearance").get(null)); + + if (appearance != null) + { + int n = appearance.getIndexCount(); + for (int i = 0; i < n; i++) + { + int attr = appearance.getIndex(i); + if (attr == TextAppearance_textColorHighlight) + textColorHighlight = appearance.getColor(attr, textColorHighlight); + else if (attr == TextAppearance_textColor) + textColor = appearance.getColorStateList(attr); + else if (attr == TextAppearance_textColorHint) + textColorHint = appearance.getColorStateList(attr); + else if (attr == TextAppearance_textColorLink) + textColorLink = appearance.getColorStateList(attr); + else if (attr == TextAppearance_textSize) + textSize = appearance.getDimensionPixelSize(attr, textSize); + else if (attr == TextAppearance_typeface) + typefaceIndex = appearance.getInt(attr, -1); + else if (attr == TextAppearance_textStyle) + styleIndex = appearance.getInt(attr, -1); + else if (attr == TextAppearance_textAllCaps) + allCaps = appearance.getBoolean(attr, false); + } + appearance.recycle(); + } + + int n = a.getIndexCount(); + + for (int i = 0; i < n; i++) { + int attr = a.getIndex(i); + + if (attr == TextView_editable) + json.put("TextView_editable", a.getBoolean(attr, false)); + else if (attr == TextView_inputMethod) + json.put("TextView_inputMethod", a.getText(attr)); + else if (attr == TextView_numeric) + json.put("TextView_numeric", a.getInt(attr, 0)); + else if (attr == TextView_digits) + json.put("TextView_digits", a.getText(attr)); + else if (attr == TextView_phoneNumber) + json.put("TextView_phoneNumber", a.getBoolean(attr, false)); + else if (attr == TextView_autoText) + json.put("TextView_autoText", a.getBoolean(attr, false)); + else if (attr == TextView_capitalize) + json.put("TextView_capitalize", a.getInt(attr, -1)); + else if (attr == TextView_bufferType) + json.put("TextView_bufferType", a.getInt(attr, 0)); + else if (attr == TextView_selectAllOnFocus) + json.put("TextView_selectAllOnFocus", a.getBoolean(attr, false)); + else if (attr == TextView_autoLink) + json.put("TextView_autoLink", a.getInt(attr, 0)); + else if (attr == TextView_linksClickable) + json.put("TextView_linksClickable", a.getBoolean(attr, true)); + else if (attr == TextView_linksClickable) + json.put("TextView_linksClickable", a.getBoolean(attr, true)); + else if (attr == TextView_drawableLeft) + json.put("TextView_drawableLeft", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableLeft")); + else if (attr == TextView_drawableTop) + json.put("TextView_drawableTop", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableTop")); + else if (attr == TextView_drawableRight) + json.put("TextView_drawableRight", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableRight")); + else if (attr == TextView_drawableBottom) + json.put("TextView_drawableBottom", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableBottom")); + else if (attr == TextView_drawableStart) + json.put("TextView_drawableStart", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableStart")); + else if (attr == TextView_drawableEnd) + json.put("TextView_drawableEnd", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableEnd")); + else if (attr == TextView_drawablePadding) + json.put("TextView_drawablePadding", a.getDimensionPixelSize(attr, 0)); + else if (attr == TextView_textCursorDrawable) + json.put("TextView_textCursorDrawable", getDrawable(m_context.getResources().getDrawable(a.getResourceId(attr, 0)), styleName + "_TextView_textCursorDrawable")); + else if (attr == TextView_maxLines) + json.put("TextView_maxLines", a.getInt(attr, -1)); + else if (attr == TextView_maxHeight) + json.put("TextView_maxHeight", a.getDimensionPixelSize(attr, -1)); + else if (attr == TextView_lines) + json.put("TextView_lines", a.getInt(attr, -1)); + else if (attr == TextView_height) + json.put("TextView_height", a.getDimensionPixelSize(attr, -1)); + else if (attr == TextView_minLines) + json.put("TextView_minLines", a.getInt(attr, -1)); + else if (attr == TextView_minHeight) + json.put("TextView_minHeight", a.getDimensionPixelSize(attr, -1)); + else if (attr == TextView_maxEms) + json.put("TextView_maxEms", a.getInt(attr, -1)); + else if (attr == TextView_maxWidth) + json.put("TextView_maxWidth", a.getDimensionPixelSize(attr, -1)); + else if (attr == TextView_ems) + json.put("TextView_ems", a.getInt(attr, -1)); + else if (attr == TextView_width) + json.put("TextView_width", a.getDimensionPixelSize(attr, -1)); + else if (attr == TextView_minEms) + json.put("TextView_minEms", a.getInt(attr, -1)); + else if (attr == TextView_minWidth) + json.put("TextView_minWidth", a.getDimensionPixelSize(attr, -1)); + else if (attr == TextView_gravity) + json.put("TextView_gravity", a.getInt(attr, -1)); + else if (attr == TextView_hint) + json.put("TextView_hint", a.getText(attr)); + else if (attr == TextView_text) + json.put("TextView_text", a.getText(attr)); + else if (attr == TextView_scrollHorizontally) + json.put("TextView_scrollHorizontally", a.getBoolean(attr, false)); + else if (attr == TextView_singleLine) + json.put("TextView_singleLine", a.getBoolean(attr, false)); + else if (attr == TextView_ellipsize) + json.put("TextView_ellipsize", a.getInt(attr, -1)); + else if (attr == TextView_marqueeRepeatLimit) + json.put("TextView_marqueeRepeatLimit", a.getInt(attr, 3)); + else if (attr == TextView_includeFontPadding) + json.put("TextView_includeFontPadding", a.getBoolean(attr, true)); + else if (attr == TextView_cursorVisible) + json.put("TextView_cursorVisible", a.getBoolean(attr, true)); + else if (attr == TextView_maxLength) + json.put("TextView_maxLength", a.getInt(attr, -1)); + else if (attr == TextView_textScaleX) + json.put("TextView_textScaleX", a.getFloat(attr, 1.0f)); + else if (attr == TextView_freezesText) + json.put("TextView_freezesText", a.getBoolean(attr, false)); + else if (attr == TextView_shadowColor) + json.put("TextView_shadowColor", a.getInt(attr, 0)); + else if (attr == TextView_shadowDx) + json.put("TextView_shadowDx", a.getFloat(attr, 0)); + else if (attr == TextView_shadowDy) + json.put("TextView_shadowDy", a.getFloat(attr, 0)); + else if (attr == TextView_shadowRadius) + json.put("TextView_shadowRadius", a.getFloat(attr, 0)); + else if (attr == TextView_enabled) + json.put("TextView_enabled", a.getBoolean(attr,true)); + else if (attr == TextView_textColorHighlight) + textColorHighlight = a.getColor(attr, textColorHighlight); + else if (attr == TextView_textColor) + textColor = a.getColorStateList(attr); + else if (attr == TextView_textColorHint) + textColorHint = a.getColorStateList(attr); + else if (attr == TextView_textColorLink) + textColorLink = a.getColorStateList(attr); + else if (attr == TextView_textSize) + textSize = a.getDimensionPixelSize(attr, textSize); + else if (attr == TextView_typeface) + typefaceIndex = a.getInt(attr, typefaceIndex); + else if (attr == TextView_textStyle) + styleIndex = a.getInt(attr, styleIndex); + else if (attr == TextView_password) + json.put("TextView_password", a.getBoolean(attr,false)); + else if (attr == TextView_lineSpacingExtra) + json.put("TextView_lineSpacingExtra", a.getDimensionPixelSize(attr, 0)); + else if (attr == TextView_lineSpacingMultiplier) + json.put("TextView_lineSpacingMultiplier", a.getFloat(attr, 1.0f)); + else if (attr == TextView_inputType) + json.put("TextView_inputType", a.getInt(attr, EditorInfo.TYPE_NULL)); + else if (attr == TextView_imeOptions) + json.put("TextView_imeOptions", a.getInt(attr, EditorInfo.IME_NULL)); + else if (attr == TextView_imeActionLabel) + json.put("TextView_imeActionLabel", a.getText(attr)); + else if (attr == TextView_imeActionId) + json.put("TextView_imeActionId", a.getInt(attr,0)); + else if (attr == TextView_privateImeOptions) + json.put("TextView_privateImeOptions", a.getString(attr)); + else if (attr == TextView_textSelectHandleLeft && styleName.equals("textViewStyle")) + json.put("TextView_textSelectHandleLeft", getDrawable(m_context.getResources().getDrawable(a.getResourceId(attr, 0)), styleName + "_TextView_textSelectHandleLeft")); + else if (attr == TextView_textSelectHandleRight && styleName.equals("textViewStyle")) + json.put("TextView_textSelectHandleRight", getDrawable(m_context.getResources().getDrawable(a.getResourceId(attr, 0)), styleName + "_TextView_textSelectHandleRight")); + else if (attr == TextView_textSelectHandle && styleName.equals("textViewStyle")) + json.put("TextView_textSelectHandle", getDrawable(m_context.getResources().getDrawable(a.getResourceId(attr, 0)), styleName + "_TextView_textSelectHandle")); + else if (attr == TextView_textIsSelectable) + json.put("TextView_textIsSelectable", a.getBoolean(attr, false)); + else if (attr == TextView_textAllCaps) + allCaps = a.getBoolean(attr, false); + } + a.recycle(); + + json.put("TextAppearance_textColorHighlight",textColorHighlight); + json.put("TextAppearance_textColor", getColorStateList(textColor)); + json.put("TextAppearance_textColorHint", getColorStateList(textColorHint)); + json.put("TextAppearance_textColorLink", getColorStateList(textColorLink)); + json.put("TextAppearance_textSize",textSize); + json.put("TextAppearance_typeface",typefaceIndex); + json.put("TextAppearance_textStyle",styleIndex); + json.put("TextAppearance_textAllCaps",allCaps); + } + catch(Exception e) + { + e.printStackTrace(); + } + return json; + } + + final String[] sScaleTypeArray = { + "MATRIX", + "FIT_XY", + "FIT_START", + "FIT_CENTER", + "FIT_END", + "CENTER", + "CENTER_CROP", + "CENTER_INSIDE" + }; + + public JSONObject extractImageViewInformations(String styleName, String qtClassName ) + { + JSONObject json = new JSONObject(); + try + { + Class<?> attrClass= Class.forName("android.R$attr"); + int styleId = attrClass.getDeclaredField(styleName).getInt(null); + + extractViewInformations(styleName, styleId, json, qtClassName, null); + + int[] imageViewAttrs=(int[]) styleableClass.getDeclaredField("ImageView").get(null); + TypedArray a =m_theme.obtainStyledAttributes(null, imageViewAttrs, styleId, 0); + Drawable d = a.getDrawable(ImageView_src); + if (d != null) + json.put("ImageView_src", getDrawable(d, styleName + "_ImageView_src")); + + json.put("ImageView_baselineAlignBottom", a.getBoolean(ImageView_baselineAlignBottom, false)); + json.put("ImageView_adjustViewBounds", a.getBoolean(ImageView_adjustViewBounds, false)); + json.put("ImageView_maxWidth", a.getDimensionPixelSize(ImageView_maxWidth, Integer.MAX_VALUE)); + json.put("ImageView_maxHeight", a.getDimensionPixelSize(ImageView_maxHeight, Integer.MAX_VALUE)); + int index = a.getInt(ImageView_scaleType, -1); + if (index >= 0) + json.put("ImageView_scaleType", sScaleTypeArray[index]); + + int tint = a.getInt(ImageView_tint, 0); + if (tint != 0) + json.put("ImageView_tint", tint); + + + json.put("ImageView_cropToPadding",a.getBoolean(ImageView_cropToPadding, false)); + a.recycle(); + } + catch(Exception e) + { + e.printStackTrace(); + } + return json; + + } + + void extractCompoundButton(JSONObject parentObject, String styleName, String qtClass) + { + JSONObject json = extractTextAppearanceInformations(styleName, qtClass, null, -1); + Class<?> attrClass; + try { + attrClass = Class.forName("android.R$attr"); + int styleId = attrClass.getDeclaredField(styleName).getInt(null); + int[] compoundButtonAttrs=(int[]) styleableClass.getDeclaredField("CompoundButton").get(null); + + TypedArray a = m_theme.obtainStyledAttributes( + null, compoundButtonAttrs, styleId, 0); + + Drawable d = a.getDrawable(getField(styleableClass,"CompoundButton_button")); + if (d != null) + json.put("CompoundButton_button", getDrawable(d, styleName + "_CompoundButton_button")); + + a.recycle(); + parentObject.put(styleName, json); + } catch (Exception e) { + e.printStackTrace(); + } + } + + void extractProgressBarInfo(JSONObject json, String styleName) + { + Class<?> attrClass; + try { + attrClass = Class.forName("android.R$attr"); + int styleId = attrClass.getDeclaredField(styleName).getInt(null); + int[] progressBarAttrs=(int[]) styleableClass.getDeclaredField("ProgressBar").get(null); + + TypedArray a = m_theme.obtainStyledAttributes(null, progressBarAttrs, styleId, 0); + int mMinWidth = 24; + int mMaxWidth = 48; + int mMinHeight = 24; + int mMaxHeight = 48; + mMinWidth = a.getDimensionPixelSize(getField(styleableClass, "ProgressBar_minWidth"), mMinWidth); + mMaxWidth = a.getDimensionPixelSize(getField(styleableClass, "ProgressBar_maxWidth"), mMaxWidth); + mMinHeight = a.getDimensionPixelSize(getField(styleableClass, "ProgressBar_minHeight"), mMinHeight); + mMaxHeight = a.getDimensionPixelSize(getField(styleableClass, "ProgressBar_maxHeight"), mMaxHeight); + + json.put("ProgressBar_indeterminateDuration", a.getInt(getField(styleableClass, "ProgressBar_indeterminateDuration"), 4000)); + json.put("ProgressBar_minWidth", mMinWidth); + json.put("ProgressBar_maxWidth", mMaxWidth); + json.put("ProgressBar_minHeight", mMinHeight); + json.put("ProgressBar_maxHeight", mMaxHeight); + json.put("ProgressBar_progress_id", android.R.id.progress); + json.put("ProgressBar_secondaryProgress_id", android.R.id.secondaryProgress); + + Drawable d = a.getDrawable(getField(styleableClass,"ProgressBar_progressDrawable")); + if (d != null) + json.put("ProgressBar_progressDrawable", getDrawable(d, styleName + "_ProgressBar_progressDrawable")); + + d = a.getDrawable(getField(styleableClass,"ProgressBar_indeterminateDrawable")); + if (d != null) + json.put("ProgressBar_indeterminateDrawable", getDrawable(d, styleName + "_ProgressBar_indeterminateDrawable")); + + a.recycle(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + void extractProgressBar(JSONObject parentObject, String styleName, String qtClass) + { + JSONObject json = extractTextAppearanceInformations(styleName, qtClass, null, -1); + try { + extractProgressBarInfo(json, styleName); + parentObject.put(styleName, json); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + void extractAbsSeekBar(JSONObject parentObject, String styleName, String qtClass) + { + JSONObject json = extractTextAppearanceInformations(styleName, qtClass, null, -1); + extractProgressBarInfo(json, styleName); + Class<?> attrClass; + try { + attrClass = Class.forName("android.R$attr"); + int styleId = attrClass.getDeclaredField(styleName).getInt(null); + int[] compoundButtonAttrs=(int[]) styleableClass.getDeclaredField("SeekBar").get(null); + + TypedArray a = m_theme.obtainStyledAttributes( + null, compoundButtonAttrs, styleId, 0); + + Drawable d = a.getDrawable(getField(styleableClass,"SeekBar_thumb")); + if (d != null) + json.put("SeekBar_thumb", getDrawable(d, styleName + "_SeekBar_thumb")); + + a.recycle(); + parentObject.put(styleName, json); + } catch (Exception e) { + e.printStackTrace(); + } + } + + JSONObject extractCheckedTextView(AttributeSet attribSet, String itemName) + { + JSONObject json = extractTextAppearanceInformations("textViewStyle", itemName, attribSet, -1); + try { + Class<?> attrClass= Class.forName("android.R$attr"); + int styleId = attrClass.getDeclaredField("textViewStyle").getInt(null); + int[] compoundButtonAttrs=(int[]) styleableClass.getDeclaredField("CheckedTextView").get(null); + + TypedArray a = m_theme.obtainStyledAttributes(attribSet, compoundButtonAttrs, styleId, 0); + + Drawable d = a.getDrawable(getField(styleableClass,"CheckedTextView_checkMark")); + if (d != null) + json.put("CheckedTextView_checkMark", getDrawable(d, itemName+"_CheckedTextView_checkMark")); + + a.recycle(); + } catch (Exception e) { + e.printStackTrace(); + } + return json; + } + + private JSONObject extractItemStyle(int resourceId, String itemName, int textAppearance) { + try + { + XmlResourceParser parser = m_context.getResources().getLayout(resourceId); + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG && + type != XmlPullParser.END_DOCUMENT) { + // Empty + } + + if (type != XmlPullParser.START_TAG) { + return null; + } + + AttributeSet attributes = Xml.asAttributeSet(parser); + String name = parser.getName(); + if (name.equals("TextView")) + return extractTextAppearanceInformations("textViewStyle", itemName, attributes, textAppearance); + if (name.equals("CheckedTextView")) + return extractCheckedTextView(attributes, itemName); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private void extractItemsStyle(JSONObject json) { + try + { + json.put("simple_list_item",extractItemStyle(android.R.layout.simple_list_item_1, "simple_list_item", android.R.style.TextAppearance_Large)); + json.put("simple_list_item_checked",extractItemStyle(android.R.layout.simple_list_item_checked, "simple_list_item_checked", android.R.style.TextAppearance_Large)); + json.put("simple_list_item_multiple_choice",extractItemStyle(android.R.layout.simple_list_item_multiple_choice, "simple_list_item_multiple_choice", android.R.style.TextAppearance_Large)); + json.put("simple_list_item_single_choice",extractItemStyle(android.R.layout.simple_list_item_single_choice, "simple_list_item_single_choice", android.R.style.TextAppearance_Large)); + json.put("simple_spinner_item",extractItemStyle(android.R.layout.simple_spinner_item, "simple_spinner_item", -1)); + json.put("simple_spinner_dropdown_item",extractItemStyle(android.R.layout.simple_spinner_dropdown_item, "simple_spinner_dropdown_item",android.R.style.TextAppearance_Large)); + json.put("simple_dropdown_item_1line",extractItemStyle(android.R.layout.simple_dropdown_item_1line, "simple_dropdown_item_1line",android.R.style.TextAppearance_Large)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public ExtractStyle(Context context, String extractPath) + { + m_extractPath = extractPath; + new File(m_extractPath).mkdirs(); + MinistroActivity.nativeChmode(m_extractPath, 0755); + m_context = context; + m_theme = context.getTheme(); + JSONObject json = new JSONObject(); + try { + json.put("buttonStyle", extractTextAppearanceInformations("buttonStyle", "QPushButton", null, -1)); + json.put("spinnerStyle", extractTextAppearanceInformations("spinnerStyle", "QComboBox", null, -1)); + extractProgressBar(json, "progressBarStyleHorizontal", "QProgressBar"); + extractAbsSeekBar(json, "seekBarStyle", "QSlider"); + extractCompoundButton(json, "checkboxStyle", "QCheckBox"); + json.put("editTextStyle", extractTextAppearanceInformations("editTextStyle", "QLineEdit", null, -1)); + extractCompoundButton(json, "radioButtonStyle", "QRadioButton"); + json.put("textViewStyle", extractTextAppearanceInformations("textViewStyle", "QWidget", null, -1)); + extractItemsStyle(json); + //extractCompoundButton(json, "buttonStyleToggle", null); + //extractCompoundButton(json, "switchStyle", null); + //json.put("imageButtonStyle", extractImageViewInformations("imageButtonStyle", null)); + OutputStreamWriter jsonWriter; + jsonWriter = new OutputStreamWriter(new FileOutputStream(m_extractPath+"style.json")); + jsonWriter.write(json.toString(1)); + jsonWriter.close(); + MinistroActivity.nativeChmode(m_extractPath+"style.json", 0644); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/Ministro/src/org/kde/necessitas/ministro/IMinistro.aidl b/Ministro/src/org/kde/necessitas/ministro/IMinistro.aidl new file mode 100644 index 0000000..c1101da --- /dev/null +++ b/Ministro/src/org/kde/necessitas/ministro/IMinistro.aidl @@ -0,0 +1,47 @@ +/* + Copyright (c) 2011, BogDan Vatra <bog_dan_ro@yahoo.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +package org.kde.necessitas.ministro; + +import org.kde.necessitas.ministro.IMinistroCallback; + +interface IMinistro +{ +/** +* Check/download required libs to run the application +* +* param callback - interface used by Minsitro service to notify the client when the loader is ready +* param parameters +* parameters fields: +* * Key Name Key type Explanations +* "required.modules" StringArray Required modules by your application +* "application.title" String Application name, used to show more informations to user +* "qt.provider" String Qt libs provider, currently only "necessitas" is supported. +* "minimum.ministro.api" Integer Minimum Ministro API level, used to check if Ministro service compatible with your application. Current API Level is 1 ! +* "minimum.qt.version" Integer Minimim Qt version (e.g. 0x040800, which means Qt 4.8.0, check http://doc.trolltech.com/4.8/qtglobal.html#QT_VERSION)! +*/ + void requestLoader(in IMinistroCallback callback, in Bundle parameters); +} diff --git a/Ministro/src/org/kde/necessitas/ministro/IMinistroCallback.aidl b/Ministro/src/org/kde/necessitas/ministro/IMinistroCallback.aidl new file mode 100644 index 0000000..dfd3ea9 --- /dev/null +++ b/Ministro/src/org/kde/necessitas/ministro/IMinistroCallback.aidl @@ -0,0 +1,52 @@ +/* + Copyright (c) 2011, BogDan Vatra <bog_dan_ro@yahoo.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.kde.necessitas.ministro; + +oneway interface IMinistroCallback { +/** +* This method is called by the Ministro service back into the application which +* implements this interface. +* +* param in - loaderParams +* loaderParams fields: +* * Key Name Key type Explanations +* * "error.code" Integer See below +* * "error.message" String Missing if no error, otherwise will contain the error message translated into phone language where available. +* * "dex.path" String The list of jar/apk files containing classes and resources, needed to be passed to application DexClassLoader +* * "lib.path" String The list of directories containing native libraries; may be missing, needed to be passed to application DexClassLoader +* * "loader.class.name" String Loader class name. +* +* "error.code" field possible errors: +* - 0 no error. +* - 1 incompatible Ministro version. Ministro needs to be upgraded. +* - 2 not all modules could be satisfy. +* - 3 invalid parameters +* +* This parameter will contain additional fields which are used by the loader to start your application, so it must be passed to loader. +*/ + + void loaderReady(in Bundle loaderParams); +} diff --git a/Ministro/src/org/kde/necessitas/ministro/Library.java b/Ministro/src/org/kde/necessitas/ministro/Library.java new file mode 100644 index 0000000..7e1a9fe --- /dev/null +++ b/Ministro/src/org/kde/necessitas/ministro/Library.java @@ -0,0 +1,259 @@ +/* + Copyright (c) 2011, BogDan Vatra <bog_dan_ro@yahoo.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package org.kde.necessitas.ministro; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import android.os.Bundle; + + +class Library +{ + public String name = null; + public String filePath = null; + public String[] depends = null; + public String[] replaces = null; + public NeedsStruct[] needs = null; + public int level=0; + public long size = 0; + public String sha1 = null; + public String url; + + public static String[] getLibNames(Element libNode) + { + if (libNode == null) + return null; + NodeList list=libNode.getElementsByTagName("lib"); + ArrayList<String> libs = new ArrayList<String>(); + for (int i=0;i<list.getLength();i++) + { + if (list.item(i).getNodeType() != Node.ELEMENT_NODE) + continue; + Element lib=(Element)list.item(i); + if (lib!=null) + libs.add(lib.getAttribute("name")); + } + String[] strings = new String[libs.size()]; + return libs.toArray(strings); + } + + public static NeedsStruct[] getNeeds(Element libNode) + { + if (libNode == null) + return null; + NodeList list=libNode.getElementsByTagName("item"); + ArrayList<NeedsStruct> needs = new ArrayList<NeedsStruct>(); + + for (int i=0;i<list.getLength();i++) + { + if (list.item(i).getNodeType() != Node.ELEMENT_NODE) + continue; + Element lib=(Element)list.item(i); + if (lib!=null) + { + NeedsStruct need=new NeedsStruct(); + need.name=lib.getAttribute("name"); + need.filePath=lib.getAttribute("file"); + need.url=lib.getAttribute("url"); + need.sha1=lib.getAttribute("sha1"); + need.size=Long.valueOf(lib.getAttribute("size")); + if ( lib.hasAttribute("type") ) + need.type=lib.getAttribute("type"); + needs.add(need); + } + } + NeedsStruct[] _needs = new NeedsStruct[needs.size()]; + return needs.toArray(_needs); + } + + public static Library getLibrary(Element libNode, boolean includeNeed) + { + Library lib= new Library(); + lib.name=libNode.getAttribute("name"); + lib.sha1=libNode.getAttribute("sha1").toUpperCase(); + lib.filePath=libNode.getAttribute("file"); + lib.url=libNode.getAttribute("url"); + try + { + lib.level=Integer.parseInt(libNode.getAttribute("level")); + } catch (Exception e) { + e.printStackTrace(); + } + + try + { + lib.size=Long.parseLong(libNode.getAttribute("size")); + } catch (Exception e) { + e.printStackTrace(); + } + NodeList list=libNode.getElementsByTagName("depends"); + for (int i=0;i<list.getLength();i++) + { + if (list.item(i).getNodeType() != Node.ELEMENT_NODE) + continue; + lib.depends=getLibNames((Element) list.item(i)); + break; + } + list=libNode.getElementsByTagName("replaces"); + for (int i=0;i<list.getLength();i++) + { + if (list.item(i).getNodeType() != Node.ELEMENT_NODE) + continue; + lib.replaces=getLibNames((Element) list.item(i)); + break; + } + + if (!includeNeed) // don't waste time. + return lib; + + list=libNode.getElementsByTagName("needs"); + for (int i=0;i<list.getLength();i++) + { + if (list.item(i).getNodeType() != Node.ELEMENT_NODE) + continue; + lib.needs=getNeeds((Element) list.item(i)); + break; + } + return lib; + } + + public static String convertToHex(byte[] data) + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < data.length; i++) + { + int halfbyte = (data[i] >>> 4) & 0x0F; + int two_halfs = 0; + do + { + if ((0 <= halfbyte) && (halfbyte <= 9)) + buf.append((char) ('0' + halfbyte)); + else + buf.append((char) ('a' + (halfbyte - 10))); + halfbyte = data[i] & 0x0F; + } while(two_halfs++ < 1); + } + return buf.toString(); + } + + public static boolean checkCRC(String fileName, String sha1) throws IOException + { + try + { + byte[] tmp = new byte[2048]; + MessageDigest digester = MessageDigest.getInstance("SHA-1"); + int downloaded; + FileInputStream inFile = new FileInputStream(new File(fileName)); + while ((downloaded = inFile.read(tmp)) != -1) + { + digester.update(tmp, 0, downloaded); + } + inFile.close(); + return sha1.equalsIgnoreCase(convertToHex(digester.digest())); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return false; + } + + public static String mkdirParents(String rootPath, String filePath, int skip) + { + String[] paths=filePath.split("/"); + String path = ""; + for (int pit=0;pit<paths.length-skip;pit++) + { + if (paths[pit].length()==0) + continue; + path+="/"+paths[pit]; + File dir=new File(rootPath+path); + dir.mkdir(); + MinistroActivity.nativeChmode(rootPath+path, 0755); + } + return rootPath+path; + } + + public static void removeAllFiles(String path) + { + File f = new File(path); + if (!f.exists()) + return; + String files[]=f.list(); + if (!path.endsWith("/")) + path+="/"; + for (int i=0;i<files.length;i++) + { + try + { + new File(path+files[i]).delete(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + } + + public static String join(Collection<String> s, String delimiter) + { + if (s == null || s.isEmpty()) return ""; + Iterator<String> iter = s.iterator(); + StringBuilder builder = new StringBuilder(iter.next()); + while( iter.hasNext() ) + { + builder.append(delimiter).append(iter.next()); + } + return builder.toString(); + } + + public static void mergeBundleParameters(Bundle out, String outKey, Bundle in, String inKey) + { + if (!in.containsKey(inKey)) + return; + + String value = null; + if (out.containsKey(outKey)) + value=out.getString(outKey); + + if (value!=null && value.length()>0 && value.charAt(value.length()-1)!='\t') + value=value+"\t"; + + value=value+in.getString(inKey); + out.putString(outKey, value); + } +}; + +class NeedsStruct +{ + public String name = null; + public String filePath = null; + public String sha1 = null; + public String url = null; + public String type = null; + public long size = 0; +};
\ No newline at end of file diff --git a/Ministro/src/org/kde/necessitas/ministro/MinistroActivity.java b/Ministro/src/org/kde/necessitas/ministro/MinistroActivity.java new file mode 100644 index 0000000..c9d6bd7 --- /dev/null +++ b/Ministro/src/org/kde/necessitas/ministro/MinistroActivity.java @@ -0,0 +1,860 @@ +/* + Copyright (c) 2011, BogDan Vatra <bog_dan_ro@yahoo.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package org.kde.necessitas.ministro; + +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.concurrent.Semaphore; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.http.client.ClientProtocolException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.NotificationManager; +import android.app.ProgressDialog; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.os.StatFs; +import android.provider.Settings; +import android.util.Log; + +public class MinistroActivity extends Activity +{ + private static final int CONNECTION_TIMEOUT = 20000; // 20 seconds for connection timeout + private static final int READ_TIMEOUT = 10000; // 10 seconds for read timeout + + public native static int nativeChmode(String filepath, int mode); + private static final String DOMAIN_NAME="https://files.kde.org/necessitas/ministro/android/necessitas/"; + + private String[] m_modules; + private int m_id=-1; + private String m_qtLibsRootPath; + private WakeLock m_wakeLock; + + private void checkNetworkAndDownload(final boolean update) + { + if (isOnline(this)) + new CheckLibraries().execute(update); + else + { + AlertDialog.Builder builder = new AlertDialog.Builder(MinistroActivity.this); + builder.setMessage(getResources().getString(R.string.ministro_network_access_msg)); + builder.setCancelable(true); + builder.setNeutralButton(getResources().getString(R.string.settings_msg), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + final ProgressDialog m_dialog = ProgressDialog.show(MinistroActivity.this, null, getResources().getString(R.string.wait_for_network_connection_msg), true, true, new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) + { + finishMe(); + } + }); + getApplication().registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) + { + if (isOnline(MinistroActivity.this)) + { + try + { + getApplication().unregisterReceiver(this); + } + catch(Exception e) + { + e.printStackTrace(); + } + runOnUiThread(new Runnable() { + public void run() + { + m_dialog.dismiss(); + new CheckLibraries().execute(update); + } + }); + } + } + }, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); + try { + startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS)); + } catch(Exception e) { + e.printStackTrace(); + try { + startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)); + } catch(Exception e1) { + + e1.printStackTrace(); + } + } + dialog.dismiss(); + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) + { + dialog.cancel(); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) + { + finishMe(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + } + } + private AlertDialog m_distSpaceDialog=null; + private final int freeSpaceCode=0xf3ee500; + private Semaphore m_diskSpaceSemaphore = new Semaphore(0); + + private boolean checkFreeSpace(final long size) throws InterruptedException + { + final StatFs stat = new StatFs(m_qtLibsRootPath); + if (stat.getBlockSize() * stat.getAvailableBlocks() < size) + { + runOnUiThread(new Runnable() { + public void run() { + + AlertDialog.Builder builder = new AlertDialog.Builder(MinistroActivity.this); + builder.setMessage(getResources().getString(R.string.ministro_disk_space_msg, + (size-(stat.getBlockSize() * stat.getAvailableBlocks()))/1024+"Kb")); + builder.setCancelable(true); + builder.setNeutralButton(getResources().getString(R.string.settings_msg), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + try { + startActivityForResult(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS), freeSpaceCode); + } catch(Exception e) { + e.printStackTrace(); + try { + startActivityForResult(new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS), freeSpaceCode); + } catch(Exception e1) { + + e1.printStackTrace(); + } + } + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) + { + dialog.dismiss(); + m_diskSpaceSemaphore.release(); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) + { + dialog.dismiss(); + m_diskSpaceSemaphore.release(); + } + }); + m_distSpaceDialog = builder.create(); + m_distSpaceDialog.show(); + } + }); + m_diskSpaceSemaphore.acquire(); + } + else + return true; + + return stat.getBlockSize() * stat.getAvailableBlocks()>size; + } + + protected void onActivityResult (int requestCode, int resultCode, Intent data) + { + if (requestCode == freeSpaceCode) + { + m_diskSpaceSemaphore.release(); + try + { + if (m_distSpaceDialog != null) + { + m_distSpaceDialog.dismiss(); + m_distSpaceDialog = null; + } + } + catch(Exception e) + { + e.printStackTrace(); + } + } + } + private ServiceConnection m_ministroConnection=new ServiceConnection() + { + public void onServiceConnected(ComponentName name, IBinder service) + { + if (getIntent().hasExtra("id")) + m_id=getIntent().getExtras().getInt("id"); + + if (getIntent().hasExtra("modules")) + { + m_modules=getIntent().getExtras().getStringArray("modules"); + AlertDialog.Builder builder = new AlertDialog.Builder(MinistroActivity.this); + builder.setMessage(getResources().getString(R.string.download_app_libs_msg, + getIntent().getExtras().getString("name"))) + .setCancelable(false) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + checkNetworkAndDownload(false); + } + }) + .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + finishMe(); + } + }); + AlertDialog alert = builder.create(); + try + { + alert.show(); + } + catch (Exception e) + { + e.printStackTrace(); + checkNetworkAndDownload(false); + } + } + else + checkNetworkAndDownload(true); + } + + public void onServiceDisconnected(ComponentName name) + { + m_ministroConnection = null; + } + }; + + void finishMe() + { + if (-1 != m_id && null != MinistroService.instance()) + MinistroService.instance().retrievalFinished(m_id); + NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + nm.cancelAll(); + finish(); + } + + private static URL getVersionUrl(Context c) throws MalformedURLException + { + return new URL(DOMAIN_NAME+MinistroService.getRepository(c)+"/"+android.os.Build.CPU_ABI+"/android-"+android.os.Build.VERSION.SDK_INT+"/versions.xml"); + } + + private static URL getLibsXmlUrl(Context c, String version) throws MalformedURLException + { + return new URL(DOMAIN_NAME+MinistroService.getRepository(c)+"/"+android.os.Build.CPU_ABI+"/android-"+android.os.Build.VERSION.SDK_INT+"/libs-"+version+".xml"); + } + + public static boolean isOnline(Context c) + { + ConnectivityManager cm = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo netInfo = cm.getActiveNetworkInfo(); + if (netInfo != null && netInfo.isConnectedOrConnecting()) + return true; + return false; + } + + private static String deviceSupportedFeatures(String supportedFeatures) + { + if (null==supportedFeatures) + return ""; + String [] serverFeaturesList=supportedFeatures.trim().split(" "); + String [] deviceFeaturesList=null; + try { + FileInputStream fstream = new FileInputStream("/proc/cpuinfo"); + DataInputStream in = new DataInputStream(fstream); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String strLine; + while ((strLine = br.readLine()) != null) + { + if (strLine.startsWith("Features")) + { + deviceFeaturesList=strLine.substring(strLine.indexOf(":")+1).trim().split(" "); + break; + } + } + br.close(); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + + String features=""; + for(String sfeature: serverFeaturesList) + for (String dfeature: deviceFeaturesList) + if (sfeature.equals(dfeature)) + features+="_"+dfeature; + + return features; + } + + + public static double downloadVersionXmlFile(Context c, boolean checkOnly) + { + if (!isOnline(c)) + return-1; + try + { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document dom = null; + Element root = null; + URLConnection connection = getVersionUrl(c).openConnection(); + connection.setConnectTimeout(CONNECTION_TIMEOUT); + connection.setReadTimeout(READ_TIMEOUT); + dom = builder.parse(connection.getInputStream()); + root = dom.getDocumentElement(); + root.normalize(); + double version = Double.valueOf(root.getAttribute("latest")); + if ( MinistroService.instance().getVersion() >= version ) + return MinistroService.instance().getVersion(); + + if (checkOnly) + return version; + String supportedFeatures=null; + if (root.hasAttribute("features")) + supportedFeatures=root.getAttribute("features"); + connection = getLibsXmlUrl(c, version+deviceSupportedFeatures(supportedFeatures)).openConnection(); + File file= new File(MinistroService.instance().getVersionXmlFile()); + file.delete(); + FileOutputStream outstream = new FileOutputStream(MinistroService.instance().getVersionXmlFile()); + InputStream instream = connection.getInputStream(); + byte[] tmp = new byte[2048]; + int downloaded; + while ((downloaded = instream.read(tmp)) != -1) + outstream.write(tmp, 0, downloaded); + + outstream.close(); + MinistroService.instance().refreshLibraries(false); + return version; + } catch (ClientProtocolException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } catch (IllegalStateException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + return -1; + } + + private class DownloadManager extends AsyncTask<Library, Integer, Long> + { + private ProgressDialog m_dialog = null; + private String m_status = getResources().getString(R.string.start_downloading_msg); + private int m_totalSize=0, m_totalProgressSize=0; + + @Override + protected void onPreExecute() + { + m_dialog = new ProgressDialog(MinistroActivity.this); + m_dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + m_dialog.setTitle(getResources().getString(R.string.downloading_qt_libraries_msg)); + m_dialog.setMessage(m_status); + m_dialog.setCancelable(true); + m_dialog.setCanceledOnTouchOutside(false); + m_dialog.setOnCancelListener(new DialogInterface.OnCancelListener(){ + public void onCancel(DialogInterface dialog) + { + DownloadManager.this.cancel(false); + finishMe(); + } + }); + try + { + m_dialog.show(); + } + catch(Exception e) + { + e.printStackTrace(); + m_dialog = null; + } + super.onPreExecute(); + } + + private boolean DownloadItem(String url, String file, long size, String fileSha1) throws NoSuchAlgorithmException, MalformedURLException, IOException + { + for (int i=0;i<2;i++) + { + MessageDigest digester = MessageDigest.getInstance("SHA-1"); + URLConnection connection = new URL(url).openConnection(); + Library.mkdirParents(m_qtLibsRootPath, file, 1); + String filePath=m_qtLibsRootPath+file; + int progressSize=0; + try + { + FileOutputStream outstream = new FileOutputStream(filePath); + InputStream instream = connection.getInputStream(); + int downloaded; + byte[] tmp = new byte[2048]; + int oldProgress=-1; + while ((downloaded = instream.read(tmp)) != -1) + { + if (isCancelled()) + break; + progressSize+=downloaded; + m_totalProgressSize+=downloaded; + digester.update(tmp, 0, downloaded); + outstream.write(tmp, 0, downloaded); + int progress=(int)(progressSize*100/size); + if (progress!=oldProgress) + { + publishProgress(progress + , m_totalProgressSize); + oldProgress = progress; + } + } + String sha1 = Library.convertToHex(digester.digest()); + if (sha1.equalsIgnoreCase(fileSha1)) + { + outstream.close(); + nativeChmode(filePath, 0644); + return true; + } + else + Log.e("Ministro", "sha1 mismatch, the file:"+file+" will be removed, expected sha1:"+fileSha1+" got sha1:"+sha1+" file was downloaded from "+url); + outstream.close(); + File f = new File(filePath); + f.delete(); + } catch (Exception e) { + e.printStackTrace(); + File f = new File(filePath); + f.delete(); + } + m_totalProgressSize-=progressSize; + } + return false; + } + + void removeFile(String file) + { + File f = new File(m_qtLibsRootPath+file); + f.delete(); + } + + @Override + protected Long doInBackground(Library... params) + { + try + { + for (int i=0;i<params.length;i++) + { + m_totalSize+=params[i].size; + if (null != params[i].needs) + for (int j=0;j<params[i].needs.length;j++) + m_totalSize+=params[i].needs[j].size; + } + m_dialog.setMax(m_totalSize); + if (!checkFreeSpace(m_totalSize)) + return null; + for (int i=0;i<params.length;i++) + { + if (isCancelled()) + break; + synchronized (m_status) + { + m_status=params[i].name+" "; + } + publishProgress(0, m_totalProgressSize); + if (!DownloadItem(params[i].url, params[i].filePath, params[i].size, params[i].sha1)) + break; + + if (null != params[i].needs) + for (int j=0;j<params[i].needs.length;j++) + { + synchronized (m_status) + { + m_status=params[i].needs[j].name+" "; + } + publishProgress(0, m_totalProgressSize); + if (!DownloadItem(params[i].needs[j].url, params[i].needs[j].filePath, params[i].needs[j].size, params[i].needs[j].sha1)) + { + for (int k=0;k<j;k++) // remove previous neede files + removeFile(params[i].needs[k].filePath); + removeFile(params[i].filePath); // remove the parent + break; + } + } + } + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override + protected void onProgressUpdate(Integer... values) + { + try + { + if (m_dialog != null) + { + synchronized (m_status) + { + m_dialog.setMessage(m_status+values[0]+"%"); + m_dialog.setProgress(values[1]); + } + } + } + catch(Exception e) + { + e.printStackTrace(); + } + super.onProgressUpdate(values); + } + + @Override + protected void onPostExecute(Long result) + { + super.onPostExecute(result); + if (m_dialog != null) + { + m_dialog.dismiss(); + m_dialog = null; + } + MinistroService.instance().refreshLibraries(false); + finishMe(); + } + } + + private class CheckLibraries extends AsyncTask<Boolean, String, Double> + { + private ProgressDialog m_dialog = null; + private final ArrayList<Library> newLibs = new ArrayList<Library>(); + private String m_message; + @Override + protected void onPreExecute() + { + try + { + m_dialog = ProgressDialog.show(MinistroActivity.this, null, + getResources().getString(R.string.checking_libraries_msg), true, true); + } + catch (Exception e) + { + e.printStackTrace(); + m_dialog = null; + } + super.onPreExecute(); + } + + @Override + protected Double doInBackground(Boolean... update) + { + double version=0.0; + try + { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document dom = null; + Element root = null; + double oldVersion=MinistroService.instance().getVersion(); + if (update[0] || MinistroService.instance().getVersion()<0) + version = downloadVersionXmlFile(MinistroActivity.this, false); + else + version = MinistroService.instance().getVersion(); + + SharedPreferences preferences=getSharedPreferences("Ministro", MODE_PRIVATE); + // extract device look&feel + if (!preferences.getString("CODENAME", "").equals(android.os.Build.VERSION.CODENAME) || + !preferences.getString("INCREMENTAL", "").equals(android.os.Build.VERSION.INCREMENTAL) || + !preferences.getString("RELEASE", "").equals(android.os.Build.VERSION.RELEASE) || + !preferences.getString("MINISTRO_VERSION", "").equals(getPackageManager().getPackageInfo(getPackageName(), 0).versionName) || + !(new File(m_qtLibsRootPath+"style").exists())) + { + m_message = getResources().getString(R.string.extracting_look_n_feel_msg); + publishProgress(m_message); + new ExtractStyle(MinistroActivity.this, m_qtLibsRootPath+"style/"); + SharedPreferences.Editor editor= preferences.edit(); + editor.putString("MINISTRO_VERSION",getPackageManager().getPackageInfo(getPackageName(), 0).versionName); + editor.commit(); + } + + ArrayList<Library> libraries; + if (update[0]) + { + if (oldVersion!=version) + libraries = MinistroService.instance().getDownloadedLibraries(); + else + return version; + } + else + libraries = MinistroService.instance().getAvailableLibraries(); + + ArrayList<String> notFoundModules = new ArrayList<String>(); + if (m_modules!=null) + MinistroService.instance().checkModules(m_modules, notFoundModules); + + dom = builder.parse(new FileInputStream(MinistroService.instance().getVersionXmlFile())); + + factory = DocumentBuilderFactory.newInstance(); + builder = factory.newDocumentBuilder(); + root = dom.getDocumentElement(); + root.normalize(); + + // extract device root certificates + if (!preferences.getString("CODENAME", "").equals(android.os.Build.VERSION.CODENAME) || + !preferences.getString("INCREMENTAL", "").equals(android.os.Build.VERSION.INCREMENTAL) || + !preferences.getString("RELEASE", "").equals(android.os.Build.VERSION.RELEASE)) + { + m_message = getResources().getString(R.string.extracting_SSL_msg); + publishProgress(m_message); + String environmentVariables=root.getAttribute("environmentVariables"); + environmentVariables=environmentVariables.replaceAll("MINISTRO_PATH", ""); + String environmentVariablesList[]=environmentVariables.split("\t"); + for (int i=0;i<environmentVariablesList.length;i++) + { + String environmentVariable[]=environmentVariablesList[i].split("="); + if (environmentVariable[0].equals("MINISTRO_SSL_CERTS_PATH")) + { + String path=Library.mkdirParents(getFilesDir().getAbsolutePath(),environmentVariable[1], 0); + Library.removeAllFiles(path); + try + { + KeyStore ks = null; + if (Build.VERSION.SDK_INT>13) + { + ks = KeyStore.getInstance("AndroidCAStore"); + ks.load(null, null); + } + else + { + ks= KeyStore.getInstance(KeyStore.getDefaultType()); + String cacertsPath=System.getProperty("javax.net.ssl.trustStore"); + if (null == cacertsPath) + cacertsPath="/system/etc/security/cacerts.bks"; + FileInputStream instream = new FileInputStream(new File(cacertsPath)); + ks.load(instream, null); + } + + for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements(); ) + { + String aName = aliases.nextElement(); + try + { + X509Certificate cert=(X509Certificate) ks.getCertificate(aName); + if (null==cert) + continue; + String filePath=path+"/"+cert.getType()+"_"+cert.hashCode()+".der"; + FileOutputStream outstream = new FileOutputStream(new File(filePath)); + byte buff[]=cert.getEncoded(); + outstream.write(buff, 0, buff.length); + outstream.close(); + nativeChmode(filePath, 0644); + } catch(KeyStoreException e) { + e.printStackTrace(); + } catch(Exception e) { + e.printStackTrace(); + } + } + } catch (KeyStoreException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (CertificateException e) { + e.printStackTrace(); + } + SharedPreferences.Editor editor= preferences.edit(); + editor.putString("CODENAME",android.os.Build.VERSION.CODENAME); + editor.putString("INCREMENTAL", android.os.Build.VERSION.INCREMENTAL); + editor.putString("RELEASE", android.os.Build.VERSION.RELEASE); + editor.commit(); + break; + } + } + } + + Node node = root.getFirstChild(); + while(node != null) + { + if (node.getNodeType() == Node.ELEMENT_NODE) + { + Library lib= Library.getLibrary((Element)node, true); + if (update[0]) + { // check for updates + for (int j=0;j<libraries.size();j++) + if (libraries.get(j).name.equals(lib.name)) + { + newLibs.add(lib); + break; + } + } + else + {// download missing libraries + for(String module : notFoundModules) + if (module.equals(lib.name)) + { + newLibs.add(lib); + break; + } + } + } + + // Workaround for an unbelievable bug !!! + try { + node = node.getNextSibling(); + } catch (Exception e) { + e.printStackTrace(); + break; + } + } + return version; + } catch (ClientProtocolException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } catch (IllegalStateException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + return -1.; + } + + @Override + protected void onProgressUpdate(String... messages) + { + try + { + if (null != m_dialog) + m_dialog.setMessage(messages[0]); + } + catch (Exception e) + { + e.printStackTrace(); + } + super.onProgressUpdate(messages); + } + + @Override + protected void onPostExecute(Double result) + { + try + { + if (null != m_dialog) + { + m_dialog.dismiss(); + m_dialog = null; + } + } + catch(Exception e) + { + e.printStackTrace(); + } + if (newLibs.size()>0 && result>0) + { + Library[] libs = new Library[newLibs.size()]; + libs = newLibs.toArray(libs); + new DownloadManager().execute(libs); + } + else + finishMe(); + super.onPostExecute(result); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + m_qtLibsRootPath = getFilesDir().getAbsolutePath()+"/qt/"; + File dir=new File(m_qtLibsRootPath); + dir.mkdirs(); + nativeChmode(m_qtLibsRootPath, 0755); + bindService(new Intent("org.kde.necessitas.ministro.IMinistro"), m_ministroConnection, Context.BIND_AUTO_CREATE); + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + m_wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "Ministro"); + m_wakeLock.acquire(); + } + + @Override + protected void onDestroy() + { + super.onDestroy(); + if (null != m_wakeLock) + { + m_wakeLock.release(); + m_wakeLock = null; + } + unbindService(m_ministroConnection); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) + { + //Avoid activity from being destroyed/created + super.onConfigurationChanged(newConfig); + } + + static + { + System.loadLibrary("ministro"); + } +} diff --git a/Ministro/src/org/kde/necessitas/ministro/MinistroConfigActivity.java b/Ministro/src/org/kde/necessitas/ministro/MinistroConfigActivity.java new file mode 100644 index 0000000..91b5d06 --- /dev/null +++ b/Ministro/src/org/kde/necessitas/ministro/MinistroConfigActivity.java @@ -0,0 +1,102 @@ +/* + Copyright (c) 2011, BogDan Vatra <bog_dan_ro@yahoo.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package org.kde.necessitas.ministro; + +import android.app.Activity; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.Toast; + +public class MinistroConfigActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + setContentView(R.layout.repoconfig); + Spinner repositoriesSpinner = (Spinner) findViewById(R.id.repositories); + ArrayAdapter<CharSequence> repositories = ArrayAdapter.createFromResource( + this, R.array.repositories, android.R.layout.simple_spinner_item); + repositories.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + repositoriesSpinner.setAdapter(repositories); + repositoriesSpinner.setSelection(repositories.getPosition(MinistroService.getRepository(this))); + repositoriesSpinner.setOnItemSelectedListener(new OnItemSelectedListener(){ + public void onItemSelected(AdapterView<?> parent, View view, int pos, + long id) { + Toast.makeText(parent.getContext() + , getResources().getString(R.string.ministro_repository_msg + , parent.getItemAtPosition(pos).toString()), Toast.LENGTH_SHORT).show(); + MinistroService.setRepository(MinistroConfigActivity.this, parent.getItemAtPosition(pos).toString()); + } + public void onNothingSelected(AdapterView<?> arg0) { + } + }); + + Spinner checkFrequencySpinner = (Spinner) findViewById(R.id.check_frequency); + ArrayAdapter<CharSequence> checkFrequency = ArrayAdapter.createFromResource( + this, R.array.check_frequency, android.R.layout.simple_spinner_item); + checkFrequency.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + checkFrequencySpinner.setAdapter(checkFrequency); + checkFrequencySpinner.setSelection(checkFrequency.getPosition(MinistroService.getCheckFrequency(this).toString())); + checkFrequencySpinner.setOnItemSelectedListener(new OnItemSelectedListener(){ + public void onItemSelected(AdapterView<?> parent, View view, int pos, + long id) { + MinistroService.setCheckFrequency(MinistroConfigActivity.this, Long.parseLong(parent.getItemAtPosition(pos).toString())); + } + + public void onNothingSelected(AdapterView<?> arg0) { + } + }); + super.onCreate(savedInstanceState); + } + + @Override + protected void onDestroy() { + NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + int icon = R.drawable.icon; + CharSequence tickerText = getResources().getString(R.string.ministro_repository_changed_msg); // ticker-text + long when = System.currentTimeMillis(); // notification time + Context context = getApplicationContext(); // application Context + CharSequence contentTitle = getResources().getString(R.string.ministro_update_msg); // expanded message title + CharSequence contentText = getResources().getString(R.string.ministro_repository_changed_tap_msg); // expanded message text + + Intent notificationIntent = new Intent(this, MinistroActivity.class); + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); + + // the next two lines initialize the Notification, using the configurations above + Notification notification = new Notification(icon, tickerText, when); + notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent); + notification.defaults |= Notification.DEFAULT_SOUND; + notification.defaults |= Notification.DEFAULT_VIBRATE; + notification.defaults |= Notification.DEFAULT_LIGHTS; + try{ + nm.notify(1, notification); + }catch(Exception e) + { + e.printStackTrace(); + } + super.onDestroy(); + } +} diff --git a/Ministro/src/org/kde/necessitas/ministro/MinistroService.java b/Ministro/src/org/kde/necessitas/ministro/MinistroService.java new file mode 100644 index 0000000..eb75801 --- /dev/null +++ b/Ministro/src/org/kde/necessitas/ministro/MinistroService.java @@ -0,0 +1,748 @@ +/* + Copyright (c) 2011, BogDan Vatra <bog_dan_ro@yahoo.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package org.kde.necessitas.ministro; + +import java.io.File; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +public class MinistroService extends Service +{ + private static final String TAG = "MinistroService"; + + private static final String MINISTRO_CHECK_UPDATES_KEY="LASTCHECK"; + private static final String MINISTRO_CHECK_FREQUENCY_KEY="CHECKFREQUENCY"; + private static final String MINISTRO_REPOSITORY_KEY="REPOSITORY"; + private static final String MINISTRO_DEFAULT_REPOSITORY="stable"; + + /// Ministro server parameter keys + private static final String REQUIRED_MODULES_KEY="required.modules"; + private static final String APPLICATION_TITLE_KEY="application.title"; + private static final String QT_PROVIDER_KEY="qt.provider"; + private static final String MINIMUM_MINISTRO_API_KEY="minimum.ministro.api"; + private static final String MINIMUM_QT_VERSION_KEY="minimum.qt.version"; + /// Ministro server parameter keys + + /// loader parameter keys + private static final String ERROR_CODE_KEY="error.code"; + private static final String ERROR_MESSAGE_KEY="error.message"; + private static final String DEX_PATH_KEY="dex.path"; + private static final String LIB_PATH_KEY="lib.path"; + private static final String LOADER_CLASS_NAME_KEY="loader.class.name"; + + private static final String NATIVE_LIBRARIES_KEY="native.libraries"; + private static final String ENVIRONMENT_VARIABLES_KEY="environment.variables"; + private static final String APPLICATION_PARAMETERS_KEY="application.parameters"; + private static final String QT_VERSION_PARAMETER_KEY="qt.version.parameter"; + /// loader parameter keys + + /// loader error codes + private static final int EC_NO_ERROR=0; + private static final int EC_INCOMPATIBLE=1; + private static final int EC_NOT_FOUND=2; + private static final int EC_INVALID_PARAMETERS=3; + private static final int EC_INVALID_QT_VERSION=3; + /// loader error codes + + + public static String getRepository(Context c) + { + SharedPreferences preferences=c.getSharedPreferences("Ministro", MODE_PRIVATE); + return preferences.getString(MINISTRO_REPOSITORY_KEY,MINISTRO_DEFAULT_REPOSITORY); + } + + public static void setRepository(Context c, String value) + { + SharedPreferences preferences=c.getSharedPreferences("Ministro", MODE_PRIVATE); + SharedPreferences.Editor editor= preferences.edit(); + editor.putString(MINISTRO_REPOSITORY_KEY,value); + editor.putLong(MINISTRO_CHECK_UPDATES_KEY,0); + editor.commit(); + } + + public static Long getCheckFrequency(Context c) + { + SharedPreferences preferences=c.getSharedPreferences("Ministro", MODE_PRIVATE); + return preferences.getLong(MINISTRO_CHECK_FREQUENCY_KEY, 7l*24*3600*1000)/(24l*3600*1000); + } + + public static void setCheckFrequency(Context c, long value) + { + SharedPreferences preferences=c.getSharedPreferences("Ministro", MODE_PRIVATE); + SharedPreferences.Editor editor= preferences.edit(); + editor.putLong(MINISTRO_CHECK_FREQUENCY_KEY, value*24*3600*1000); + editor.putLong(MINISTRO_CHECK_UPDATES_KEY,0); + editor.commit(); + } + + // used to check Ministro Service compatibility + private static final int MINISTRO_MIN_API_LEVEL=1; + private static final int MINISTRO_MAX_API_LEVEL=2; + + // MinistroService instance, its used by MinistroActivity to directly access services data (e.g. libraries) + private static MinistroService m_instance = null; + private String m_environmentVariables = null; + private String m_applicationParams = null; + private String m_loaderClassName = null; + private String m_pathSeparator = null; + public static MinistroService instance() + { + return m_instance; + } + + public MinistroService() + { + m_instance = this; + } + + private int m_actionId=0; // last actions id + private Handler m_handler = null; + + // current downloaded libraries + private final ArrayList<Library> m_downloadedLibraries = new ArrayList<Library>(); + + ArrayList<Library> getDownloadedLibraries() + { + synchronized (this) + { + return m_downloadedLibraries; + } + } + + // current available libraries + private final ArrayList<Library> m_availableLibraries = new ArrayList<Library>(); + ArrayList<Library> getAvailableLibraries() + { + synchronized (this) + { + return m_availableLibraries; + } + } + + class CheckForUpdates extends AsyncTask<Void, Void, Void> + { + @Override + protected void onPreExecute() + { + if (m_version<MinistroActivity.downloadVersionXmlFile(MinistroService.this, true)) + { + NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + int icon = R.drawable.icon; + CharSequence tickerText = getResources().getString(R.string.new_qt_libs_msg); // ticker-text + long when = System.currentTimeMillis(); // notification time + Context context = getApplicationContext(); // application Context + CharSequence contentTitle = getResources().getString(R.string.ministro_update_msg); // expanded message title + CharSequence contentText = getResources().getString(R.string.new_qt_libs_tap_msg); // expanded message text + + Intent notificationIntent = new Intent(MinistroService.this, MinistroActivity.class); + PendingIntent contentIntent = PendingIntent.getActivity(MinistroService.this, 0, notificationIntent, 0); + + // the next two lines initialize the Notification, using the configurations above + Notification notification = new Notification(icon, tickerText, when); + notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent); + notification.defaults |= Notification.DEFAULT_SOUND; + notification.defaults |= Notification.DEFAULT_LIGHTS; + try { + nm.notify(1, notification); + } catch(Exception e) { + e.printStackTrace(); + } + } + } + + @Override + protected Void doInBackground(Void... params) { + return null; + } + } + + + // this method reload all downloaded libraries + synchronized ArrayList<Library> refreshLibraries(boolean checkCrc) + { + synchronized (this) + { + try + { + m_downloadedLibraries.clear(); + m_availableLibraries.clear(); + if (! (new File(m_versionXmlFile)).exists()) + return m_downloadedLibraries; + DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder(); + Document dom = documentBuilder.parse(new FileInputStream(m_versionXmlFile)); + Element root = dom.getDocumentElement(); + m_version = Double.valueOf(root.getAttribute("version")); + m_loaderClassName=root.getAttribute("loaderClassName"); + m_applicationParams=root.getAttribute("applicationParameters"); + m_applicationParams=m_applicationParams.replaceAll("MINISTRO_PATH", getFilesDir().getAbsolutePath()); + m_environmentVariables=root.getAttribute("environmentVariables"); + m_environmentVariables=m_environmentVariables.replaceAll("MINISTRO_PATH", getFilesDir().getAbsolutePath()); + m_environmentVariables="MINISTRO_ANDROID_STYLE_PATH="+m_qtLibsRootPath+"style/\t"+m_environmentVariables; + if (root.hasAttribute("qtVersion")) + m_qtVersion = Integer.valueOf(root.getAttribute("qtVersion")); + root.normalize(); + Node node = root.getFirstChild(); + while(node != null) + { + if (node.getNodeType() == Node.ELEMENT_NODE) + { + Library lib= Library.getLibrary((Element)node, true); + File file=new File(m_qtLibsRootPath + lib.filePath); + if (file.exists()) + { + if (checkCrc && !Library.checkCRC(file.getAbsolutePath(), lib.sha1)) + file.delete(); + else + { + + boolean allOk = true; + if (lib.needs != null) + { + for(NeedsStruct needed: lib.needs)// check if its needed files are available + if (needed.type != null && needed.type.equals("jar")) + { + File f=new File(m_qtLibsRootPath + needed.filePath); + if (!f.exists()) + { + allOk = false; + break; + } + } + if (!allOk) + { + for(NeedsStruct needed: lib.needs)// remove all needed files + if (needed.type != null && needed.type.equals("jar")) + { + try { + File f=new File(m_qtLibsRootPath + needed.filePath); + if (f.exists()) + f.delete(); + } catch (Exception e) + { + e.printStackTrace(); + } + } + file.delete(); // delete the parent + } + } + if (allOk) + m_downloadedLibraries.add(lib); + } + } + m_availableLibraries.add(lib); + } + // Workaround for an unbelievable bug !!! + try { + node = node.getNextSibling(); + } catch (Exception e) { + e.printStackTrace(); + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return m_downloadedLibraries; + } + + // version xml file + private String m_versionXmlFile; + public String getVersionXmlFile() + { + return m_versionXmlFile; + } + + private String m_qtLibsRootPath; + public String getQtLibsRootPath() + { + return m_qtLibsRootPath; + } + + private double m_version = -1; + public double getVersion() + { + return m_version; + } + + private double m_qtVersion = 0x040800; + public double getQtVersion() + { + return m_qtVersion; + } + + // class used to fire an action, this class is used + // to start an activity when user needs more libraries to start its application + class ActionStruct + { + ActionStruct(IMinistroCallback cb, String[] m, ArrayList<String> notFoundMoules, String appName, Bundle p) + { + id=++m_actionId; + callback = cb; + modules = m; + parameters = p; + } + public int id; + public IMinistroCallback callback; + public String[] modules; + public Bundle parameters; + } + + // we can have more then one action + ArrayList<ActionStruct> m_actions = new ArrayList<ActionStruct>(); + + @Override + public void onCreate() + { + m_handler = new Handler(); + m_versionXmlFile = getFilesDir().getAbsolutePath()+"/version.xml"; + m_qtLibsRootPath = getFilesDir().getAbsolutePath()+"/qt/"; + m_pathSeparator = System.getProperty("path.separator", ":"); + SharedPreferences preferences=getSharedPreferences("Ministro", MODE_PRIVATE); + long lastCheck = preferences.getLong(MINISTRO_CHECK_UPDATES_KEY,0); + long checkFrequency = preferences.getLong(MINISTRO_CHECK_FREQUENCY_KEY,7l*24*3600*1000); // check once per week by default + if (MinistroActivity.isOnline(this) && System.currentTimeMillis()-lastCheck>checkFrequency) + { + refreshLibraries(true); + SharedPreferences.Editor editor= preferences.edit(); + editor.putLong(MINISTRO_CHECK_UPDATES_KEY,System.currentTimeMillis()); + editor.commit(); + new CheckForUpdates().execute((Void[])null); + } + else + refreshLibraries(false); + super.onCreate(); + } + + @Override + public void onDestroy() + { + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) + { + return new IMinistro.Stub() + { + public void requestLoader(IMinistroCallback callback, Bundle parameters) throws RemoteException + { + + checkModulesImpl(callback, parameters); + } + }; + } + + /** + * Implements the {@link IMinistro.Stub#checkModules(IMinistroCallback, String[], String, int, int)} + * service method. + * + * @param callback + * @param modules + * @param appName + * @param ministroApiLevel + * @param necessitasApiLevel + * @throws RemoteException + */ + final void checkModulesImpl(IMinistroCallback callback, Bundle parameters) throws RemoteException + { + if (!parameters.containsKey(REQUIRED_MODULES_KEY) + || !parameters.containsKey(APPLICATION_TITLE_KEY) + || !parameters.containsKey(MINIMUM_MINISTRO_API_KEY) + || !parameters.containsKey(MINIMUM_QT_VERSION_KEY)) + { + Bundle loaderParams = new Bundle(); + loaderParams.putInt(ERROR_CODE_KEY, EC_INVALID_PARAMETERS); + loaderParams.putString(ERROR_MESSAGE_KEY, getResources().getString(R.string.invalid_parameters)); + try + { + callback.loaderReady(loaderParams); + } + catch (Exception e) { + e.printStackTrace(); + } + Log.e(TAG, "Invalid parameters: " + parameters.toString()); + return; + } + int ministroApiLevel = parameters.getInt(MINIMUM_MINISTRO_API_KEY); + String[] modules = parameters.getStringArray(REQUIRED_MODULES_KEY); + String appName = parameters.getString(APPLICATION_TITLE_KEY); + + int qtApiLevel = parameters.getInt(MINIMUM_QT_VERSION_KEY); + if (qtApiLevel > m_qtVersion) // the application needs a newer qt version + { + if (parameters.getBoolean(QT_VERSION_PARAMETER_KEY, false)) + { + Bundle loaderParams = new Bundle(); + loaderParams.putInt(ERROR_CODE_KEY, EC_INVALID_QT_VERSION); + loaderParams.putString(ERROR_MESSAGE_KEY, getResources().getString(R.string.invalid_qt_version)); + try + { + callback.loaderReady(loaderParams); + } + catch (Exception e) { + e.printStackTrace(); + } + Log.e(TAG, "Invalid qt verson"); + return; + } + startRetrieval(callback, null, null, appName, parameters); + parameters.putBoolean(QT_VERSION_PARAMETER_KEY, true); + return; + } + + @SuppressWarnings("unused") + String qtProvider="necessitas"; + if (parameters.containsKey(QT_PROVIDER_KEY)) + qtProvider=parameters.getString(QT_PROVIDER_KEY); // TODO add the possibility to have more than one provider + + if (ministroApiLevel<MINISTRO_MIN_API_LEVEL || ministroApiLevel>MINISTRO_MAX_API_LEVEL) + { + // panic !!! Ministro service is not compatible, user should upgrade Ministro package + Bundle loaderParams = new Bundle(); + loaderParams.putInt(ERROR_CODE_KEY, EC_INCOMPATIBLE); + loaderParams.putString(ERROR_MESSAGE_KEY, getResources().getString(R.string.incompatible_ministo_api)); + try + { + callback.loaderReady(loaderParams); + } + catch (Exception e) { + e.printStackTrace(); + } + Log.e(TAG, "Ministro cannot satisfy API version: " + ministroApiLevel); + return; + } + + // check necessitasApiLevel !!! I'm pretty sure some people will completely ignore my warning + // and they will deploying apps to Android Market, so let's try to give them a chance. + + // this method is called by the activity client who needs modules. + ArrayList<String> notFoundModules = new ArrayList<String>(); + Bundle loaderParams = checkModules(modules, notFoundModules); + if (loaderParams.containsKey(ERROR_CODE_KEY) && EC_NO_ERROR == loaderParams.getInt(ERROR_CODE_KEY)) + { + try + { + Library.mergeBundleParameters(loaderParams, ENVIRONMENT_VARIABLES_KEY, parameters, ENVIRONMENT_VARIABLES_KEY); + Library.mergeBundleParameters(loaderParams, APPLICATION_PARAMETERS_KEY, parameters, APPLICATION_PARAMETERS_KEY); + callback.loaderReady(loaderParams); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + else + { + // Starts a retrieval of the modules which are not readily accessible. + startRetrieval(callback, modules, notFoundModules, appName, parameters); + } + } + + /** + * Creates and sets up a {@link MinistroActivity} to retrieve the modules specified in the + * <code>notFoundModules</code> argument. + * + * @param callback + * @param modules + * @param notFoundModules + * @param appName + * @throws RemoteException + */ + private void startRetrieval(IMinistroCallback callback, String[] modules + , ArrayList<String> notFoundModules, String appName, Bundle parameters) throws RemoteException + { + ActionStruct as = new ActionStruct(callback, modules, notFoundModules, appName, parameters); + m_actions.add(as); // if not, lets start an activity to do it. + + final Intent intent = new Intent(MinistroService.this, MinistroActivity.class); + intent.putExtra("id", as.id); + intent.putExtra("name", appName); + if (null != notFoundModules) + { + String[] libs = notFoundModules.toArray(new String[notFoundModules.size()]); + intent.putExtra("modules", libs); + } + + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + boolean failed = false; + try + { + m_handler.postDelayed(new Runnable() { + public void run() { + MinistroService.this.startActivity(intent); + } + }, 100); + } + catch(Exception e) + { + failed = true; + throw (RemoteException) new RemoteException().initCause(e); + } + finally + { + // Removes the dead Activity from our list as it will never finish by itself. + if (failed) + m_actions.remove(as); + } + } + + /** + * Called by a finished {@link MinistroActivity} in order to let + * the service notify the application which caused the activity about + * the result of the retrieval. + * + * @param id + */ + void retrievalFinished(int id) + { + for (int i=0;i<m_actions.size();i++) + { + ActionStruct action=m_actions.get(i); + if (action.id==id) + { + postRetrieval(action); + m_actions.remove(i); + break; + } + } + if (m_actions.size() == 0) + m_actionId = 0; + } + + /** + * Helper method for the last step of the retrieval process. + * + * <p>Checks the availability of the requested modules and informs + * the requesting application about it via the {@link IMinistroCallback} + * instance.</p> + * + * @param callback + * @param modules + */ + private void postRetrieval(ActionStruct action) + { + // Does a final check whether the libraries are accessible (without caring for + // the non-accessible ones). + try + { + if (null != action.modules) + { + Bundle loaderParams = checkModules(action.modules, null); + Library.mergeBundleParameters(loaderParams, ENVIRONMENT_VARIABLES_KEY, action.parameters, ENVIRONMENT_VARIABLES_KEY); + Library.mergeBundleParameters(loaderParams, APPLICATION_PARAMETERS_KEY, action.parameters, APPLICATION_PARAMETERS_KEY); + action.callback.loaderReady(loaderParams); + } + else + checkModulesImpl(action.callback, action.parameters); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * Checks whether a given list of libraries are readily accessible (e.g. usable by a program). + * + * <p>If the <code>notFoundModules</code> argument is given, the method fills the list with + * libraries that need to be retrieved first.</p> + * + * @param libs + * @param notFoundModules + * @return true if all modules are available + */ + Bundle checkModules(String[] modules, ArrayList<String> notFoundModules) + { + Bundle params = new Bundle(); + boolean res=true; + ArrayList<Module> libs= new ArrayList<Module>(); + Set<String> jars= new HashSet<String>(); + for (String module: modules) + res = res & addModules(module, libs, notFoundModules, jars); // don't stop on first error + + ArrayList<String> librariesArray = new ArrayList<String>(); + // sort all libraries + Collections.sort(libs, new ModuleCompare()); + for (Module lib: libs) + librariesArray.add(m_qtLibsRootPath+lib.path); + params.putStringArrayList(NATIVE_LIBRARIES_KEY, librariesArray); + + ArrayList<String> jarsArray = new ArrayList<String>(); + for (String jar: jars) + jarsArray.add(m_qtLibsRootPath+jar); + params.putString(DEX_PATH_KEY, Library.join(jarsArray, m_pathSeparator)); + + params.putString(LOADER_CLASS_NAME_KEY, m_loaderClassName); + params.putString(LIB_PATH_KEY, m_qtLibsRootPath); + params.putString(ENVIRONMENT_VARIABLES_KEY, m_environmentVariables); + params.putString(APPLICATION_PARAMETERS_KEY, m_applicationParams); + params.putInt(ERROR_CODE_KEY, res?EC_NO_ERROR:EC_NOT_FOUND); + if (!res) + params.putString(ERROR_MESSAGE_KEY, getResources().getString(R.string.dependencies_error)); + return params; + } + +/** + * Helper method for the module resolution mechanism. It deals with an individual module's + * resolution request. + * + * <p>The method checks whether a given <em>single</em> <code>module</code> is already + * accessible or needs to be retrieved first. In the latter case the method returns + * <code>false</code>.</p> + * + * <p>The method traverses a <code>module<code>'s dependencies automatically.</p> + * + * <p>In order to find out whether a <code>module</code> is accessible the method consults + * the list of downloaded libraries. If found, an entry to the <code>modules</code> list is + * added.</p> + * + * <p>In case the <code>module</code> is not immediately accessible and the <code>notFoundModules</code> + * argument exists, a list of available libraries is consulted to fill a list of modules which + * yet need to be retrieved.</p> + * + * @param module + * @param modules + * @param notFoundModules + * @param jars + * @return <code>true</code> if the given module and all its dependencies are readily available. + */ + private boolean addModules(String module, ArrayList<Module> modules + , ArrayList<String> notFoundModules, Set<String> jars) + { + // Module argument is not supposed to be null at this point. + if (modules == null) + return false; // we are in deep shit if this happens + + // Short-cut: If the module is already in our list of previously found modules then we do not + // need to consult the list of downloaded modules. + for (int i=0;i<modules.size();i++) + { + if (modules.get(i).name.equals(module)) + return true; + } + + // Consult the list of downloaded modules. If a matching entry is found, it is added to the + // list of readily accessible modules and its dependencies are checked via a recursive call. + for (Library library:m_downloadedLibraries) + { + if (library.name.equals(module)) + { + Module m = new Module(); + m.name=library.name; + m.path=library.filePath; + m.level=library.level; + if (library.needs != null) + for(NeedsStruct needed: library.needs) + if (needed.type != null && needed.type.equals("jar")) + jars.add(needed.filePath); + modules.add(m); + + boolean res = true; + if (library.depends != null) + for (String depend: library.depends) + res &= addModules(depend, modules, notFoundModules, jars); + + if (library.replaces != null) + for (String replaceLibrary: library.replaces) + for (int mIt=0; mIt<modules.size();mIt++) + if (replaceLibrary.equals(modules.get(mIt).name)) + modules.remove(mIt--); + + return res; + } + } + + // Requested module is not readily accessible. + if (notFoundModules != null) + { + // Checks list of modules which are known to not be readily accessible and returns early to + // prevent double entries. + for (int i=0;i<notFoundModules.size();i++) + { + if (notFoundModules.get(i).equals(module)) + return false; + } + + // Deal with not yet readily accessible module's dependencies. + notFoundModules.add(module); + for (int i = 0; i< m_availableLibraries.size(); i++) + { + if (m_availableLibraries.get(i).name.equals(module)) + { + if (m_availableLibraries.get(i).depends != null) + for (int depIt=0;depIt<m_availableLibraries.get(i).depends.length;depIt++) + addModules(m_availableLibraries.get(i).depends[depIt], modules, notFoundModules, jars); + break; + } + } + } + return false; + } + + /** Sorter for libraries. + * + * Hence the order in which the libraries have to be loaded is important, it is neccessary + * to sort them. + */ + static private class ModuleCompare implements Comparator<Module> + { + public int compare(Module a, Module b) + { + return a.level-b.level; + } + } + + /** Helper class which allows manipulating libraries. + * + * It is similar to the {@link Library} class but has fewer fields. + */ + static private class Module + { + String path; + String name; + int level; + } +} diff --git a/MinistroConfigurationTool/AndroidManifest.xml b/MinistroConfigurationTool/AndroidManifest.xml new file mode 100644 index 0000000..175529f --- /dev/null +++ b/MinistroConfigurationTool/AndroidManifest.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.kde.ministro.config" + android:versionCode="2" + android:versionName="2.0"> + <application android:icon="@drawable/icon" android:label="@string/app_name"> + <activity android:name=".MinistroConfigurationToolActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + <uses-sdk android:minSdkVersion="4" /> +</manifest> diff --git a/MinistroConfigurationTool/res/drawable-hdpi/icon.png b/MinistroConfigurationTool/res/drawable-hdpi/icon.png Binary files differnew file mode 100644 index 0000000..5b97acc --- /dev/null +++ b/MinistroConfigurationTool/res/drawable-hdpi/icon.png diff --git a/MinistroConfigurationTool/res/drawable-ldpi/icon.png b/MinistroConfigurationTool/res/drawable-ldpi/icon.png Binary files differnew file mode 100644 index 0000000..0011174 --- /dev/null +++ b/MinistroConfigurationTool/res/drawable-ldpi/icon.png diff --git a/MinistroConfigurationTool/res/drawable-mdpi/icon.png b/MinistroConfigurationTool/res/drawable-mdpi/icon.png Binary files differnew file mode 100644 index 0000000..0f3bf43 --- /dev/null +++ b/MinistroConfigurationTool/res/drawable-mdpi/icon.png diff --git a/MinistroConfigurationTool/res/values-ro/strings.xml b/MinistroConfigurationTool/res/values-ro/strings.xml new file mode 100644 index 0000000..7933125 --- /dev/null +++ b/MinistroConfigurationTool/res/values-ro/strings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Instrument de configurare Ministro</string> + <string name="install_ministro_msg">Acest instrument are nevoie de cel mai recent serviciu Ministro. Doriţi să-l instalaţi?</string> +</resources> diff --git a/MinistroConfigurationTool/res/values/strings.xml b/MinistroConfigurationTool/res/values/strings.xml new file mode 100644 index 0000000..67016f5 --- /dev/null +++ b/MinistroConfigurationTool/res/values/strings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Ministro configuration tool</string> + <string name="install_ministro_msg">This tool needs latest Ministro service. Would you like to install it?</string> +</resources> diff --git a/MinistroConfigurationTool/src/org/kde/ministro/config/MinistroConfigurationToolActivity.java b/MinistroConfigurationTool/src/org/kde/ministro/config/MinistroConfigurationToolActivity.java new file mode 100644 index 0000000..d62b828 --- /dev/null +++ b/MinistroConfigurationTool/src/org/kde/ministro/config/MinistroConfigurationToolActivity.java @@ -0,0 +1,67 @@ +/* + Copyright (c) 2011, BogDan Vatra <bog_dan_ro@yahoo.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package org.kde.ministro.config; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.ComponentName; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +public class MinistroConfigurationToolActivity extends Activity { + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + try + { + Intent intent = new Intent(); + intent.setComponent(new ComponentName("org.kde.necessitas.ministro", "org.kde.necessitas.ministro.MinistroConfigActivity")); + startActivity(intent); + finish(); + } + catch (Exception e) { + AlertDialog.Builder downloadDialog = new AlertDialog.Builder(this); + downloadDialog.setMessage("This tool needs latest Ministro service. Would you like to install it?"); + downloadDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialogInterface, int i) { + try + { + Uri uri = Uri.parse("market://search?q=pname:org.kde.necessitas.ministro"); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivity(intent); + finish(); + } + catch (Exception e) { + e.printStackTrace(); + finish(); + } + } + }); + + downloadDialog.setNegativeButton("No", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialogInterface, int i) { + finish(); + } + }); + downloadDialog.show(); + } + super.onCreate(savedInstanceState); + } +} |