Initial commit
This commit is contained in:
commit
ce05364151
6
.netbeans.xml
Normal file
6
.netbeans.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<auxiliary-configuration xmlns="http://www.netbeans.org/ns/auxiliary-configuration/1">
|
||||
<spellchecker-wordlist xmlns="http://www.netbeans.org/ns/spellchecker-wordlist/1">
|
||||
<word>xml</word>
|
||||
</spellchecker-wordlist>
|
||||
</auxiliary-configuration>
|
20
AndroidManifest.xml
Normal file
20
AndroidManifest.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.annimon.layoutviewer"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.1">
|
||||
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
|
||||
<activity android:name=".LayoutViewerActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/ApplicationTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:mimeType ="text/xml" />
|
||||
<data android:mimeType = "text/plain"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<!-- <category android:name="android.intent.category.LAUNCHER" /> -->
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
18
ant.properties
Normal file
18
ant.properties
Normal file
@ -0,0 +1,18 @@
|
||||
# This file is used to override default values used by the Ant build system.
|
||||
#
|
||||
# This file must be checked into Version Control Systems, as it is
|
||||
# integral to the build system of your project.
|
||||
|
||||
# This file is only used by the Ant script.
|
||||
|
||||
# You can use this to override default values such as
|
||||
# 'source.dir' for the location of your java source folder and
|
||||
# 'out.dir' for the location of your output folder.
|
||||
|
||||
# You can also use it define how the release builds are signed by declaring
|
||||
# the following properties:
|
||||
# 'key.store' for the location of your keystore and
|
||||
# 'key.alias' for the name of the key to use.
|
||||
# The password will be asked during the build when you use the 'release' target.
|
||||
key.store=D:\\\\\\\\Android\\\\\\\\android-sign\\\\\\\\keystore
|
||||
key.alias=sign
|
92
build.xml
Normal file
92
build.xml
Normal file
@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="LayoutViewer" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
'android' tool to add properties to it.
|
||||
This is the place to change some Ant specific build properties.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
source.dir
|
||||
The name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
The name of the output directory. Default is 'bin'.
|
||||
|
||||
For other overridable properties, look at the beginning of the rules
|
||||
files in the SDK, at tools/ant/build.xml
|
||||
|
||||
Properties related to the SDK location or the project target should
|
||||
be updated using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="ant.properties" />
|
||||
|
||||
<!-- if sdk.dir was not set from one of the property file, then
|
||||
get it from the ANDROID_HOME env var.
|
||||
This must be done before we load project.properties since
|
||||
the proguard config can use sdk.dir -->
|
||||
<property environment="env" />
|
||||
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
||||
<isset property="env.ANDROID_HOME" />
|
||||
</condition>
|
||||
|
||||
<!-- The project.properties file is created and updated by the 'android'
|
||||
tool, as well as ADT.
|
||||
|
||||
This contains project specific properties such as project target, and library
|
||||
dependencies. Lower level build properties are stored in ant.properties
|
||||
(or in .classpath for Eclipse projects).
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
<!--
|
||||
Import per project custom build rules if present at the root of the project.
|
||||
This is the place to put custom intermediary targets such as:
|
||||
-pre-build
|
||||
-pre-compile
|
||||
-post-compile (This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||
-post-package
|
||||
-post-build
|
||||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<!-- version-tag: 1 -->
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
</project>
|
10
local.properties
Normal file
10
local.properties
Normal file
@ -0,0 +1,10 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must *NOT* be checked into Version Control Systems,
|
||||
# as it contains information specific to your local configuration.
|
||||
|
||||
# location of the SDK. This is only used by Ant
|
||||
# For customization when using a Version Control System, please read the
|
||||
# header note.
|
||||
sdk.dir=D:\\Android\\android-sdk
|
25
proguard-project.txt
Normal file
25
proguard-project.txt
Normal file
@ -0,0 +1,25 @@
|
||||
# To enable ProGuard in your project, edit project.properties
|
||||
# to define the proguard.config property as described in that file.
|
||||
#
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
@E:\\SETUPS\\Disk\\Programming\\Java\\android.pro
|
||||
-obfuscationdictionary E:\\SETUPS\\Disk\\Programming\\Java\\compact.txt
|
||||
-optimizationpasses 9
|
||||
-allowaccessmodification
|
||||
-overloadaggressively
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
14
project.properties
Normal file
14
project.properties
Normal file
@ -0,0 +1,14 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
proguard.config=${sdk.dir}\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\tools\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\proguard\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-16
|
BIN
res/drawable-hdpi/ic_launcher.png
Normal file
BIN
res/drawable-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
res/drawable-ldpi/ic_launcher.png
Normal file
BIN
res/drawable-ldpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 615 B |
BIN
res/drawable-mdpi/ic_launcher.png
Normal file
BIN
res/drawable-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 846 B |
BIN
res/drawable-xhdpi/ic_launcher.png
Normal file
BIN
res/drawable-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
13
res/values/strings.xml
Normal file
13
res/values/strings.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Layout Viewer</string>
|
||||
<string name="error">Error while openning file.</string>
|
||||
|
||||
<string name="theme_light">Light Theme</string>
|
||||
<string name="theme_black">Black Theme</string>
|
||||
<string name="theme_dialog">Dialog Theme</string>
|
||||
<string name="theme_wallpaper">Wallpaper Theme</string>
|
||||
<string name="theme_holo_light">Holo Light Theme</string>
|
||||
<string name="theme_holo">Holo Theme</string>
|
||||
<string name="theme_holo_dialog">Holo Dialog Theme</string>
|
||||
</resources>
|
3
res/values/styles.xml
Normal file
3
res/values/styles.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<style name="ApplicationTheme" parent="android:Theme.Light" />
|
||||
</resources>
|
166
src/com/annimon/layoutviewer/AXMLPrinter.java
Normal file
166
src/com/annimon/layoutviewer/AXMLPrinter.java
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.annimon.layoutviewer;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import com.googlecode.android4me.res.AXmlResourceParser;
|
||||
import com.googlecode.android4me.util.TypedValue;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
* This is example usage of AXMLParser class.
|
||||
*
|
||||
* Prints xml document from Android's binary xml file.
|
||||
*/
|
||||
public class AXMLPrinter {
|
||||
|
||||
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
|
||||
private static StringBuilder xmlOutput;
|
||||
|
||||
public static String decode(InputStream is) {
|
||||
xmlOutput = new StringBuilder();
|
||||
try {
|
||||
AXmlResourceParser parser = new AXmlResourceParser();
|
||||
parser.open(is);
|
||||
StringBuilder indent = new StringBuilder(4);
|
||||
final String indentStep = " ";
|
||||
while (true) {
|
||||
int type = parser.next();
|
||||
if (type == XmlPullParser.END_DOCUMENT) {
|
||||
break;
|
||||
}
|
||||
switch (type) {
|
||||
case XmlPullParser.START_DOCUMENT: {
|
||||
log("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
break;
|
||||
}
|
||||
case XmlPullParser.START_TAG: {
|
||||
log("%s<%s%s", indent,
|
||||
getNamespacePrefix(parser.getPrefix()), parser.getName());
|
||||
indent.append(indentStep);
|
||||
|
||||
int namespaceCountBefore = parser.getNamespaceCount(parser.getDepth() - 1);
|
||||
int namespaceCount = parser.getNamespaceCount(parser.getDepth());
|
||||
for (int i = namespaceCountBefore; i < namespaceCount; i++) {
|
||||
log("%sxmlns:%s=\"%s\"",
|
||||
indent,
|
||||
parser.getNamespacePrefix(i),
|
||||
parser.getNamespaceUri(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < parser.getAttributeCount(); i++) {
|
||||
log("%s%s%s=\"%s\"", indent,
|
||||
getNamespacePrefix(parser.getAttributePrefix(i)),
|
||||
parser.getAttributeName(i),
|
||||
getAttributeValue(parser, i));
|
||||
}
|
||||
log("%s>", indent);
|
||||
break;
|
||||
}
|
||||
case XmlPullParser.END_TAG: {
|
||||
indent.setLength(indent.length() - indentStep.length());
|
||||
log("%s</%s%s>", indent,
|
||||
getNamespacePrefix(parser.getPrefix()),
|
||||
parser.getName());
|
||||
break;
|
||||
}
|
||||
case XmlPullParser.TEXT: {
|
||||
log("%s%s", indent, parser.getText());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return xmlOutput.toString();
|
||||
}
|
||||
|
||||
private static String getNamespacePrefix(String prefix) {
|
||||
if (prefix == null || prefix.length() == 0) {
|
||||
return "";
|
||||
}
|
||||
return prefix + ":";
|
||||
}
|
||||
|
||||
private static String getAttributeValue(AXmlResourceParser parser, int index) {
|
||||
int type = parser.getAttributeValueType(index);
|
||||
int data = parser.getAttributeValueData(index);
|
||||
if (type == TypedValue.TYPE_STRING) {
|
||||
return parser.getAttributeValue(index);
|
||||
}
|
||||
if (type == TypedValue.TYPE_ATTRIBUTE) {
|
||||
return String.format("?%s%08X", getPackage(data), data);
|
||||
}
|
||||
if (type == TypedValue.TYPE_REFERENCE) {
|
||||
return String.format("@%s%08X", getPackage(data), data);
|
||||
}
|
||||
if (type == TypedValue.TYPE_FLOAT) {
|
||||
return String.valueOf(Float.intBitsToFloat(data));
|
||||
}
|
||||
if (type == TypedValue.TYPE_INT_HEX) {
|
||||
return String.format("0x%08X", data);
|
||||
}
|
||||
if (type == TypedValue.TYPE_INT_BOOLEAN) {
|
||||
return data != 0 ? "true" : "false";
|
||||
}
|
||||
if (type == TypedValue.TYPE_DIMENSION) {
|
||||
return Float.toString(complexToFloat(data))
|
||||
+ DIMENSION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK];
|
||||
}
|
||||
if (type == TypedValue.TYPE_FRACTION) {
|
||||
return Float.toString(complexToFloat(data))
|
||||
+ FRACTION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK];
|
||||
}
|
||||
if (type >= TypedValue.TYPE_FIRST_COLOR_INT && type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
return String.format("#%08X", data);
|
||||
}
|
||||
if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) {
|
||||
return String.valueOf(data);
|
||||
}
|
||||
return String.format("<0x%X, type 0x%02X>", data, type);
|
||||
}
|
||||
|
||||
private static String getPackage(int id) {
|
||||
if (id >>> 24 == 1) {
|
||||
return "android:";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static void log(String format, Object... arguments) {
|
||||
xmlOutput.append(String.format(format, arguments));
|
||||
xmlOutput.append(LINE_SEPARATOR);
|
||||
//System.out.printf(format, arguments);
|
||||
//System.out.println();
|
||||
}
|
||||
|
||||
/////////////////////////////////// ILLEGAL STUFF, DONT LOOK :)
|
||||
private static float complexToFloat(int complex) {
|
||||
return (float) (complex & 0xFFFFFF00) * RADIX_MULTS[(complex >> 4) & 3];
|
||||
}
|
||||
private static final float RADIX_MULTS[] = {
|
||||
0.00390625F, 3.051758E-005F, 1.192093E-007F, 4.656613E-010F
|
||||
};
|
||||
private static final String DIMENSION_UNITS[] = {
|
||||
"px", "dp", "sp", "pt", "in", "mm", "", ""
|
||||
};
|
||||
private static final String FRACTION_UNITS[] = {
|
||||
"%", "%p", "", "", "", "", "", ""
|
||||
};
|
||||
}
|
92
src/com/annimon/layoutviewer/AndroidXml.java
Normal file
92
src/com/annimon/layoutviewer/AndroidXml.java
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* aNNiMON 2012
|
||||
* For more info visit http://annimon.com/
|
||||
*/
|
||||
package com.annimon.layoutviewer;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author aNNiMON
|
||||
*/
|
||||
public class AndroidXml {
|
||||
|
||||
public static final byte XML = 0, AXML = 1;
|
||||
private static final int AXML_FILE_SIGNATURE = 0x03000800;
|
||||
|
||||
private byte xmlType;
|
||||
private ByteArrayInputStream bais;
|
||||
|
||||
public static AndroidXml readFromStream(InputStream is) throws IOException {
|
||||
byte[] data = readData(is);
|
||||
|
||||
AndroidXml androidXml = new AndroidXml(data);
|
||||
androidXml.readXmlType(data);
|
||||
return androidXml;
|
||||
}
|
||||
|
||||
private AndroidXml(byte[] data) {
|
||||
bais = new ByteArrayInputStream(data);
|
||||
}
|
||||
|
||||
public String getText() throws IOException {
|
||||
if (getXmlType() == AXML) return getDecodedText();
|
||||
return getPlainText();
|
||||
}
|
||||
|
||||
public byte getXmlType() {
|
||||
return xmlType;
|
||||
}
|
||||
|
||||
public String getPlainText() throws IOException {
|
||||
InputStreamReader reader = new InputStreamReader(bais, Charset.defaultCharset());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int read;
|
||||
while ((read = reader.read()) != -1) {
|
||||
sb.append((char) read);
|
||||
}
|
||||
|
||||
reader.close();
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getDecodedText() {
|
||||
return AXMLPrinter.decode(bais);
|
||||
}
|
||||
|
||||
private void readXmlType(byte[] data) {
|
||||
if (data.length < 4) xmlType = XML;
|
||||
if (readSignature(data) == AXML_FILE_SIGNATURE) {
|
||||
xmlType = AXML;
|
||||
} else xmlType = XML;
|
||||
}
|
||||
|
||||
private int readSignature(byte[] data) {
|
||||
int value = (data[0] << 24) |
|
||||
(data[1] << 16) |
|
||||
(data[2] << 8) |
|
||||
(data[3]);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static byte[] readData(InputStream is) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int length;
|
||||
byte[] buffer = new byte[1024];
|
||||
while ( (length = is.read(buffer)) != -1 ) {
|
||||
baos.write(buffer, 0, length);
|
||||
}
|
||||
is.close();
|
||||
baos.flush();
|
||||
buffer = baos.toByteArray();
|
||||
baos.close();
|
||||
return buffer;
|
||||
}
|
||||
}
|
102
src/com/annimon/layoutviewer/LayoutViewerActivity.java
Normal file
102
src/com/annimon/layoutviewer/LayoutViewerActivity.java
Normal file
@ -0,0 +1,102 @@
|
||||
package com.annimon.layoutviewer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
import android.view.*;
|
||||
import android.util.SparseIntArray;
|
||||
import android.content.*;
|
||||
import android.os.Build;
|
||||
|
||||
public class LayoutViewerActivity extends Activity {
|
||||
|
||||
private static final SparseIntArray THEMES = new SparseIntArray() {{
|
||||
// All API levels
|
||||
put(R.string.theme_light, android.R.style.Theme_Light);
|
||||
put(R.string.theme_black, android.R.style.Theme_Black);
|
||||
put(R.string.theme_dialog, android.R.style.Theme_Dialog);
|
||||
// API level >= 5
|
||||
if (Build.VERSION.SDK_INT >= 5) {
|
||||
put(R.string.theme_wallpaper, android.R.style.Theme_Wallpaper);
|
||||
}
|
||||
// API level >= 11
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
put(R.string.theme_holo_light, android.R.style.Theme_Holo_Light);
|
||||
put(R.string.theme_holo, android.R.style.Theme_Holo);
|
||||
put(R.string.theme_holo_dialog, android.R.style.Theme_Holo_Dialog);
|
||||
}
|
||||
}};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
this.setTitle(getString(R.string.app_name));
|
||||
int theme = Prefs.getPreference(this).getThemeInt(android.R.style.Theme);
|
||||
setTheme(theme);
|
||||
|
||||
String data = getLayoutText();
|
||||
if ((data == null) || (data.length() == 0)) {
|
||||
Toast.makeText(this, R.string.error, Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
ViewInflater inflater = new ViewInflater(this);
|
||||
XmlPullParser parse;
|
||||
try {
|
||||
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
||||
parse = factory.newPullParser();
|
||||
parse.setInput(new StringReader(data));
|
||||
View v = inflater.inflate(parse);
|
||||
setContentView(v);
|
||||
} catch (XmlPullParserException ex) {
|
||||
ex.printStackTrace();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
for (int i = 0; i < THEMES.size(); i++) {
|
||||
int key = THEMES.keyAt(i);
|
||||
menu.add(Menu.NONE, key, Menu.NONE, key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
final int id = item.getItemId();
|
||||
int theme = THEMES.get(id, -1);
|
||||
if (theme != -1) {
|
||||
// save pref
|
||||
Prefs.getPreference(this).putThemeInt(theme);
|
||||
// restart activity
|
||||
Intent intent = getIntent();
|
||||
finish();
|
||||
startActivity(intent);
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private String getLayoutText() {
|
||||
try {
|
||||
Uri fileUri = getIntent().getData();
|
||||
AndroidXml androidXml = AndroidXml.readFromStream(new FileInputStream(fileUri.getPath()));
|
||||
return androidXml.getText();
|
||||
} catch (Exception ex) {
|
||||
Log.e(getClass().getName(), ex.getMessage(), ex);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
43
src/com/annimon/layoutviewer/Prefs.java
Normal file
43
src/com/annimon/layoutviewer/Prefs.java
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* aNNiMON 2012
|
||||
* For more info visit http://annimon.com/
|
||||
*/
|
||||
package com.annimon.layoutviewer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author aNNiMON
|
||||
*/
|
||||
public class Prefs {
|
||||
|
||||
private static Prefs instance;
|
||||
|
||||
public static Prefs getPreference(Activity activity) {
|
||||
if (instance == null) {
|
||||
instance = new Prefs(activity.getPreferences(Activity.MODE_PRIVATE));
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static final String THEME_PREFERENCE = "theme";
|
||||
private SharedPreferences pref;
|
||||
|
||||
private Prefs(SharedPreferences pref) {
|
||||
this.pref = pref;
|
||||
}
|
||||
|
||||
public int getThemeInt(int defaultValue) {
|
||||
return pref.getInt(THEME_PREFERENCE, defaultValue);
|
||||
}
|
||||
|
||||
public void putThemeInt(int value) {
|
||||
SharedPreferences.Editor editor = pref.edit();
|
||||
editor.putInt(THEME_PREFERENCE, value);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
}
|
613
src/com/annimon/layoutviewer/ViewInflater.java
Normal file
613
src/com/annimon/layoutviewer/ViewInflater.java
Normal file
@ -0,0 +1,613 @@
|
||||
package com.annimon.layoutviewer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.*;
|
||||
|
||||
public class ViewInflater {
|
||||
|
||||
private Stack<ViewGroup> layoutStack;
|
||||
private Hashtable<String, Integer> ids;
|
||||
private Context context;
|
||||
private int idIndex;
|
||||
|
||||
public ViewInflater(Context context) {
|
||||
this.layoutStack = new Stack<ViewGroup>();
|
||||
this.ids = new Hashtable<String, Integer>();
|
||||
this.context = context;
|
||||
this.idIndex = 0;
|
||||
}
|
||||
|
||||
public View inflate(String text) {
|
||||
XmlPullParser parse;
|
||||
try {
|
||||
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
||||
parse = factory.newPullParser();
|
||||
parse.setInput(new StringReader(text));
|
||||
return inflate(parse);
|
||||
} catch (XmlPullParserException ex) {
|
||||
return null;
|
||||
} catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public View inflate(XmlPullParser parse) throws XmlPullParserException, IOException {
|
||||
layoutStack.clear();
|
||||
ids.clear();
|
||||
|
||||
Stack<StringBuffer> data = new Stack<StringBuffer>();
|
||||
int evt = parse.getEventType();
|
||||
View root = null;
|
||||
while (evt != XmlPullParser.END_DOCUMENT) {
|
||||
switch (evt) {
|
||||
case XmlPullParser.START_DOCUMENT:
|
||||
data.clear();
|
||||
break;
|
||||
case XmlPullParser.START_TAG:
|
||||
data.push(new StringBuffer());
|
||||
View v = createView(parse);
|
||||
if (v == null) {
|
||||
evt = parse.next();
|
||||
continue;
|
||||
}
|
||||
if (root == null) {
|
||||
root = v;
|
||||
} else {
|
||||
layoutStack.peek().addView(v);
|
||||
}
|
||||
if (v instanceof ViewGroup) {
|
||||
layoutStack.push((ViewGroup) v);
|
||||
}
|
||||
break;
|
||||
case XmlPullParser.TEXT:
|
||||
data.peek().append(parse.getText());
|
||||
break;
|
||||
case XmlPullParser.END_TAG:
|
||||
data.pop();
|
||||
if (isLayout(parse.getName())) {
|
||||
layoutStack.pop();
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
evt = parse.next();
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
private View createView(XmlPullParser parse) {
|
||||
String name = parse.getName();
|
||||
View result = null;
|
||||
AttributeSet atts = Xml.asAttributeSet(parse);
|
||||
if (name.equals("LinearLayout")) {
|
||||
result = new LinearLayout(context);
|
||||
} else if (name.equals("RadioGroup")) {
|
||||
result = new RadioGroup(context);
|
||||
} else if (name.equals("TableRow")) {
|
||||
result = new TableRow(context);
|
||||
} else if (name.equals("TableLayout")) {
|
||||
result = new TableLayout(context);
|
||||
} else if (name.equals("AbsoluteLayout")) {
|
||||
result = new AbsoluteLayout(context);
|
||||
} else if (name.equals("RelativeLayout")) {
|
||||
result = new RelativeLayout(context);
|
||||
} else if (name.equals("ScrollView")) {
|
||||
result = new ScrollView(context);
|
||||
} else if (name.equals("FrameLayout")) {
|
||||
result = new FrameLayout(context);
|
||||
} else if (name.equals("ListView")) {
|
||||
result = new ListView(context);
|
||||
} else if (name.equals("TextView")) {
|
||||
result = new TextView(context);
|
||||
} else if (name.equals("AutoCompleteTextView")) {
|
||||
result = new AutoCompleteTextView(context);
|
||||
} else if (name.equals("AnalogClock")) {
|
||||
result = new AnalogClock(context);
|
||||
} else if (name.equals("Button")) {
|
||||
result = new Button(context);
|
||||
} else if (name.equals("CalendarView")) {
|
||||
result = new CalendarView(context);
|
||||
} else if (name.equals("CheckBox")) {
|
||||
result = new CheckBox(context);
|
||||
} else if (name.equals("Chronometer")) {
|
||||
result = new Chronometer(context);
|
||||
} else if (name.equals("DatePicker")) {
|
||||
result = new DatePicker(context);
|
||||
} else if (name.equals("DigitalClock")) {
|
||||
result = new DigitalClock(context);
|
||||
} else if (name.equals("EditText")) {
|
||||
result = new EditText(context);
|
||||
} else if (name.equals("ImageButton")) {
|
||||
result = new ImageButton(context);
|
||||
} else if (name.equals("ImageView")) {
|
||||
result = new ImageView(context);
|
||||
} else if (name.equals("ProgressBar")) {
|
||||
result = new ProgressBar(context);
|
||||
} else if (name.equals("RadioButton")) {
|
||||
result = new RadioButton(context);
|
||||
} else if (name.equals("RatingBar")) {
|
||||
result = new RatingBar(context);
|
||||
} else if (name.equals("SeekBar")) {
|
||||
result = new SeekBar(context);
|
||||
} else if (name.equals("Spinner")) {
|
||||
result = new Spinner(context);
|
||||
} else if (name.equals("TimePicker")) {
|
||||
result = new TimePicker(context);
|
||||
} else {
|
||||
Toast.makeText(context, "Unhandled tag:" + name, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
if (result == null) return null;
|
||||
|
||||
String id = findAttribute(atts, "android:id");
|
||||
if (id != null) {
|
||||
int idNumber = lookupId(id);
|
||||
if (idNumber > -1) {
|
||||
result.setId(idNumber);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (result instanceof CompoundButton) {
|
||||
CompoundButton cb = (CompoundButton) result;
|
||||
String checked = findAttribute(atts, "android:checked");
|
||||
cb.setChecked("true".equals(checked));
|
||||
}
|
||||
|
||||
if (result instanceof Chronometer) {
|
||||
Chronometer cr = (Chronometer) result;
|
||||
String format = findAttribute(atts, "android:format");
|
||||
if (format != null) {
|
||||
cr.setFormat(format);
|
||||
}
|
||||
}
|
||||
|
||||
if (result instanceof DatePicker) {
|
||||
DatePicker dp = (DatePicker) result;
|
||||
|
||||
String calendarViewShown = findAttribute(atts, "android:calendarViewShown");
|
||||
dp.setCalendarViewShown("true".equals(calendarViewShown));
|
||||
|
||||
String spinnersShown = findAttribute(atts, "android:spinnersShown");
|
||||
dp.setSpinnersShown("true".equals(spinnersShown));
|
||||
}
|
||||
|
||||
if (result instanceof ImageView) {
|
||||
ImageView iv = (ImageView) result;
|
||||
|
||||
String adjustViewBounds = findAttribute(atts, "android:adjustViewBounds");
|
||||
iv.setAdjustViewBounds("true".equals(adjustViewBounds));
|
||||
|
||||
String maxWidth = findAttribute(atts, "android:maxWidth");
|
||||
if (maxWidth != null) {
|
||||
iv.setMaxWidth(readSize(maxWidth));
|
||||
}
|
||||
|
||||
String maxHeight = findAttribute(atts, "android:maxHeight");
|
||||
if (maxHeight != null) {
|
||||
iv.setMaxHeight(readSize(maxHeight));
|
||||
}
|
||||
|
||||
iv.setImageResource(R.drawable.ic_launcher);
|
||||
}
|
||||
|
||||
if (result instanceof RatingBar) {
|
||||
RatingBar rb = (RatingBar) result;
|
||||
|
||||
String isIndicator = findAttribute(atts, "android:isIndicator");
|
||||
rb.setIsIndicator("true".equals(isIndicator));
|
||||
|
||||
String numStars = findAttribute(atts, "android:numStars");
|
||||
if (numStars != null) {
|
||||
rb.setNumStars(Integer.parseInt(numStars));
|
||||
}
|
||||
|
||||
String rating = findAttribute(atts, "android:rating");
|
||||
if (rating != null) {
|
||||
rb.setRating(Float.parseFloat(rating));
|
||||
}
|
||||
|
||||
String stepSize = findAttribute(atts, "android:stepSize");
|
||||
if (stepSize != null) {
|
||||
rb.setStepSize(Float.parseFloat(stepSize));
|
||||
}
|
||||
}
|
||||
|
||||
if (result instanceof TextView) {
|
||||
TextView tv = (TextView) result;
|
||||
|
||||
String gravity = findAttribute(atts, "android:gravity");
|
||||
if (gravity != null) {
|
||||
tv.setGravity(parseGravity(gravity));
|
||||
}
|
||||
|
||||
String hint = findAttribute(atts, "android:hint");
|
||||
if (hint != null) {
|
||||
hint = hint.replace("\\n", "\n");
|
||||
tv.setHint(hint);
|
||||
}
|
||||
|
||||
String password = findAttribute(atts, "android:password");
|
||||
if ("true".equals(password)) {
|
||||
tv.setTransformationMethod(new PasswordTransformationMethod());
|
||||
}
|
||||
|
||||
String text = findAttribute(atts, "android:text");
|
||||
if (text != null) {
|
||||
text = text.replace("\\n", "\n");
|
||||
tv.setText(text);
|
||||
} else tv.setText("text");
|
||||
|
||||
String textColor = findAttribute(atts, "android:textColor");
|
||||
if ( (textColor != null) && (textColor.startsWith("#")) ) {
|
||||
textColor = textColor.substring(1);
|
||||
int value;
|
||||
try {
|
||||
value = (int)Long.parseLong(textColor, 16);
|
||||
} catch (NumberFormatException nfe) {
|
||||
value = Integer.parseInt(textColor);
|
||||
}
|
||||
tv.setBackgroundColor(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (result instanceof ProgressBar) {
|
||||
ProgressBar pb = (ProgressBar) result;
|
||||
|
||||
String indet = findAttribute(atts, "android:indeterminate");
|
||||
if (indet != null) {
|
||||
pb.setIndeterminate("true".equals(indet));
|
||||
}
|
||||
|
||||
String max = findAttribute(atts, "android:max");
|
||||
if (max != null) {
|
||||
pb.setMax(parseInt(max));
|
||||
}
|
||||
|
||||
String progress = findAttribute(atts, "android:progress");
|
||||
if (progress != null) {
|
||||
pb.setProgress(parseInt(progress));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (result instanceof LinearLayout) {
|
||||
LinearLayout ll = (LinearLayout) result;
|
||||
String orient = findAttribute(atts, "android:orientation");
|
||||
if (orient != null) {
|
||||
if (orient.equals("horizontal"))
|
||||
ll.setOrientation(LinearLayout.HORIZONTAL);
|
||||
else if (orient.equals("vertical"))
|
||||
ll.setOrientation(LinearLayout.VERTICAL);
|
||||
}
|
||||
}
|
||||
|
||||
if (result instanceof RadioGroup) {
|
||||
RadioGroup rg = (RadioGroup) result;
|
||||
String cid = findAttribute(atts, "android:checkedButton");
|
||||
if (cid != null) {
|
||||
rg.check(parseInt(cid));
|
||||
}
|
||||
}
|
||||
|
||||
if (result instanceof View) {
|
||||
View view = result;
|
||||
/* API 11
|
||||
String alpha = findAttribute(atts, "android:alpha");
|
||||
if (alpha != null) v.setAlpha(Float.parseFloat(alpha));
|
||||
*/
|
||||
maybeSetBoolean(view, "setClickable", atts, "android:clickable");
|
||||
maybeSetBoolean(view, "setFocusable", atts, "android:focusable");
|
||||
maybeSetBoolean(view, "setHapticFeedbackEnabled", atts, "android:hapticFeedbackEnabled");
|
||||
|
||||
String background = findAttribute(atts, "android:background");
|
||||
if ( (background != null) && (background.startsWith("#")) ) {
|
||||
background = background.substring(1);
|
||||
int value;
|
||||
try {
|
||||
value = (int)Long.parseLong(background, 16);
|
||||
} catch (NumberFormatException nfe) {
|
||||
value = Integer.parseInt(background);
|
||||
}
|
||||
view.setBackgroundColor(value);
|
||||
}
|
||||
|
||||
String minWidth = findAttribute(atts, "android:minWidth");
|
||||
if (minWidth != null) {
|
||||
view.setMinimumWidth(readSize(minWidth));
|
||||
}
|
||||
|
||||
String minHeight = findAttribute(atts, "android:minHeight");
|
||||
if (minHeight != null) {
|
||||
view.setMinimumHeight(readSize(minHeight));
|
||||
}
|
||||
|
||||
String visibility = findAttribute(atts, "android:visibility");
|
||||
if (visibility != null) {
|
||||
int code = -1;
|
||||
if ("visible".equals(visibility)) {
|
||||
code = 0;
|
||||
} else if ("invisible".equals(visibility)) {
|
||||
code = 1;
|
||||
} else if ("gone".equals(visibility)) {
|
||||
code = 2;
|
||||
}
|
||||
if (code != -1) {
|
||||
view.setVisibility(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (layoutStack.size() > 0) {
|
||||
result.setLayoutParams(loadLayoutParams(atts, layoutStack.peek()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean maybeSetBoolean(View v, String method, AttributeSet atts, String arg) {
|
||||
return maybeSetBoolean(v, method, findAttribute(atts, arg));
|
||||
}
|
||||
|
||||
private static boolean isLayout(String name) {
|
||||
return name.endsWith("Layout")
|
||||
|| name.equals("RadioGroup")
|
||||
|| name.equals("TableRow")
|
||||
|| name.equals("ScrollView")
|
||||
|| name.equals("ListView");
|
||||
}
|
||||
|
||||
private int lookupId(String id) {
|
||||
int ix = id.indexOf('/');
|
||||
if (ix != -1) {
|
||||
String idName = id.substring(ix + 1);
|
||||
Integer n = ids.get(idName);
|
||||
if (n == null && id.startsWith("@+")) {
|
||||
n = Integer.valueOf(idIndex++);
|
||||
ids.put(idName, n);
|
||||
}
|
||||
if (n != null) return n.intValue();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String findAttribute(AttributeSet atts, String id) {
|
||||
for (int i = 0; i < atts.getAttributeCount(); i++) {
|
||||
if (atts.getAttributeName(i).equals(id)) {
|
||||
return atts.getAttributeValue(i);
|
||||
}
|
||||
}
|
||||
int ix = id.indexOf(':');
|
||||
if (ix != -1) {
|
||||
return atts.getAttributeValue("http://schemas.android.com/apk/res/android", id.substring(ix + 1));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ViewGroup.LayoutParams loadLayoutParams(AttributeSet atts, ViewGroup vg) {
|
||||
ViewGroup.LayoutParams lps = null;
|
||||
|
||||
String width = findAttribute(atts, "android:layout_width");
|
||||
String height = findAttribute(atts, "android:layout_height");
|
||||
int w = readSize(width);
|
||||
int h = readSize(height);
|
||||
|
||||
if (vg instanceof RadioGroup) {
|
||||
lps = new RadioGroup.LayoutParams(w, h);
|
||||
} else if (vg instanceof TableRow) {
|
||||
lps = new TableRow.LayoutParams();
|
||||
} else if (vg instanceof TableLayout) {
|
||||
lps = new TableLayout.LayoutParams();
|
||||
} else if (vg instanceof LinearLayout) {
|
||||
lps = new LinearLayout.LayoutParams(w, h);
|
||||
} else if (vg instanceof AbsoluteLayout) {
|
||||
String x = findAttribute(atts, "android:layout_x");
|
||||
String y = findAttribute(atts, "android:layout_y");
|
||||
|
||||
lps = new AbsoluteLayout.LayoutParams(w, h, readSize(x), readSize(y));
|
||||
} else if (vg instanceof RelativeLayout) {
|
||||
lps = new RelativeLayout.LayoutParams(w, h);
|
||||
} else if (vg instanceof ScrollView) {
|
||||
lps = new ScrollView.LayoutParams(w, h);
|
||||
} else if (vg instanceof ListView) {
|
||||
lps = new ListView.LayoutParams(w, h);
|
||||
} else if (vg instanceof FrameLayout) {
|
||||
lps = new FrameLayout.LayoutParams(w, h);
|
||||
}
|
||||
|
||||
if (lps instanceof LinearLayout.LayoutParams) {
|
||||
LinearLayout.LayoutParams l = (LinearLayout.LayoutParams) lps;
|
||||
String gravity = findAttribute(atts, "android:layout_gravity");
|
||||
if (gravity != null) {
|
||||
l.gravity = parseGravity(gravity);
|
||||
//l.gravity = Integer.parseInt(gravity);
|
||||
}
|
||||
|
||||
String weight = findAttribute(atts, "android:layout_weight");
|
||||
if (weight != null) {
|
||||
l.weight = Float.parseFloat(weight);
|
||||
}
|
||||
lps = l;
|
||||
}
|
||||
|
||||
if (lps instanceof RelativeLayout.LayoutParams) {
|
||||
RelativeLayout.LayoutParams l = (RelativeLayout.LayoutParams) lps;
|
||||
for (int i = 0; i < relative_strings.length; i++) {
|
||||
String id = findAttribute(atts, relative_strings[i]);
|
||||
if (id != null) {
|
||||
int idN = lookupId(id);
|
||||
l.addRule(relative_verbs[i], idN);
|
||||
}
|
||||
}
|
||||
// Margin handling
|
||||
// Contributed by Vishal Choudhary - Thanks!
|
||||
String bottom = findAttribute(atts, "android:layout_marginBottom");
|
||||
String left = findAttribute(atts, "android:layout_marginLeft");
|
||||
String right = findAttribute(atts, "android:layout_marginRight");
|
||||
String top = findAttribute(atts, "android:layout_marginTop");
|
||||
int bottomInt = 0, leftInt = 0, rightInt = 0, topInt = 0;
|
||||
if (bottom != null) bottomInt = readSize(bottom);
|
||||
if (left != null) leftInt = readSize(left);
|
||||
if (right != null) rightInt = readSize(right);
|
||||
if (top != null) topInt = readSize(top);
|
||||
|
||||
l.setMargins(leftInt, topInt, rightInt, bottomInt);
|
||||
}
|
||||
|
||||
return lps;
|
||||
}
|
||||
|
||||
private int readSize(String val) {
|
||||
if ("wrap_content".equals(val)) {
|
||||
return ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
} else if ("fill_parent".equals(val)) {
|
||||
return ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
} else if ("match_parent".equals(val)) {
|
||||
return ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
} else if (val != null) {
|
||||
int ix = val.indexOf("px");
|
||||
if (ix != -1) return parseInt(val.substring(0, ix));
|
||||
|
||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
|
||||
ix = val.indexOf("dp");
|
||||
if (ix != -1) {
|
||||
return (int) (parseInt(val.substring(0, ix)) * dm.density + 0.5);
|
||||
}
|
||||
|
||||
ix = val.indexOf("sp");
|
||||
if (ix != -1) {
|
||||
return (int) (parseInt(val.substring(0, ix)) * dm.density + 0.5);
|
||||
}
|
||||
|
||||
ix = val.indexOf("pt");
|
||||
if (ix != -1) {
|
||||
int dp = parseInt(val.substring(0, ix)) * 96 / 72;
|
||||
return (int) (dp * dm.density + 0.5);
|
||||
}
|
||||
|
||||
return parseInt(val);
|
||||
} else {
|
||||
return ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean maybeSetBoolean(View view, String method, String value) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
value = value.toLowerCase();
|
||||
Boolean boolValue;
|
||||
if ("true".equals(value)) {
|
||||
boolValue = Boolean.TRUE;
|
||||
} else if ("false".equals(value)) {
|
||||
boolValue = Boolean.FALSE;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Method m = View.class.getMethod(method, boolean.class);
|
||||
m.invoke(view, boolValue);
|
||||
return true;
|
||||
} catch (NoSuchMethodException ex) {
|
||||
Log.e("ViewInflater", "No such method: " + method, ex);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e("ViewInflater", "Call", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e("ViewInflater", "Call", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.e("ViewInflater", "Call", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int parseGravity(String gravity) {
|
||||
int grav = Gravity.NO_GRAVITY;
|
||||
if (gravity.indexOf("top") != -1) grav |= Gravity.TOP;
|
||||
else if (gravity.indexOf("bottom") != -1) grav |= Gravity.BOTTOM;
|
||||
else if (gravity.indexOf("left") != -1) grav |= Gravity.LEFT;
|
||||
else if (gravity.indexOf("right") != -1) grav |= Gravity.RIGHT;
|
||||
else if (gravity.indexOf("center_vertical") != -1) grav |= Gravity.CENTER_VERTICAL;
|
||||
else if (gravity.indexOf("center_horizontal") != -1) grav |= Gravity.CENTER_HORIZONTAL;
|
||||
else if (gravity.indexOf("clip_vertical") != -1) grav |= Gravity.CLIP_VERTICAL;
|
||||
else if (gravity.indexOf("clip_horizontal") != -1) grav |= Gravity.CLIP_HORIZONTAL;
|
||||
else if (gravity.indexOf("fill_vertical") != -1) grav |= Gravity.FILL_VERTICAL;
|
||||
else if (gravity.indexOf("fill_horizontal") != -1) grav |= Gravity.FILL_HORIZONTAL;
|
||||
else if (gravity.indexOf("start") != -1) grav |= Gravity.START;
|
||||
else if (gravity.indexOf("end") != -1) grav |= Gravity.END;
|
||||
else {
|
||||
if ( (gravity.indexOf("center_") == -1) && (gravity.indexOf("center") != -1) ) grav |= Gravity.CENTER;
|
||||
if ( (gravity.indexOf("fill_") == -1) && (gravity.indexOf("fill") != -1) ) grav |= Gravity.FILL;
|
||||
}
|
||||
return grav;
|
||||
}
|
||||
|
||||
private int parseInt(String str) {
|
||||
try {
|
||||
return Integer.parseInt(str);
|
||||
} catch (NumberFormatException numberFormatException) {
|
||||
return (int) Float.parseFloat(str);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String[] relative_strings = {
|
||||
"android:layout_above",
|
||||
"android:layout_alignBaseline",
|
||||
"android:layout_alignBottom",
|
||||
"android:layout_alignLeft",
|
||||
"android:layout_alignParentBottom",
|
||||
"android:layout_alignParentLeft",
|
||||
"android:layout_alignParentRight",
|
||||
"android:layout_alignParentTop",
|
||||
"android:layout_alignRight",
|
||||
"android:layout_alignTop",
|
||||
"android:layout_below",
|
||||
"android:layout_centerHorizontal",
|
||||
"android:layout_centerInParent",
|
||||
"android:layout_centerVertical",
|
||||
"android:layout_toLeft",
|
||||
"android:layout_toRight"
|
||||
};
|
||||
|
||||
private static final int[] relative_verbs = {
|
||||
RelativeLayout.ABOVE,
|
||||
RelativeLayout.ALIGN_BASELINE,
|
||||
RelativeLayout.ALIGN_BOTTOM,
|
||||
RelativeLayout.ALIGN_LEFT,
|
||||
RelativeLayout.ALIGN_PARENT_BOTTOM,
|
||||
RelativeLayout.ALIGN_PARENT_LEFT,
|
||||
RelativeLayout.ALIGN_PARENT_RIGHT,
|
||||
RelativeLayout.ALIGN_PARENT_TOP,
|
||||
RelativeLayout.ALIGN_RIGHT,
|
||||
RelativeLayout.ALIGN_TOP,
|
||||
RelativeLayout.BELOW,
|
||||
RelativeLayout.CENTER_HORIZONTAL,
|
||||
RelativeLayout.CENTER_IN_PARENT,
|
||||
RelativeLayout.CENTER_VERTICAL,
|
||||
RelativeLayout.LEFT_OF,
|
||||
RelativeLayout.RIGHT_OF
|
||||
};
|
||||
|
||||
}
|
905
src/com/googlecode/android4me/res/AXmlResourceParser.java
Normal file
905
src/com/googlecode/android4me/res/AXmlResourceParser.java
Normal file
@ -0,0 +1,905 @@
|
||||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.android4me.res;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import com.googlecode.android4me.util.TypedValue;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
* Binary xml files parser.
|
||||
*
|
||||
* Parser has only two states:
|
||||
* (1) Operational state, which parser obtains after first successful call
|
||||
* to next() and retains until open(), close(), or failed call to next().
|
||||
* (2) Closed state, which parser obtains after open(), close(), or failed
|
||||
* call to next(). In this state methods return invalid values or throw exceptions.
|
||||
*
|
||||
* TODO:
|
||||
* * check all methods in closed state
|
||||
*
|
||||
*/
|
||||
public class AXmlResourceParser implements XmlResourceParser {
|
||||
|
||||
public AXmlResourceParser() {
|
||||
resetEventInfo();
|
||||
}
|
||||
|
||||
public void open(InputStream stream) {
|
||||
close();
|
||||
if (stream != null) {
|
||||
m_reader = new IntReader(stream, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (!m_operational) {
|
||||
return;
|
||||
}
|
||||
m_operational = false;
|
||||
m_reader.close();
|
||||
m_reader = null;
|
||||
m_strings = null;
|
||||
m_resourceIDs = null;
|
||||
m_namespaces.reset();
|
||||
resetEventInfo();
|
||||
}
|
||||
|
||||
/////////////////////////////////// iteration
|
||||
public int next() throws XmlPullParserException, IOException {
|
||||
if (m_reader == null) {
|
||||
throw new XmlPullParserException("Parser is not opened.", this, null);
|
||||
}
|
||||
try {
|
||||
doNext();
|
||||
return m_event;
|
||||
} catch (IOException e) {
|
||||
close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public int nextToken() throws XmlPullParserException, IOException {
|
||||
return next();
|
||||
}
|
||||
|
||||
public int nextTag() throws XmlPullParserException, IOException {
|
||||
int eventType = next();
|
||||
if (eventType == TEXT && isWhitespace()) {
|
||||
eventType = next();
|
||||
}
|
||||
if (eventType != START_TAG && eventType != END_TAG) {
|
||||
throw new XmlPullParserException("Expected start or end tag.", this, null);
|
||||
}
|
||||
return eventType;
|
||||
}
|
||||
|
||||
public String nextText() throws XmlPullParserException, IOException {
|
||||
if (getEventType() != START_TAG) {
|
||||
throw new XmlPullParserException("Parser must be on START_TAG to read next text.", this, null);
|
||||
}
|
||||
int eventType = next();
|
||||
if (eventType == TEXT) {
|
||||
String result = getText();
|
||||
eventType = next();
|
||||
if (eventType != END_TAG) {
|
||||
throw new XmlPullParserException("Event TEXT must be immediately followed by END_TAG.", this, null);
|
||||
}
|
||||
return result;
|
||||
} else if (eventType == END_TAG) {
|
||||
return "";
|
||||
} else {
|
||||
throw new XmlPullParserException("Parser must be on START_TAG or TEXT to read text.", this, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void require(int type, String namespace, String name) throws XmlPullParserException, IOException {
|
||||
if (type != getEventType()
|
||||
|| (namespace != null && !namespace.equals(getNamespace()))
|
||||
|| (name != null && !name.equals(getName()))) {
|
||||
throw new XmlPullParserException(TYPES[type] + " is expected.", this, null);
|
||||
}
|
||||
}
|
||||
|
||||
public int getDepth() {
|
||||
return m_namespaces.getDepth() - 1;
|
||||
}
|
||||
|
||||
public int getEventType() throws XmlPullParserException {
|
||||
return m_event;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return m_lineNumber;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if (m_name == -1 || (m_event != START_TAG && m_event != END_TAG)) {
|
||||
return null;
|
||||
}
|
||||
return m_strings.getString(m_name);
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
if (m_name == -1 || m_event != TEXT) {
|
||||
return null;
|
||||
}
|
||||
return m_strings.getString(m_name);
|
||||
}
|
||||
|
||||
public char[] getTextCharacters(int[] holderForStartAndLength) {
|
||||
String text = getText();
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
holderForStartAndLength[0] = 0;
|
||||
holderForStartAndLength[1] = text.length();
|
||||
char[] chars = new char[text.length()];
|
||||
text.getChars(0, text.length(), chars, 0);
|
||||
return chars;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return m_strings.getString(m_namespaceUri);
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
int prefix = m_namespaces.findPrefix(m_namespaceUri);
|
||||
return m_strings.getString(prefix);
|
||||
}
|
||||
|
||||
public String getPositionDescription() {
|
||||
return "XML line #" + getLineNumber();
|
||||
}
|
||||
|
||||
public int getNamespaceCount(int depth) throws XmlPullParserException {
|
||||
return m_namespaces.getAccumulatedCount(depth);
|
||||
}
|
||||
|
||||
public String getNamespacePrefix(int pos) throws XmlPullParserException {
|
||||
int prefix = m_namespaces.getPrefix(pos);
|
||||
return m_strings.getString(prefix);
|
||||
}
|
||||
|
||||
public String getNamespaceUri(int pos) throws XmlPullParserException {
|
||||
int uri = m_namespaces.getUri(pos);
|
||||
return m_strings.getString(uri);
|
||||
}
|
||||
|
||||
/////////////////////////////////// attributes
|
||||
public String getClassAttribute() {
|
||||
if (m_classAttribute == -1) {
|
||||
return null;
|
||||
}
|
||||
int offset = getAttributeOffset(m_classAttribute);
|
||||
int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING];
|
||||
return m_strings.getString(value);
|
||||
}
|
||||
|
||||
public String getIdAttribute() {
|
||||
if (m_idAttribute == -1) {
|
||||
return null;
|
||||
}
|
||||
int offset = getAttributeOffset(m_idAttribute);
|
||||
int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING];
|
||||
return m_strings.getString(value);
|
||||
}
|
||||
|
||||
public int getIdAttributeResourceValue(int defaultValue) {
|
||||
if (m_idAttribute == -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
int offset = getAttributeOffset(m_idAttribute);
|
||||
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
|
||||
if (valueType != TypedValue.TYPE_REFERENCE) {
|
||||
return defaultValue;
|
||||
}
|
||||
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
|
||||
}
|
||||
|
||||
public int getStyleAttribute() {
|
||||
if (m_styleAttribute == -1) {
|
||||
return 0;
|
||||
}
|
||||
int offset = getAttributeOffset(m_styleAttribute);
|
||||
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
|
||||
}
|
||||
|
||||
public int getAttributeCount() {
|
||||
if (m_event != START_TAG) {
|
||||
return -1;
|
||||
}
|
||||
return m_attributes.length / ATTRIBUTE_LENGHT;
|
||||
}
|
||||
|
||||
public String getAttributeNamespace(int index) {
|
||||
int offset = getAttributeOffset(index);
|
||||
int namespace = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI];
|
||||
if (namespace == -1) {
|
||||
return "";
|
||||
}
|
||||
return m_strings.getString(namespace);
|
||||
}
|
||||
|
||||
public String getAttributePrefix(int index) {
|
||||
int offset = getAttributeOffset(index);
|
||||
int uri = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI];
|
||||
int prefix = m_namespaces.findPrefix(uri);
|
||||
if (prefix == -1) {
|
||||
return "";
|
||||
}
|
||||
return m_strings.getString(prefix);
|
||||
}
|
||||
|
||||
public String getAttributeName(int index) {
|
||||
int offset = getAttributeOffset(index);
|
||||
int name = m_attributes[offset + ATTRIBUTE_IX_NAME];
|
||||
if (name == -1) {
|
||||
return "";
|
||||
}
|
||||
return m_strings.getString(name);
|
||||
}
|
||||
|
||||
public int getAttributeNameResource(int index) {
|
||||
int offset = getAttributeOffset(index);
|
||||
int name = m_attributes[offset + ATTRIBUTE_IX_NAME];
|
||||
if (m_resourceIDs == null
|
||||
|| name < 0 || name >= m_resourceIDs.length) {
|
||||
return 0;
|
||||
}
|
||||
return m_resourceIDs[name];
|
||||
}
|
||||
|
||||
public int getAttributeValueType(int index) {
|
||||
int offset = getAttributeOffset(index);
|
||||
return m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
|
||||
}
|
||||
|
||||
public int getAttributeValueData(int index) {
|
||||
int offset = getAttributeOffset(index);
|
||||
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
|
||||
}
|
||||
|
||||
public String getAttributeValue(int index) {
|
||||
int offset = getAttributeOffset(index);
|
||||
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
|
||||
if (valueType == TypedValue.TYPE_STRING) {
|
||||
int valueString = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING];
|
||||
return m_strings.getString(valueString);
|
||||
}
|
||||
int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
|
||||
return "";//TypedValue.coerceToString(valueType,valueData);
|
||||
}
|
||||
|
||||
public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
|
||||
return getAttributeIntValue(index, defaultValue ? 1 : 0) != 0;
|
||||
}
|
||||
|
||||
public float getAttributeFloatValue(int index, float defaultValue) {
|
||||
int offset = getAttributeOffset(index);
|
||||
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
|
||||
if (valueType == TypedValue.TYPE_FLOAT) {
|
||||
int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
|
||||
return Float.intBitsToFloat(valueData);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public int getAttributeIntValue(int index, int defaultValue) {
|
||||
int offset = getAttributeOffset(index);
|
||||
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
|
||||
if (valueType >= TypedValue.TYPE_FIRST_INT
|
||||
&& valueType <= TypedValue.TYPE_LAST_INT) {
|
||||
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public int getAttributeUnsignedIntValue(int index, int defaultValue) {
|
||||
return getAttributeIntValue(index, defaultValue);
|
||||
}
|
||||
|
||||
public int getAttributeResourceValue(int index, int defaultValue) {
|
||||
int offset = getAttributeOffset(index);
|
||||
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
|
||||
if (valueType == TypedValue.TYPE_REFERENCE) {
|
||||
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public String getAttributeValue(String namespace, String attribute) {
|
||||
int index = findAttribute(namespace, attribute);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
return getAttributeValue(index);
|
||||
}
|
||||
|
||||
public boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue) {
|
||||
int index = findAttribute(namespace, attribute);
|
||||
if (index == -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getAttributeBooleanValue(index, defaultValue);
|
||||
}
|
||||
|
||||
public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) {
|
||||
int index = findAttribute(namespace, attribute);
|
||||
if (index == -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getAttributeFloatValue(index, defaultValue);
|
||||
}
|
||||
|
||||
public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
|
||||
int index = findAttribute(namespace, attribute);
|
||||
if (index == -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getAttributeIntValue(index, defaultValue);
|
||||
}
|
||||
|
||||
public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) {
|
||||
int index = findAttribute(namespace, attribute);
|
||||
if (index == -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getAttributeUnsignedIntValue(index, defaultValue);
|
||||
}
|
||||
|
||||
public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
|
||||
int index = findAttribute(namespace, attribute);
|
||||
if (index == -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getAttributeResourceValue(index, defaultValue);
|
||||
}
|
||||
|
||||
public int getAttributeListValue(int index, String[] options, int defaultValue) {
|
||||
// TODO implement
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue) {
|
||||
// TODO implement
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getAttributeType(int index) {
|
||||
return "CDATA";
|
||||
}
|
||||
|
||||
public boolean isAttributeDefault(int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////////////// dummies
|
||||
public void setInput(InputStream stream, String inputEncoding) throws XmlPullParserException {
|
||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
public void setInput(Reader reader) throws XmlPullParserException {
|
||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
public String getInputEncoding() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getColumnNumber() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean isEmptyElementTag() throws XmlPullParserException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isWhitespace() throws XmlPullParserException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException {
|
||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
public String getNamespace(String prefix) {
|
||||
throw new RuntimeException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
public Object getProperty(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setProperty(String name, Object value) throws XmlPullParserException {
|
||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
public boolean getFeature(String feature) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setFeature(String name, boolean value) throws XmlPullParserException {
|
||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////// implementation
|
||||
/**
|
||||
* Namespace stack, holds prefix+uri pairs, as well as
|
||||
* depth information.
|
||||
* All information is stored in one int[] array.
|
||||
* Array consists of depth frames:
|
||||
* Data=DepthFrame*;
|
||||
* DepthFrame=Count+[Prefix+Uri]*+Count;
|
||||
* Count='count of Prefix+Uri pairs';
|
||||
* Yes, count is stored twice, to enable bottom-up traversal.
|
||||
* increaseDepth adds depth frame, decreaseDepth removes it.
|
||||
* push/pop operations operate only in current depth frame.
|
||||
* decreaseDepth removes any remaining (not pop'ed) namespace pairs.
|
||||
* findXXX methods search all depth frames starting
|
||||
* from the last namespace pair of current depth frame.
|
||||
* All functions that operate with int, use -1 as 'invalid value'.
|
||||
*
|
||||
* !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !!
|
||||
*
|
||||
*/
|
||||
private static final class NamespaceStack {
|
||||
|
||||
public NamespaceStack() {
|
||||
m_data = new int[32];
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
m_dataLength = 0;
|
||||
m_count = 0;
|
||||
m_depth = 0;
|
||||
}
|
||||
|
||||
public int getTotalCount() {
|
||||
return m_count;
|
||||
}
|
||||
|
||||
public int getCurrentCount() {
|
||||
if (m_dataLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
int offset = m_dataLength - 1;
|
||||
return m_data[offset];
|
||||
}
|
||||
|
||||
public int getAccumulatedCount(int depth) {
|
||||
if (m_dataLength == 0 || depth < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (depth > m_depth) {
|
||||
depth = m_depth;
|
||||
}
|
||||
int accumulatedCount = 0;
|
||||
int offset = 0;
|
||||
for (; depth > 0; --depth) {
|
||||
int count = m_data[offset];
|
||||
accumulatedCount += count;
|
||||
offset += (2 + count * 2);
|
||||
}
|
||||
return accumulatedCount;
|
||||
}
|
||||
|
||||
public void push(int prefix, int uri) {
|
||||
if (m_depth == 0) {
|
||||
increaseDepth();
|
||||
}
|
||||
ensureDataCapacity(2);
|
||||
int offset = m_dataLength - 1;
|
||||
int count = m_data[offset];
|
||||
m_data[offset - 1 - count * 2] = count + 1;
|
||||
m_data[offset] = prefix;
|
||||
m_data[offset + 1] = uri;
|
||||
m_data[offset + 2] = count + 1;
|
||||
m_dataLength += 2;
|
||||
m_count += 1;
|
||||
}
|
||||
|
||||
public boolean pop(int prefix, int uri) {
|
||||
if (m_dataLength == 0) {
|
||||
return false;
|
||||
}
|
||||
int offset = m_dataLength - 1;
|
||||
int count = m_data[offset];
|
||||
for (int i = 0, o = offset - 2; i != count; ++i, o -= 2) {
|
||||
if (m_data[o] != prefix || m_data[o + 1] != uri) {
|
||||
continue;
|
||||
}
|
||||
count -= 1;
|
||||
if (i == 0) {
|
||||
m_data[o] = count;
|
||||
o -= (1 + count * 2);
|
||||
m_data[o] = count;
|
||||
} else {
|
||||
m_data[offset] = count;
|
||||
offset -= (1 + 2 + count * 2);
|
||||
m_data[offset] = count;
|
||||
System.arraycopy(
|
||||
m_data, o + 2,
|
||||
m_data, o,
|
||||
m_dataLength - o);
|
||||
}
|
||||
m_dataLength -= 2;
|
||||
m_count -= 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean pop() {
|
||||
if (m_dataLength == 0) {
|
||||
return false;
|
||||
}
|
||||
int offset = m_dataLength - 1;
|
||||
int count = m_data[offset];
|
||||
if (count == 0) {
|
||||
return false;
|
||||
}
|
||||
count -= 1;
|
||||
offset -= 2;
|
||||
m_data[offset] = count;
|
||||
offset -= (1 + count * 2);
|
||||
m_data[offset] = count;
|
||||
m_dataLength -= 2;
|
||||
m_count -= 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getPrefix(int index) {
|
||||
return get(index, true);
|
||||
}
|
||||
|
||||
public int getUri(int index) {
|
||||
return get(index, false);
|
||||
}
|
||||
|
||||
public int findPrefix(int uri) {
|
||||
return find(uri, false);
|
||||
}
|
||||
|
||||
public int findUri(int prefix) {
|
||||
return find(prefix, true);
|
||||
}
|
||||
|
||||
public int getDepth() {
|
||||
return m_depth;
|
||||
}
|
||||
|
||||
public void increaseDepth() {
|
||||
ensureDataCapacity(2);
|
||||
int offset = m_dataLength;
|
||||
m_data[offset] = 0;
|
||||
m_data[offset + 1] = 0;
|
||||
m_dataLength += 2;
|
||||
m_depth += 1;
|
||||
}
|
||||
|
||||
public void decreaseDepth() {
|
||||
if (m_dataLength == 0) {
|
||||
return;
|
||||
}
|
||||
int offset = m_dataLength - 1;
|
||||
int count = m_data[offset];
|
||||
if ((offset - 1 - count * 2) == 0) {
|
||||
return;
|
||||
}
|
||||
m_dataLength -= 2 + count * 2;
|
||||
m_count -= count;
|
||||
m_depth -= 1;
|
||||
}
|
||||
|
||||
private void ensureDataCapacity(int capacity) {
|
||||
int available = (m_data.length - m_dataLength);
|
||||
if (available > capacity) {
|
||||
return;
|
||||
}
|
||||
int newLength = (m_data.length + available) * 2;
|
||||
int[] newData = new int[newLength];
|
||||
System.arraycopy(m_data, 0, newData, 0, m_dataLength);
|
||||
m_data = newData;
|
||||
}
|
||||
|
||||
private int find(int prefixOrUri, boolean prefix) {
|
||||
if (m_dataLength == 0) {
|
||||
return -1;
|
||||
}
|
||||
int offset = m_dataLength - 1;
|
||||
for (int i = m_depth; i != 0; --i) {
|
||||
int count = m_data[offset];
|
||||
offset -= 2;
|
||||
for (; count != 0; --count) {
|
||||
if (prefix) {
|
||||
if (m_data[offset] == prefixOrUri) {
|
||||
return m_data[offset + 1];
|
||||
}
|
||||
} else {
|
||||
if (m_data[offset + 1] == prefixOrUri) {
|
||||
return m_data[offset];
|
||||
}
|
||||
}
|
||||
offset -= 2;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int get(int index, boolean prefix) {
|
||||
if (m_dataLength == 0 || index < 0) {
|
||||
return -1;
|
||||
}
|
||||
int offset = 0;
|
||||
for (int i = m_depth; i != 0; --i) {
|
||||
int count = m_data[offset];
|
||||
if (index >= count) {
|
||||
index -= count;
|
||||
offset += (2 + count * 2);
|
||||
continue;
|
||||
}
|
||||
offset += (1 + index * 2);
|
||||
if (!prefix) {
|
||||
offset += 1;
|
||||
}
|
||||
return m_data[offset];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
private int[] m_data;
|
||||
private int m_dataLength;
|
||||
private int m_count;
|
||||
private int m_depth;
|
||||
}
|
||||
|
||||
/////////////////////////////////// package-visible
|
||||
// final void fetchAttributes(int[] styleableIDs,TypedArray result) {
|
||||
// result.resetIndices();
|
||||
// if (m_attributes==null || m_resourceIDs==null) {
|
||||
// return;
|
||||
// }
|
||||
// boolean needStrings=false;
|
||||
// for (int i=0,e=styleableIDs.length;i!=e;++i) {
|
||||
// int id=styleableIDs[i];
|
||||
// for (int o=0;o!=m_attributes.length;o+=ATTRIBUTE_LENGHT) {
|
||||
// int name=m_attributes[o+ATTRIBUTE_IX_NAME];
|
||||
// if (name>=m_resourceIDs.length ||
|
||||
// m_resourceIDs[name]!=id)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// int valueType=m_attributes[o+ATTRIBUTE_IX_VALUE_TYPE];
|
||||
// int valueData;
|
||||
// int assetCookie;
|
||||
// if (valueType==TypedValue.TYPE_STRING) {
|
||||
// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_STRING];
|
||||
// assetCookie=-1;
|
||||
// needStrings=true;
|
||||
// } else {
|
||||
// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_DATA];
|
||||
// assetCookie=0;
|
||||
// }
|
||||
// result.addValue(i,valueType,valueData,assetCookie,id,0);
|
||||
// }
|
||||
// }
|
||||
// if (needStrings) {
|
||||
// result.setStrings(m_strings);
|
||||
// }
|
||||
// }
|
||||
final StringBlock getStrings() {
|
||||
return m_strings;
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
private int getAttributeOffset(int index) {
|
||||
if (m_event != START_TAG) {
|
||||
throw new IndexOutOfBoundsException("Current event is not START_TAG.");
|
||||
}
|
||||
int offset = index * 5;
|
||||
if (offset >= m_attributes.length) {
|
||||
throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ").");
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private int findAttribute(String namespace, String attribute) {
|
||||
if (m_strings == null || attribute == null) {
|
||||
return -1;
|
||||
}
|
||||
int name = m_strings.find(attribute);
|
||||
if (name == -1) {
|
||||
return -1;
|
||||
}
|
||||
int uri = (namespace != null)
|
||||
? m_strings.find(namespace)
|
||||
: -1;
|
||||
for (int o = 0; o != m_attributes.length; ++o) {
|
||||
if (name == m_attributes[o + ATTRIBUTE_IX_NAME]
|
||||
&& (uri == -1 || uri == m_attributes[o + ATTRIBUTE_IX_NAMESPACE_URI])) {
|
||||
return o / ATTRIBUTE_LENGHT;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void resetEventInfo() {
|
||||
m_event = -1;
|
||||
m_lineNumber = -1;
|
||||
m_name = -1;
|
||||
m_namespaceUri = -1;
|
||||
m_attributes = null;
|
||||
m_idAttribute = -1;
|
||||
m_classAttribute = -1;
|
||||
m_styleAttribute = -1;
|
||||
}
|
||||
|
||||
private void doNext() throws IOException {
|
||||
// Delayed initialization.
|
||||
if (m_strings == null) {
|
||||
ChunkUtil.readCheckType(m_reader, CHUNK_AXML_FILE);
|
||||
/*chunkSize*/ m_reader.skipInt();
|
||||
m_strings = StringBlock.read(m_reader);
|
||||
m_namespaces.increaseDepth();
|
||||
m_operational = true;
|
||||
}
|
||||
|
||||
if (m_event == END_DOCUMENT) {
|
||||
return;
|
||||
}
|
||||
|
||||
int event = m_event;
|
||||
resetEventInfo();
|
||||
|
||||
while (true) {
|
||||
if (m_decreaseDepth) {
|
||||
m_decreaseDepth = false;
|
||||
m_namespaces.decreaseDepth();
|
||||
}
|
||||
|
||||
// Fake END_DOCUMENT event.
|
||||
if (event == END_TAG
|
||||
&& m_namespaces.getDepth() == 1
|
||||
&& m_namespaces.getCurrentCount() == 0) {
|
||||
m_event = END_DOCUMENT;
|
||||
break;
|
||||
}
|
||||
|
||||
int chunkType;
|
||||
if (event == START_DOCUMENT) {
|
||||
// Fake event, see CHUNK_XML_START_TAG handler.
|
||||
chunkType = CHUNK_XML_START_TAG;
|
||||
} else {
|
||||
chunkType = m_reader.readInt();
|
||||
}
|
||||
|
||||
if (chunkType == CHUNK_RESOURCEIDS) {
|
||||
int chunkSize = m_reader.readInt();
|
||||
if (chunkSize < 8 || (chunkSize % 4) != 0) {
|
||||
throw new IOException("Invalid resource ids size (" + chunkSize + ").");
|
||||
}
|
||||
m_resourceIDs = m_reader.readIntArray(chunkSize / 4 - 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chunkType < CHUNK_XML_FIRST || chunkType > CHUNK_XML_LAST) {
|
||||
throw new IOException("Invalid chunk type (" + chunkType + ").");
|
||||
}
|
||||
|
||||
// Fake START_DOCUMENT event.
|
||||
if (chunkType == CHUNK_XML_START_TAG && event == -1) {
|
||||
m_event = START_DOCUMENT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Common header.
|
||||
/*chunkSize*/ m_reader.skipInt();
|
||||
int lineNumber = m_reader.readInt();
|
||||
/*0xFFFFFFFF*/ m_reader.skipInt();
|
||||
|
||||
if (chunkType == CHUNK_XML_START_NAMESPACE
|
||||
|| chunkType == CHUNK_XML_END_NAMESPACE) {
|
||||
if (chunkType == CHUNK_XML_START_NAMESPACE) {
|
||||
int prefix = m_reader.readInt();
|
||||
int uri = m_reader.readInt();
|
||||
m_namespaces.push(prefix, uri);
|
||||
} else {
|
||||
/*prefix*/ m_reader.skipInt();
|
||||
/*uri*/ m_reader.skipInt();
|
||||
m_namespaces.pop();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
m_lineNumber = lineNumber;
|
||||
|
||||
if (chunkType == CHUNK_XML_START_TAG) {
|
||||
m_namespaceUri = m_reader.readInt();
|
||||
m_name = m_reader.readInt();
|
||||
/*flags?*/ m_reader.skipInt();
|
||||
int attributeCount = m_reader.readInt();
|
||||
m_idAttribute = (attributeCount >>> 16) - 1;
|
||||
attributeCount &= 0xFFFF;
|
||||
m_classAttribute = m_reader.readInt();
|
||||
m_styleAttribute = (m_classAttribute >>> 16) - 1;
|
||||
m_classAttribute = (m_classAttribute & 0xFFFF) - 1;
|
||||
m_attributes = m_reader.readIntArray(attributeCount * ATTRIBUTE_LENGHT);
|
||||
for (int i = ATTRIBUTE_IX_VALUE_TYPE; i < m_attributes.length;) {
|
||||
m_attributes[i] = (m_attributes[i] >>> 24);
|
||||
i += ATTRIBUTE_LENGHT;
|
||||
}
|
||||
m_namespaces.increaseDepth();
|
||||
m_event = START_TAG;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chunkType == CHUNK_XML_END_TAG) {
|
||||
m_namespaceUri = m_reader.readInt();
|
||||
m_name = m_reader.readInt();
|
||||
m_event = END_TAG;
|
||||
m_decreaseDepth = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chunkType == CHUNK_XML_TEXT) {
|
||||
m_name = m_reader.readInt();
|
||||
/*?*/ m_reader.skipInt();
|
||||
/*?*/ m_reader.skipInt();
|
||||
m_event = TEXT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/////////////////////////////////// data
|
||||
/*
|
||||
* All values are essentially indices, e.g. m_name is
|
||||
* an index of name in m_strings.
|
||||
*/
|
||||
private IntReader m_reader;
|
||||
private boolean m_operational = false;
|
||||
private StringBlock m_strings;
|
||||
private int[] m_resourceIDs;
|
||||
private NamespaceStack m_namespaces = new NamespaceStack();
|
||||
private boolean m_decreaseDepth;
|
||||
private int m_event;
|
||||
private int m_lineNumber;
|
||||
private int m_name;
|
||||
private int m_namespaceUri;
|
||||
private int[] m_attributes;
|
||||
private int m_idAttribute;
|
||||
private int m_classAttribute;
|
||||
private int m_styleAttribute;
|
||||
private static final String E_NOT_SUPPORTED = "Method is not supported.";
|
||||
private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0,
|
||||
ATTRIBUTE_IX_NAME = 1,
|
||||
ATTRIBUTE_IX_VALUE_STRING = 2,
|
||||
ATTRIBUTE_IX_VALUE_TYPE = 3,
|
||||
ATTRIBUTE_IX_VALUE_DATA = 4,
|
||||
ATTRIBUTE_LENGHT = 5;
|
||||
private static final int CHUNK_AXML_FILE = 0x00080003,
|
||||
CHUNK_RESOURCEIDS = 0x00080180,
|
||||
CHUNK_XML_FIRST = 0x00100100,
|
||||
CHUNK_XML_START_NAMESPACE = 0x00100100,
|
||||
CHUNK_XML_END_NAMESPACE = 0x00100101,
|
||||
CHUNK_XML_START_TAG = 0x00100102,
|
||||
CHUNK_XML_END_TAG = 0x00100103,
|
||||
CHUNK_XML_TEXT = 0x00100104,
|
||||
CHUNK_XML_LAST = 0x00100104;
|
||||
}
|
34
src/com/googlecode/android4me/res/ChunkUtil.java
Normal file
34
src/com/googlecode/android4me/res/ChunkUtil.java
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.android4me.res;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
*/
|
||||
class ChunkUtil {
|
||||
|
||||
public static void readCheckType(IntReader reader, int expectedType) throws IOException {
|
||||
int type = reader.readInt();
|
||||
if (type != expectedType) {
|
||||
throw new IOException(
|
||||
"Expected chunk of type 0x" + Integer.toHexString(expectedType)
|
||||
+ ", read 0x" + Integer.toHexString(type) + ".");
|
||||
}
|
||||
}
|
||||
}
|
156
src/com/googlecode/android4me/res/IntReader.java
Normal file
156
src/com/googlecode/android4me/res/IntReader.java
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.android4me.res;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
* Simple helper class that allows reading of integers.
|
||||
*
|
||||
* TODO:
|
||||
* * implement buffering
|
||||
*
|
||||
*/
|
||||
public final class IntReader {
|
||||
|
||||
public IntReader() { }
|
||||
|
||||
public IntReader(InputStream stream, boolean bigEndian) {
|
||||
reset(stream, bigEndian);
|
||||
}
|
||||
|
||||
public void reset(InputStream stream, boolean bigEndian) {
|
||||
m_stream = stream;
|
||||
m_bigEndian = bigEndian;
|
||||
m_position = 0;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (m_stream == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
m_stream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
reset(null, false);
|
||||
}
|
||||
|
||||
public InputStream getStream() {
|
||||
return m_stream;
|
||||
}
|
||||
|
||||
public boolean isBigEndian() {
|
||||
return m_bigEndian;
|
||||
}
|
||||
|
||||
public void setBigEndian(boolean bigEndian) {
|
||||
m_bigEndian = bigEndian;
|
||||
}
|
||||
|
||||
public int readByte() throws IOException {
|
||||
return readInt(1);
|
||||
}
|
||||
|
||||
public int readShort() throws IOException {
|
||||
return readInt(2);
|
||||
}
|
||||
|
||||
public int readInt() throws IOException {
|
||||
return readInt(4);
|
||||
}
|
||||
|
||||
public int readInt(int length) throws IOException {
|
||||
if (length < 0 || length > 4) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int result = 0;
|
||||
if (m_bigEndian) {
|
||||
for (int i = (length - 1) * 8; i >= 0; i -= 8) {
|
||||
int b = m_stream.read();
|
||||
if (b == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
m_position += 1;
|
||||
result |= (b << i);
|
||||
}
|
||||
} else {
|
||||
length *= 8;
|
||||
for (int i = 0; i < length; i += 8) {
|
||||
int b = m_stream.read();
|
||||
if (b == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
m_position += 1;
|
||||
result |= (b << i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int[] readIntArray(int length) throws IOException {
|
||||
int[] array = new int[length];
|
||||
readIntArray(array, 0, length);
|
||||
return array;
|
||||
}
|
||||
|
||||
public void readIntArray(int[] array, int offset, int length) throws IOException {
|
||||
for (; length > 0; length -= 1) {
|
||||
array[offset++] = readInt();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] readByteArray(int length) throws IOException {
|
||||
byte[] array = new byte[length];
|
||||
int read = m_stream.read(array);
|
||||
m_position += read;
|
||||
if (read != length) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public void skip(int bytes) throws IOException {
|
||||
if (bytes <= 0) {
|
||||
return;
|
||||
}
|
||||
long skipped = m_stream.skip(bytes);
|
||||
m_position += skipped;
|
||||
if (skipped != bytes) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
public void skipInt() throws IOException {
|
||||
skip(4);
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
return m_stream.available();
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return m_position;
|
||||
}
|
||||
|
||||
private InputStream m_stream;
|
||||
private boolean m_bigEndian;
|
||||
private int m_position;
|
||||
}
|
245
src/com/googlecode/android4me/res/StringBlock.java
Normal file
245
src/com/googlecode/android4me/res/StringBlock.java
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.android4me.res;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
* Block of strings, used in binary xml and arsc.
|
||||
*
|
||||
* TODO:
|
||||
* - implement get()
|
||||
*
|
||||
*/
|
||||
public class StringBlock {
|
||||
|
||||
/*
|
||||
* Reads whole (including chunk type) string block from stream.
|
||||
* Stream must be at the chunk type.
|
||||
*/
|
||||
public static StringBlock read(IntReader reader) throws IOException {
|
||||
ChunkUtil.readCheckType(reader, CHUNK_TYPE);
|
||||
int chunkSize = reader.readInt();
|
||||
int stringCount = reader.readInt();
|
||||
int styleOffsetCount = reader.readInt();
|
||||
/*?*/ reader.readInt();
|
||||
int stringsOffset = reader.readInt();
|
||||
int stylesOffset = reader.readInt();
|
||||
|
||||
StringBlock block = new StringBlock();
|
||||
block.m_stringOffsets = reader.readIntArray(stringCount);
|
||||
if (styleOffsetCount != 0) {
|
||||
block.m_styleOffsets = reader.readIntArray(styleOffsetCount);
|
||||
}
|
||||
{
|
||||
int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) - stringsOffset;
|
||||
if ((size % 4) != 0) {
|
||||
throw new IOException("String data size is not multiple of 4 (" + size + ").");
|
||||
}
|
||||
block.m_strings = reader.readIntArray(size / 4);
|
||||
}
|
||||
if (stylesOffset != 0) {
|
||||
int size = (chunkSize - stylesOffset);
|
||||
if ((size % 4) != 0) {
|
||||
throw new IOException("Style data size is not multiple of 4 (" + size + ").");
|
||||
}
|
||||
block.m_styles = reader.readIntArray(size / 4);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns number of strings in block.
|
||||
*/
|
||||
public int getCount() {
|
||||
return m_stringOffsets != null
|
||||
? m_stringOffsets.length
|
||||
: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns raw string (without any styling information) at specified index.
|
||||
*/
|
||||
public String getString(int index) {
|
||||
if (index < 0
|
||||
|| m_stringOffsets == null
|
||||
|| index >= m_stringOffsets.length) {
|
||||
return null;
|
||||
}
|
||||
int offset = m_stringOffsets[index];
|
||||
int length = getShort(m_strings, offset);
|
||||
StringBuilder result = new StringBuilder(length);
|
||||
for (; length > 0; length -= 1) {
|
||||
offset += 2;
|
||||
if ( (offset / 4) < m_strings.length) {
|
||||
result.append((char) getShort(m_strings, offset));
|
||||
}
|
||||
}
|
||||
|
||||
//return utf16toAscii(result.toString());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Not yet implemented.
|
||||
*
|
||||
* Returns string with style information (if any).
|
||||
*/
|
||||
public CharSequence get(int index) {
|
||||
return getString(index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns string with style tags (html-like).
|
||||
*/
|
||||
public String getHTML(int index) {
|
||||
String raw = getString(index);
|
||||
if (raw == null) {
|
||||
return raw;
|
||||
}
|
||||
int[] style = getStyle(index);
|
||||
if (style == null) {
|
||||
return raw;
|
||||
}
|
||||
StringBuilder html = new StringBuilder(raw.length() + 32);
|
||||
int offset = 0;
|
||||
while (true) {
|
||||
int i = -1;
|
||||
for (int j = 0; j < style.length; j += 3) {
|
||||
if (style[j + 1] == -1) {
|
||||
continue;
|
||||
}
|
||||
if (i == -1 || style[i + 1] > style[j + 1]) {
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
int start = ((i != -1) ? style[i + 1] : raw.length());
|
||||
for (int j = 0; j < style.length; j += 3) {
|
||||
int end = style[j + 2];
|
||||
if (end == -1 || end >= start) {
|
||||
continue;
|
||||
}
|
||||
if (offset <= end) {
|
||||
html.append(raw, offset, end + 1);
|
||||
offset = end + 1;
|
||||
}
|
||||
style[j + 2] = -1;
|
||||
html.append('<');
|
||||
html.append('/');
|
||||
html.append(getString(style[j]));
|
||||
html.append('>');
|
||||
}
|
||||
if (offset < start) {
|
||||
html.append(raw, offset, start);
|
||||
offset = start;
|
||||
}
|
||||
if (i == -1) {
|
||||
break;
|
||||
}
|
||||
html.append('<');
|
||||
html.append(getString(style[i]));
|
||||
html.append('>');
|
||||
style[i + 1] = -1;
|
||||
}
|
||||
return html.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds index of the string.
|
||||
* Returns -1 if the string was not found.
|
||||
*/
|
||||
public int find(String string) {
|
||||
if (string == null) {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < m_stringOffsets.length; i++) {
|
||||
int offset = m_stringOffsets[i];
|
||||
int length = getShort(m_strings, offset);
|
||||
if (length != string.length()) {
|
||||
continue;
|
||||
}
|
||||
int j = 0;
|
||||
for (; j < length; ++j) {
|
||||
offset += 2;
|
||||
if (string.charAt(j) != getShort(m_strings, offset)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == length) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////// implementation
|
||||
private StringBlock() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns style information - array of int triplets,
|
||||
* where in each triplet:
|
||||
* * first int is index of tag name ('b','i', etc.)
|
||||
* * second int is tag start index in string
|
||||
* * third int is tag end index in string
|
||||
*/
|
||||
private int[] getStyle(int index) {
|
||||
if (m_styleOffsets == null || m_styles == null
|
||||
|| index >= m_styleOffsets.length) {
|
||||
return null;
|
||||
}
|
||||
int offset = m_styleOffsets[index] / 4;
|
||||
int style[];
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = offset; i < m_styles.length; ++i) {
|
||||
if (m_styles[i] == -1) {
|
||||
break;
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
if (count == 0 || (count % 3) != 0) {
|
||||
return null;
|
||||
}
|
||||
style = new int[count];
|
||||
}
|
||||
for (int i = offset, j = 0; i < m_styles.length;) {
|
||||
if (m_styles[i] == -1) {
|
||||
break;
|
||||
}
|
||||
style[j++] = m_styles[i++];
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
private static int getShort(int[] array, int offset) {
|
||||
int value = array[offset / 4];
|
||||
if ((offset % 4) / 2 == 0) {
|
||||
return (value & 0xFFFF);
|
||||
} else {
|
||||
return (value >>> 16);
|
||||
}
|
||||
}
|
||||
|
||||
private int[] m_stringOffsets;
|
||||
private int[] m_strings;
|
||||
private int[] m_styleOffsets;
|
||||
private int[] m_styles;
|
||||
private static final int CHUNK_TYPE = 0x001C0001;
|
||||
}
|
28
src/com/googlecode/android4me/res/XmlResourceParser.java
Normal file
28
src/com/googlecode/android4me/res/XmlResourceParser.java
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.android4me.res;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import com.googlecode.android4me.util.AttributeSet;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
*/
|
||||
public interface XmlResourceParser extends XmlPullParser, AttributeSet {
|
||||
|
||||
void close();
|
||||
}
|
72
src/com/googlecode/android4me/util/AttributeSet.java
Normal file
72
src/com/googlecode/android4me/util/AttributeSet.java
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.android4me.util;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
*/
|
||||
public interface AttributeSet {
|
||||
|
||||
int getAttributeCount();
|
||||
|
||||
String getAttributeName(int index);
|
||||
|
||||
String getAttributeValue(int index);
|
||||
|
||||
String getPositionDescription();
|
||||
|
||||
int getAttributeNameResource(int index);
|
||||
|
||||
int getAttributeListValue(int index, String options[], int defaultValue);
|
||||
|
||||
boolean getAttributeBooleanValue(int index, boolean defaultValue);
|
||||
|
||||
int getAttributeResourceValue(int index, int defaultValue);
|
||||
|
||||
int getAttributeIntValue(int index, int defaultValue);
|
||||
|
||||
int getAttributeUnsignedIntValue(int index, int defaultValue);
|
||||
|
||||
float getAttributeFloatValue(int index, float defaultValue);
|
||||
|
||||
String getIdAttribute();
|
||||
|
||||
String getClassAttribute();
|
||||
|
||||
int getIdAttributeResourceValue(int index);
|
||||
|
||||
int getStyleAttribute();
|
||||
|
||||
String getAttributeValue(String namespace, String attribute);
|
||||
|
||||
int getAttributeListValue(String namespace, String attribute, String options[], int defaultValue);
|
||||
|
||||
boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue);
|
||||
|
||||
int getAttributeResourceValue(String namespace, String attribute, int defaultValue);
|
||||
|
||||
int getAttributeIntValue(String namespace, String attribute, int defaultValue);
|
||||
|
||||
int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue);
|
||||
|
||||
float getAttributeFloatValue(String namespace, String attribute, float defaultValue);
|
||||
|
||||
//TODO: remove
|
||||
int getAttributeValueType(int index);
|
||||
|
||||
int getAttributeValueData(int index);
|
||||
}
|
66
src/com/googlecode/android4me/util/TypedValue.java
Normal file
66
src/com/googlecode/android4me/util/TypedValue.java
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.android4me.util;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
*/
|
||||
public class TypedValue {
|
||||
|
||||
public int type;
|
||||
public CharSequence string;
|
||||
public int data;
|
||||
public int assetCookie;
|
||||
public int resourceId;
|
||||
public int changingConfigurations;
|
||||
public static final int TYPE_NULL = 0,
|
||||
TYPE_REFERENCE = 1,
|
||||
TYPE_ATTRIBUTE = 2,
|
||||
TYPE_STRING = 3,
|
||||
TYPE_FLOAT = 4,
|
||||
TYPE_DIMENSION = 5,
|
||||
TYPE_FRACTION = 6,
|
||||
TYPE_FIRST_INT = 16,
|
||||
TYPE_INT_DEC = 16,
|
||||
TYPE_INT_HEX = 17,
|
||||
TYPE_INT_BOOLEAN = 18,
|
||||
TYPE_FIRST_COLOR_INT = 28,
|
||||
TYPE_INT_COLOR_ARGB8 = 28,
|
||||
TYPE_INT_COLOR_RGB8 = 29,
|
||||
TYPE_INT_COLOR_ARGB4 = 30,
|
||||
TYPE_INT_COLOR_RGB4 = 31,
|
||||
TYPE_LAST_COLOR_INT = 31,
|
||||
TYPE_LAST_INT = 31;
|
||||
public static final int COMPLEX_UNIT_PX = 0,
|
||||
COMPLEX_UNIT_DIP = 1,
|
||||
COMPLEX_UNIT_SP = 2,
|
||||
COMPLEX_UNIT_PT = 3,
|
||||
COMPLEX_UNIT_IN = 4,
|
||||
COMPLEX_UNIT_MM = 5,
|
||||
COMPLEX_UNIT_SHIFT = 0,
|
||||
COMPLEX_UNIT_MASK = 15,
|
||||
COMPLEX_UNIT_FRACTION = 0,
|
||||
COMPLEX_UNIT_FRACTION_PARENT = 1,
|
||||
COMPLEX_RADIX_23p0 = 0,
|
||||
COMPLEX_RADIX_16p7 = 1,
|
||||
COMPLEX_RADIX_8p15 = 2,
|
||||
COMPLEX_RADIX_0p23 = 3,
|
||||
COMPLEX_RADIX_SHIFT = 4,
|
||||
COMPLEX_RADIX_MASK = 3,
|
||||
COMPLEX_MANTISSA_SHIFT = 8,
|
||||
COMPLEX_MANTISSA_MASK = 0xFFFFFF;
|
||||
}
|
Loading…
Reference in New Issue
Block a user