summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>2013-02-13 09:08:37 +0100
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>2013-02-14 09:29:47 +0100
commit27438946427ea006a2dfb6369fdb742abcf9ea05 (patch)
treefa3ad8f3b0618cc14b437413452906de273d017f
parent2ef46bbfada155ae65e999f633f7edfda751438a (diff)
parentf71e67d47f681b55a8b68d051e607e8478958638 (diff)
Merge branch 'unstable' into masterHEADmaster
-rw-r--r--.commit-template34
-rw-r--r--Ministro/AndroidManifest.xml30
-rw-r--r--Ministro/jni/Android.mk10
-rw-r--r--Ministro/jni/Application.mk1
-rw-r--r--Ministro/jni/chmode.c27
-rw-r--r--Ministro/jni/extract.cpp178
-rw-r--r--Ministro/jni/extract.h101
-rw-r--r--Ministro/res/drawable-hdpi/icon.pngbin0 -> 31620 bytes
-rw-r--r--Ministro/res/drawable-ldpi/icon.pngbin0 -> 1670 bytes
-rw-r--r--Ministro/res/drawable-mdpi/icon.pngbin0 -> 2394 bytes
-rw-r--r--Ministro/res/drawable/logo.pngbin0 -> 427576 bytes
-rw-r--r--Ministro/res/layout/main.xml10
-rw-r--r--Ministro/res/layout/repoconfig.xml33
-rw-r--r--Ministro/res/values-de/mstrings.xml22
-rw-r--r--Ministro/res/values-el/strings.xml22
-rw-r--r--Ministro/res/values-es/strings.xml22
-rw-r--r--Ministro/res/values-et/strings.xml22
-rw-r--r--Ministro/res/values-fa/strings.xml22
-rw-r--r--Ministro/res/values-fr/strings.xml22
-rw-r--r--Ministro/res/values-id/strings.xml22
-rw-r--r--Ministro/res/values-it/strings.xml22
-rw-r--r--Ministro/res/values-ja/strings.xml22
-rw-r--r--Ministro/res/values-ms/strings.xml22
-rw-r--r--Ministro/res/values-nb/strings.xml22
-rw-r--r--Ministro/res/values-nl/strings.xml22
-rw-r--r--Ministro/res/values-pl/strings.xml22
-rw-r--r--Ministro/res/values-pt-rBR/strings.xml22
-rw-r--r--Ministro/res/values-ro/strings.xml23
-rw-r--r--Ministro/res/values-rs/strings.xml22
-rw-r--r--Ministro/res/values-ru/strings.xml22
-rw-r--r--Ministro/res/values-zh-rCN/strings.xml22
-rw-r--r--Ministro/res/values-zh-rTW/strings.xml22
-rw-r--r--Ministro/res/values/arrays.xml13
-rw-r--r--Ministro/res/values/strings.xml26
-rw-r--r--Ministro/src/org/kde/necessitas/ministro/ExtractStyle.java1291
-rw-r--r--Ministro/src/org/kde/necessitas/ministro/IMinistro.aidl47
-rw-r--r--Ministro/src/org/kde/necessitas/ministro/IMinistroCallback.aidl52
-rw-r--r--Ministro/src/org/kde/necessitas/ministro/Library.java259
-rw-r--r--Ministro/src/org/kde/necessitas/ministro/MinistroActivity.java860
-rw-r--r--Ministro/src/org/kde/necessitas/ministro/MinistroConfigActivity.java102
-rw-r--r--Ministro/src/org/kde/necessitas/ministro/MinistroService.java748
-rw-r--r--MinistroConfigurationTool/AndroidManifest.xml16
-rw-r--r--MinistroConfigurationTool/res/drawable-hdpi/icon.pngbin0 -> 47754 bytes
-rw-r--r--MinistroConfigurationTool/res/drawable-ldpi/icon.pngbin0 -> 2357 bytes
-rw-r--r--MinistroConfigurationTool/res/drawable-mdpi/icon.pngbin0 -> 3665 bytes
-rw-r--r--MinistroConfigurationTool/res/values-ro/strings.xml5
-rw-r--r--MinistroConfigurationTool/res/values/strings.xml5
-rw-r--r--MinistroConfigurationTool/src/org/kde/ministro/config/MinistroConfigurationToolActivity.java67
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
new file mode 100644
index 0000000..e9050a4
--- /dev/null
+++ b/Ministro/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/Ministro/res/drawable-ldpi/icon.png b/Ministro/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..585728a
--- /dev/null
+++ b/Ministro/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/Ministro/res/drawable-mdpi/icon.png b/Ministro/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..3896146
--- /dev/null
+++ b/Ministro/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/Ministro/res/drawable/logo.png b/Ministro/res/drawable/logo.png
new file mode 100644
index 0000000..8366c4f
--- /dev/null
+++ b/Ministro/res/drawable/logo.png
Binary files differ
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&amp;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
new file mode 100644
index 0000000..5b97acc
--- /dev/null
+++ b/MinistroConfigurationTool/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/MinistroConfigurationTool/res/drawable-ldpi/icon.png b/MinistroConfigurationTool/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..0011174
--- /dev/null
+++ b/MinistroConfigurationTool/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/MinistroConfigurationTool/res/drawable-mdpi/icon.png b/MinistroConfigurationTool/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..0f3bf43
--- /dev/null
+++ b/MinistroConfigurationTool/res/drawable-mdpi/icon.png
Binary files differ
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);
+ }
+}