Initial commit

This commit is contained in:
aNNiMON 2024-02-14 00:23:12 +02:00
commit ce05364151
25 changed files with 2723 additions and 0 deletions

6
.netbeans.xml Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

13
res/values/strings.xml Normal file
View 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
View File

@ -0,0 +1,3 @@
<resources>
<style name="ApplicationTheme" parent="android:Theme.Light" />
</resources>

View 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", "", "", "", "", "", ""
};
}

View 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;
}
}

View 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 "";
}
}
}

View 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();
}
}

View 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
};
}

View 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;
}

View 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) + ".");
}
}
}

View 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;
}

View 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;
}

View 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();
}

View 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);
}

View 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;
}