diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /core/java/android/util | |
download | frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2 |
Initial Contribution
Diffstat (limited to 'core/java/android/util')
27 files changed, 3922 insertions, 0 deletions
diff --git a/core/java/android/util/AndroidException.java b/core/java/android/util/AndroidException.java new file mode 100644 index 0000000..a767ea1 --- /dev/null +++ b/core/java/android/util/AndroidException.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +/** + * Base class for all checked exceptions thrown by the Android frameworks. + */ +public class AndroidException extends Exception { + public AndroidException() { + } + + public AndroidException(String name) { + super(name); + } + + public AndroidException(Exception cause) { + super(cause); + } +}; + diff --git a/core/java/android/util/AndroidRuntimeException.java b/core/java/android/util/AndroidRuntimeException.java new file mode 100644 index 0000000..4ed17bc --- /dev/null +++ b/core/java/android/util/AndroidRuntimeException.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +/** + * Base class for all unchecked exceptions thrown by the Android frameworks. + */ +public class AndroidRuntimeException extends RuntimeException { + public AndroidRuntimeException() { + } + + public AndroidRuntimeException(String name) { + super(name); + } + + public AndroidRuntimeException(Exception cause) { + super(cause); + } +}; + diff --git a/core/java/android/util/AttributeSet.java b/core/java/android/util/AttributeSet.java new file mode 100644 index 0000000..01a7ad4 --- /dev/null +++ b/core/java/android/util/AttributeSet.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + + +/** + * A collection of attributes, as found associated with a tag in an XML + * document. Often you will not want to use this interface directly, instead + * passing it to {@link android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int) + * Resources.Theme.obtainStyledAttributes()} + * which will take care of parsing the attributes for you. In particular, + * the Resources API will convert resource references (attribute values such as + * "@string/my_label" in the original XML) to the desired type + * for you; if you use AttributeSet directly then you will need to manually + * check for resource references + * (with {@link #getAttributeResourceValue(int, int)}) and do the resource + * lookup yourself if needed. Direct use of AttributeSet also prevents the + * application of themes and styles when retrieving attribute values. + * + * <p>This interface provides an efficient mechanism for retrieving + * data from compiled XML files, which can be retrieved for a particular + * XmlPullParser through {@link Xml#asAttributeSet + * Xml.getAttributeSet()}. Normally this will return an implementation + * of the interface that works on top of a generic XmlPullParser, however it + * is more useful in conjunction with compiled XML resources: + * + * <pre> + * XmlPullParser parser = resources.getXml(myResouce); + * AttributeSet attributes = Xml.getAttributeSet(parser);</pre> + * + * <p>The implementation returned here, unlike using + * the implementation on top of a generic XmlPullParser, + * is highly optimized by retrieving pre-computed information that was + * generated by aapt when compiling your resources. For example, + * the {@link #getAttributeFloatValue(int, float)} method returns a floating + * point number previous stored in the compiled resource instead of parsing + * at runtime the string originally in the XML file. + * + * <p>This interface also provides additional information contained in the + * compiled XML resource that is not available in a normal XML file, such + * as {@link #getAttributeNameResource(int)} which returns the resource + * identifier associated with a particular XML attribute name. + */ +public interface AttributeSet { + public int getAttributeCount(); + public String getAttributeName(int index); + public String getAttributeValue(int index); + public String getAttributeValue(String namespace, String name); + public String getPositionDescription(); + + /** + * Return the resource ID associated with the given attribute name. This + * will be the identifier for an attribute resource, which can be used by + * styles. Returns 0 if there is no resource associated with this + * attribute. + * + * <p>Note that this is different than {@link #getAttributeResourceValue} + * in that it returns a resource identifier for the attribute name; the + * other method returns this attribute's value as a resource identifier. + * + * @param index Index of the desired attribute, 0...count-1. + * + * @return The resource identifier, 0 if none. + */ + public int getAttributeNameResource(int index); + + /** + * Return the index of the value of 'attribute' in the list 'options'. + * + * @param attribute Name of attribute to retrieve. + * @param options List of strings whose values we are checking against. + * @param defaultValue Value returned if attribute doesn't exist or no + * match is found. + * + * @return Index in to 'options' or defaultValue. + */ + public int getAttributeListValue(String namespace, String attribute, + String[] options, int defaultValue); + + /** + * Return the boolean value of 'attribute'. + * + * @param attribute The attribute to retrieve. + * @param defaultValue What to return if the attribute isn't found. + * + * @return Resulting value. + */ + public boolean getAttributeBooleanValue(String namespace, String attribute, + boolean defaultValue); + + /** + * Return the value of 'attribute' as a resource identifier. + * + * <p>Note that this is different than {@link #getAttributeNameResource} + * in that it returns a the value contained in this attribute as a + * resource identifier (i.e., a value originally of the form + * "@package:type/resource"); the other method returns a resource + * identifier that identifies the name of the attribute. + * + * @param attribute The attribute to retrieve. + * @param defaultValue What to return if the attribute isn't found. + * + * @return Resulting value. + */ + public int getAttributeResourceValue(String namespace, String attribute, + int defaultValue); + + /** + * Return the integer value of 'attribute'. + * + * @param attribute The attribute to retrieve. + * @param defaultValue What to return if the attribute isn't found. + * + * @return Resulting value. + */ + public int getAttributeIntValue(String namespace, String attribute, + int defaultValue); + + /** + * Return the boolean value of 'attribute' that is formatted as an + * unsigned value. In particular, the formats 0xn...n and #n...n are + * handled. + * + * @param attribute The attribute to retrieve. + * @param defaultValue What to return if the attribute isn't found. + * + * @return Resulting value. + */ + public int getAttributeUnsignedIntValue(String namespace, String attribute, + int defaultValue); + + /** + * Return the float value of 'attribute'. + * + * @param attribute The attribute to retrieve. + * @param defaultValue What to return if the attribute isn't found. + * + * @return Resulting value. + */ + public float getAttributeFloatValue(String namespace, String attribute, + float defaultValue); + + /** + * Return the index of the value of attribute at 'index' in the list + * 'options'. + * + * @param index Index of the desired attribute, 0...count-1. + * @param options List of strings whose values we are checking against. + * @param defaultValue Value returned if attribute doesn't exist or no + * match is found. + * + * @return Index in to 'options' or defaultValue. + */ + public int getAttributeListValue(int index, + String[] options, int defaultValue); + + /** + * Return the boolean value of attribute at 'index'. + * + * @param index Index of the desired attribute, 0...count-1. + * @param defaultValue What to return if the attribute isn't found. + * + * @return Resulting value. + */ + public boolean getAttributeBooleanValue(int index, + boolean defaultValue); + + /** + * Return the value of attribute at 'index' as a resource identifier. + * + * <p>Note that this is different than {@link #getAttributeNameResource} + * in that it returns a the value contained in this attribute as a + * resource identifier (i.e., a value originally of the form + * "@package:type/resource"); the other method returns a resource + * identifier that identifies the name of the attribute. + * + * @param index Index of the desired attribute, 0...count-1. + * @param defaultValue What to return if the attribute isn't found. + * + * @return Resulting value. + */ + public int getAttributeResourceValue(int index, + int defaultValue); + + /** + * Return the integer value of attribute at 'index'. + * + * @param index Index of the desired attribute, 0...count-1. + * @param defaultValue What to return if the attribute isn't found. + * + * @return Resulting value. + */ + public int getAttributeIntValue(int index, + int defaultValue); + + /** + * Return the integer value of attribute at 'index' that is formatted as an + * unsigned value. In particular, the formats 0xn...n and #n...n are + * handled. + * + * @param index Index of the desired attribute, 0...count-1. + * @param defaultValue What to return if the attribute isn't found. + * + * @return Resulting value. + */ + public int getAttributeUnsignedIntValue(int index, + int defaultValue); + + /** + * Return the float value of attribute at 'index'. + * + * @param index Index of the desired attribute, 0...count-1. + * @param defaultValue What to return if the attribute isn't found. + * + * @return Resulting value. + */ + public float getAttributeFloatValue(int index, + float defaultValue); + + /** + * Return the value of the "id" attribute or null if there is not one. + * Equivalent to getAttributeValue(null, "id"). + * + * @return The id attribute's value or null. + */ + public String getIdAttribute(); + + /** + * Return the value of the "class" attribute or null if there is not one. + * Equivalent to getAttributeValue(null, "class"). + * + * @return The class attribute's value or null. + */ + public String getClassAttribute(); + + /** + * Return the integer value of the "id" attribute or defaultValue if there + * is none. + * Equivalent to getAttributeResourceValue(null, "id", defaultValue); + * + * @param defaultValue What to return if the "id" attribute isn't found. + * @return int Resulting value. + */ + public int getIdAttributeResourceValue(int defaultValue); + + /** + + * Return the value of the "style" attribute or 0 if there is not one. + * Equivalent to getAttributeResourceValue(null, "style"). + * + * @return The style attribute's resource identifier or 0. + */ + public int getStyleAttribute(); +} + diff --git a/core/java/android/util/Config.java b/core/java/android/util/Config.java new file mode 100644 index 0000000..c0b27f8 --- /dev/null +++ b/core/java/android/util/Config.java @@ -0,0 +1,51 @@ +/* device/vmlibs-config/release/android/util/Config.java +** +** Copyright 2006, The Android Open Source Project +** +** 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 android.util; + +/** + * Build configuration. The constants in this class vary depending + * on release vs. debug build. This is the configuration for release builds. + * {@more} + */ +public final class Config +{ + /** + * Is this a release build? + */ + public static final boolean RELEASE = true; + + /** + * Is this a debug build? + */ + public static final boolean DEBUG = false; + + /** + * Is profiling enabled? + */ + public static final boolean PROFILE = false; + + /** + * Are VERBOSE log messages enabled? + */ + public static final boolean LOGV = false; + + /** + * Are DEBUG log messages enabled? + */ + public static final boolean LOGD = true; +} diff --git a/core/java/android/util/DayOfMonthCursor.java b/core/java/android/util/DayOfMonthCursor.java new file mode 100644 index 0000000..52ee00e --- /dev/null +++ b/core/java/android/util/DayOfMonthCursor.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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 android.util; + +/** + * Helps control and display a month view of a calendar that has a current + * selected day. + * <ul> + * <li>Keeps track of current month, day, year</li> + * <li>Keeps track of current cursor position (row, column)</li> + * <li>Provides methods to help display the calendar</li> + * <li>Provides methods to move the cursor up / down / left / right.</li> + * </ul> + * + * This should be used by anyone who presents a month view to users and wishes + * to behave consistently with other widgets and apps; if we ever change our + * mind about when to flip the month, we can change it here only. + * + * @hide + */ +public class DayOfMonthCursor extends MonthDisplayHelper { + + private int mRow; + private int mColumn; + + /** + * @param year The initial year. + * @param month The initial month. + * @param dayOfMonth The initial dayOfMonth. + * @param weekStartDay What dayOfMonth of the week the week should start, + * in terms of {@link java.util.Calendar} constants such as + * {@link java.util.Calendar#SUNDAY}. + */ + public DayOfMonthCursor(int year, int month, int dayOfMonth, int weekStartDay) { + super(year, month, weekStartDay); + mRow = getRowOf(dayOfMonth); + mColumn = getColumnOf(dayOfMonth); + } + + + public int getSelectedRow() { + return mRow; + } + + public int getSelectedColumn() { + return mColumn; + } + + public void setSelectedRowColumn(int row, int col) { + mRow = row; + mColumn = col; + } + + public int getSelectedDayOfMonth() { + return getDayAt(mRow, mColumn); + } + + public void setSelectedDayOfMonth(int dayOfMonth) { + mRow = getRowOf(dayOfMonth); + mColumn = getColumnOf(dayOfMonth); + } + + public boolean isSelected(int row, int column) { + return (mRow == row) && (mColumn == column); + } + + /** + * Move up one box, potentially flipping to the previous month. + * @return Whether the month was flipped to the previous month + * due to the move. + */ + public boolean up() { + if (isWithinCurrentMonth(mRow - 1, mColumn)) { + // within current month, just move up + mRow--; + return false; + } + // flip back to previous month, same column, first position within month + previousMonth(); + mRow = 5; + while(!isWithinCurrentMonth(mRow, mColumn)) { + mRow--; + } + return true; + } + + /** + * Move down one box, potentially flipping to the next month. + * @return Whether the month was flipped to the next month + * due to the move. + */ + public boolean down() { + if (isWithinCurrentMonth(mRow + 1, mColumn)) { + // within current month, just move down + mRow++; + return false; + } + // flip to next month, same column, first position within month + nextMonth(); + mRow = 0; + while (!isWithinCurrentMonth(mRow, mColumn)) { + mRow++; + } + return true; + } + + /** + * Move left one box, potentially flipping to the previous month. + * @return Whether the month was flipped to the previous month + * due to the move. + */ + public boolean left() { + if (mColumn == 0) { + mRow--; + mColumn = 6; + } else { + mColumn--; + } + + if (isWithinCurrentMonth(mRow, mColumn)) { + return false; + } + + // need to flip to last day of previous month + previousMonth(); + int lastDay = getNumberOfDaysInMonth(); + mRow = getRowOf(lastDay); + mColumn = getColumnOf(lastDay); + return true; + } + + /** + * Move right one box, potentially flipping to the next month. + * @return Whether the month was flipped to the next month + * due to the move. + */ + public boolean right() { + if (mColumn == 6) { + mRow++; + mColumn = 0; + } else { + mColumn++; + } + + if (isWithinCurrentMonth(mRow, mColumn)) { + return false; + } + + // need to flip to first day of next month + nextMonth(); + mRow = 0; + mColumn = 0; + while (!isWithinCurrentMonth(mRow, mColumn)) { + mColumn++; + } + return true; + } + +} diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java new file mode 100644 index 0000000..1c5d669 --- /dev/null +++ b/core/java/android/util/DebugUtils.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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 android.util; + +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +/** + * <p>Various utilities for debugging and logging.</p> + */ +public class DebugUtils { + /** + * <p>Filters objects against the <code>ANDROID_OBJECT_FILTER</code> + * environment variable. This environment variable can filter objects + * based on their class name and attribute values.</p> + * + * <p>Here is the syntax for <code>ANDROID_OBJECT_FILTER</code>:</p> + * + * <p><code>ClassName@attribute1=value1@attribute2=value2...</code></p> + * + * <p>Examples:</p> + * <ul> + * <li>Select TextView instances: <code>TextView</code></li> + * <li>Select TextView instances of text "Loading" and bottom offset of 22: + * <code>TextView@text=Loading.*@bottom=22</code></li> + * </ul> + * + * <p>The class name and the values are regular expressions.</p> + * + * <p>This class is useful for debugging and logging purpose:</p> + * <pre> + * if (Config.DEBUG) { + * if (DebugUtils.isObjectSelected(childView) && Config.LOGV) { + * Log.v(TAG, "Object " + childView + " logged!"); + * } + * } + * </pre> + * + * <p><strong>NOTE</strong>: This method is very expensive as it relies + * heavily on regular expressions and reflection. Calls to this method + * should always be stripped out of the release binaries and avoided + * as much as possible in debug mode.</p> + * + * @param object any object to match against the ANDROID_OBJECT_FILTER + * environement variable + * @return true if object is selected by the ANDROID_OBJECT_FILTER + * environment variable, false otherwise + */ + public static boolean isObjectSelected(Object object) { + boolean match = false; + String s = System.getenv("ANDROID_OBJECT_FILTER"); + if (s != null && s.length() > 0) { + String[] selectors = s.split("@"); + // first selector == class name + if (object.getClass().getSimpleName().matches(selectors[0])) { + // check potential attributes + for (int i = 1; i < selectors.length; i++) { + String[] pair = selectors[i].split("="); + Class<?> klass = object.getClass(); + try { + Method declaredMethod = null; + Class<?> parent = klass; + do { + declaredMethod = parent.getDeclaredMethod("get" + + pair[0].substring(0, 1).toUpperCase() + + pair[0].substring(1), + (Class[]) null); + } while ((parent = klass.getSuperclass()) != null && + declaredMethod == null); + + if (declaredMethod != null) { + Object value = declaredMethod + .invoke(object, (Object[])null); + match |= (value != null ? + value.toString() : "null").matches(pair[1]); + } + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + } + } + return match; + } + +} diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java new file mode 100644 index 0000000..8fc3602 --- /dev/null +++ b/core/java/android/util/DisplayMetrics.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + + +/** + * A structure describing general information about a display, such as its + * size, density, and font scaling. + */ +public class DisplayMetrics { + /** + * The absolute width of the display in pixels. + */ + public int widthPixels; + /** + * The absolute height of the display in pixels. + */ + public int heightPixels; + /** + * The logical density of the display. This is a scaling factor for the + * Density Independent Pixel unit, where one DIP is one pixel on an + * approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen), + * providing the baseline of the system's display. Thus on a 160dpi screen + * this density value will be 1; on a 106 dpi screen it would be .75; etc. + * + * <p>This value does not exactly follow the real screen size (as given by + * {@link #xdpi} and {@link #ydpi}, but rather is used to scale the size of + * the overall UI in steps based on gross changes in the display dpi. For + * example, a 240x320 screen will have a density of 1 even if its width is + * 1.8", 1.3", etc. However, if the screen resolution is increased to + * 320x480 but the screen size remained 1.5"x2" then the density would be + * increased (probably to 1.5). + */ + public float density; + /** + * A scaling factor for fonts displayed on the display. This is the same + * as {@link #density}, except that it may be adjusted in smaller + * increments at runtime based on a user preference for the font size. + */ + public float scaledDensity; + /** + * The exact physical pixels per inch of the screen in the X dimension. + */ + public float xdpi; + /** + * The exact physical pixels per inch of the screen in the Y dimension. + */ + public float ydpi; + + public DisplayMetrics() { + } + + public void setTo(DisplayMetrics o) { + widthPixels = o.widthPixels; + heightPixels = o.heightPixels; + density = o.density; + scaledDensity = o.scaledDensity; + xdpi = o.xdpi; + ydpi = o.ydpi; + } + + public void setToDefaults() { + widthPixels = 0; + heightPixels = 0; + density = 1; + scaledDensity = 1; + xdpi = 160; + ydpi = 160; + } +} + diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java new file mode 100644 index 0000000..24b4f73 --- /dev/null +++ b/core/java/android/util/EventLog.java @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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 android.util; + +import com.google.android.collect.Lists; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * {@hide} + * Dynamically defined (in terms of event types), space efficient (i.e. "tight") event logging + * to help instrument code for large scale stability and performance monitoring. + * + * Note that this class contains all static methods. This is done for efficiency reasons. + * + * Events for the event log are self-describing binary data structures. They start with a 20 byte + * header (generated automatically) which contains all of the following in order: + * + * <ul> + * <li> Payload length: 2 bytes - length of the non-header portion </li> + * <li> Padding: 2 bytes - no meaning at this time </li> + * <li> Timestamp: + * <ul> + * <li> Seconds: 4 bytes - seconds since Epoch </li> + * <li> Nanoseconds: 4 bytes - plus extra nanoseconds </li> + * </ul></li> + * <li> Process ID: 4 bytes - matching {@link android.os.Process#myPid} </li> + * <li> Thread ID: 4 bytes - matching {@link android.os.Process#myTid} </li> + * </li> + * </ul> + * + * The above is followed by a payload, comprised of the following: + * <ul> + * <li> Tag: 4 bytes - unique integer used to identify a particular event. This number is also + * used as a key to map to a string that can be displayed by log reading tools. + * </li> + * <li> Type: 1 byte - can be either {@link #INT}, {@link #LONG}, {@link #STRING}, + * or {@link #LIST}. </li> + * <li> Event log value: the size and format of which is one of: + * <ul> + * <li> INT: 4 bytes </li> + * <li> LONG: 8 bytes </li> + * <li> STRING: + * <ul> + * <li> Size of STRING: 4 bytes </li> + * <li> The string: n bytes as specified in the size fields above. </li> + * </ul></li> + * <li> {@link List LIST}: + * <ul> + * <li> Num items: 1 byte </li> + * <li> N value payloads, where N is the number of items specified above. </li> + * </ul></li> + * </ul> + * </li> + * <li> '\n': 1 byte - an automatically generated newline, used to help detect and recover from log + * corruption and enable stansard unix tools like grep, tail and wc to operate + * on event logs. </li> + * </ul> + * + * Note that all output is done in the endian-ness of the device (as determined + * by {@link ByteOrder#nativeOrder()}). + */ + +public class EventLog { + + // Value types + public static final byte INT = 0; + public static final byte LONG = 1; + public static final byte STRING = 2; + public static final byte LIST = 3; + + /** + * An immutable tuple used to log a heterogeneous set of loggable items. + * The items can be Integer, Long, String, or {@link List}. + * The maximum number of items is 127 + */ + public static final class List { + private Object[] mItems; + + /** + * Get a particular tuple item + * @param pos The position of the item in the tuple + */ + public final Object getItem(int pos) { + return mItems[pos]; + } + + /** + * Get the number of items in the tuple. + */ + public final byte getNumItems() { + return (byte) mItems.length; + } + + /** + * Create a new tuple. + * @param items The items to create the tuple with, as varargs. + * @throws IllegalArgumentException if the arguments are too few (0), + * too many, or aren't loggable types. + */ + public List(Object... items) throws IllegalArgumentException { + if (items.length > Byte.MAX_VALUE) { + throw new IllegalArgumentException( + "A List must have fewer than " + + Byte.MAX_VALUE + " items in it."); + } + if (items.length < 1) { + throw new IllegalArgumentException( + "A List must have at least one item in it."); + } + for (int i = 0; i < items.length; i++) { + final Object item = items[i]; + if (item == null) { + // Would be nice to be able to write null strings... + items[i] = ""; + } else if (!(item instanceof List || + item instanceof String || + item instanceof Integer || + item instanceof Long)) { + throw new IllegalArgumentException( + "Attempt to create a List with illegal item type."); + } + } + this.mItems = items; + } + } + + /** + * A previously logged event read from the logs. + */ + public static final class Event { + private final ByteBuffer mBuffer; + + // Layout of event log entry received from kernel. + private static final int LENGTH_OFFSET = 0; + private static final int PROCESS_OFFSET = 4; + private static final int THREAD_OFFSET = 8; + private static final int SECONDS_OFFSET = 12; + private static final int NANOSECONDS_OFFSET = 16; + + private static final int PAYLOAD_START = 20; + private static final int TAG_OFFSET = 20; + private static final int DATA_START = 24; + + /** @param data containing event, read from the system */ + public Event(byte[] data) { + mBuffer = ByteBuffer.wrap(data); + mBuffer.order(ByteOrder.nativeOrder()); + } + + public int getProcessId() { + return mBuffer.getInt(PROCESS_OFFSET); + } + + public int getThreadId() { + return mBuffer.getInt(THREAD_OFFSET); + } + + public long getTimeNanos() { + return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l + + mBuffer.getInt(NANOSECONDS_OFFSET); + } + + public int getTag() { + return mBuffer.getInt(TAG_OFFSET); + } + + /** @return one of Integer, Long, String, or List. */ + public synchronized Object getData() { + mBuffer.limit(PAYLOAD_START + mBuffer.getShort(LENGTH_OFFSET)); + mBuffer.position(DATA_START); // Just after the tag. + return decodeObject(); + } + + /** @return the loggable item at the current position in mBuffer. */ + private Object decodeObject() { + if (mBuffer.remaining() < 1) return null; + switch (mBuffer.get()) { + case INT: + if (mBuffer.remaining() < 4) return null; + return mBuffer.getInt(); + + case LONG: + if (mBuffer.remaining() < 8) return null; + return mBuffer.getLong(); + + case STRING: + try { + if (mBuffer.remaining() < 4) return null; + int length = mBuffer.getInt(); + if (length < 0 || mBuffer.remaining() < length) return null; + int start = mBuffer.position(); + mBuffer.position(start + length); + return new String(mBuffer.array(), start, length, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); // UTF-8 is guaranteed. + } + + case LIST: + if (mBuffer.remaining() < 1) return null; + int length = mBuffer.get(); + if (length <= 0) return null; + Object[] array = new Object[length]; + for (int i = 0; i < length; ++i) { + array[i] = decodeObject(); + if (array[i] == null) return null; + } + return new List(array); + + default: + return null; + } + } + } + + // We assume that the native methods deal with any concurrency issues. + + /** + * Send an event log message. + * @param tag An event identifer + * @param value A value to log + * @return The number of bytes written + */ + public static native int writeEvent(int tag, int value); + + /** + * Send an event log message. + * @param tag An event identifer + * @param value A value to log + * @return The number of bytes written + */ + public static native int writeEvent(int tag, long value); + + /** + * Send an event log message. + * @param tag An event identifer + * @param str A value to log + * @return The number of bytes written + */ + public static native int writeEvent(int tag, String str); + + /** + * Send an event log message. + * @param tag An event identifer + * @param list A {@link List} to log + * @return The number of bytes written + */ + public static native int writeEvent(int tag, List list); + + /** + * Send an event log message. + * @param tag An event identifer + * @param list A list of values to log + * @return The number of bytes written + */ + public static int writeEvent(int tag, Object... list) { + return writeEvent(tag, new List(list)); + } + + /** + * Read events from the log, filtered by type. + * @param tags to search for + * @param output container to add events into + * @throws IOException if something goes wrong reading events + */ + public static native void readEvents(int[] tags, Collection<Event> output) + throws IOException; +} diff --git a/core/java/android/util/EventLogTags.java b/core/java/android/util/EventLogTags.java new file mode 100644 index 0000000..be905e3 --- /dev/null +++ b/core/java/android/util/EventLogTags.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * 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 android.util; + +import android.util.Log; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** Parsed representation of /etc/event-log-tags. */ +public class EventLogTags { + private final static String TAG = "EventLogTags"; + + private final static String TAGS_FILE = "/etc/event-log-tags"; + + public static class Description { + public final int mTag; + public final String mName; + // TODO: Parse parameter descriptions when anyone has a use for them. + + Description(int tag, String name) { + mTag = tag; + mName = name; + } + } + + private final static Pattern COMMENT_PATTERN = Pattern.compile( + "^\\s*(#.*)?$"); + + private final static Pattern TAG_PATTERN = Pattern.compile( + "^\\s*(\\d+)\\s+(\\w+)\\s*(\\(.*\\))?\\s*$"); + + private final HashMap<String, Description> mNameMap = + new HashMap<String, Description>(); + + private final HashMap<Integer, Description> mTagMap = + new HashMap<Integer, Description>(); + + public EventLogTags() throws IOException { + this(new BufferedReader(new FileReader(TAGS_FILE), 256)); + } + + public EventLogTags(BufferedReader input) throws IOException { + String line; + while ((line = input.readLine()) != null) { + Matcher m = COMMENT_PATTERN.matcher(line); + if (m.matches()) continue; + + m = TAG_PATTERN.matcher(line); + if (m.matches()) { + try { + int tag = Integer.parseInt(m.group(1)); + Description d = new Description(tag, m.group(2)); + mNameMap.put(d.mName, d); + mTagMap.put(d.mTag, d); + } catch (NumberFormatException e) { + Log.e(TAG, "Error in event log tags entry: " + line, e); + } + } else { + Log.e(TAG, "Can't parse event log tags entry: " + line); + } + } + } + + public Description get(String name) { + return mNameMap.get(name); + } + + public Description get(int tag) { + return mTagMap.get(tag); + } +} diff --git a/core/java/android/util/FloatMath.java b/core/java/android/util/FloatMath.java new file mode 100644 index 0000000..6216638 --- /dev/null +++ b/core/java/android/util/FloatMath.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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 android.util; + +/** + * Math routines similar to those found in {@link java.lang.Math}. Performs + * computations on {@code float} values directly without incurring the overhead + * of conversions to and from {@code double}. + * + * <p>On one platform, {@code FloatMath.sqrt(100)} executes in one third of the + * time required by {@code java.lang.Math.sqrt(100)}.</p> + */ +public class FloatMath { + + /** Prevents instantiation. */ + private FloatMath() {} + + /** + * Returns the float conversion of the most positive (i.e. closest to + * positive infinity) integer value which is less than the argument. + * + * @param value to be converted + * @return the floor of value + */ + public static native float floor(float value); + + /** + * Returns the float conversion of the most negative (i.e. closest to + * negative infinity) integer value which is greater than the argument. + * + * @param value to be converted + * @return the ceiling of value + */ + public static native float ceil(float value); + + /** + * Returns the closest float approximation of the sine of the argument. + * + * @param angle to compute the cosine of, in radians + * @return the sine of angle + */ + public static native float sin(float angle); + + /** + * Returns the closest float approximation of the cosine of the argument. + * + * @param angle to compute the cosine of, in radians + * @return the cosine of angle + */ + public static native float cos(float angle); + + /** + * Returns the closest float approximation of the square root of the + * argument. + * + * @param value to compute sqrt of + * @return the square root of value + */ + public static native float sqrt(float value); +} diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java new file mode 100644 index 0000000..24f67cd --- /dev/null +++ b/core/java/android/util/Log.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +import com.android.internal.os.RuntimeInit; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * API for sending log output. + * + * <p>Generally, use the Log.v() Log.d() Log.i() Log.w() and Log.e() + * methods. + * + * <p>The order in terms of verbosity, from least to most is + * ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose should never be compiled + * into an application except during development. Debug logs are compiled + * in but stripped at runtime. Error, warning and info logs are always kept. + * + * <p><b>Tip:</b> A good convention is to declare a <code>TAG</code> constant + * in your class: + * + * <pre>private static final String TAG = "MyActivity";</pre> + * + * and use that in subsequent calls to the log methods. + * </p> + * + * <p><b>Tip:</b> Don't forget that when you make a call like + * <pre>Log.v(TAG, "index=" + i);</pre> + * that when you're building the string to pass into Log.d, the compiler uses a + * StringBuilder and at least three allocations occur: the StringBuilder + * itself, the buffer, and the String object. Realistically, there is also + * another buffer allocation and copy, and even more pressure on the gc. + * That means that if your log message is filtered out, you might be doing + * significant work and incurring significant overhead. + */ +public final class Log { + + /** + * Priority constant for the println method; use Log.v. + */ + public static final int VERBOSE = 2; + + /** + * Priority constant for the println method; use Log.d. + */ + public static final int DEBUG = 3; + + /** + * Priority constant for the println method; use Log.i. + */ + public static final int INFO = 4; + + /** + * Priority constant for the println method; use Log.w. + */ + public static final int WARN = 5; + + /** + * Priority constant for the println method; use Log.e. + */ + public static final int ERROR = 6; + + /** + * Priority constant for the println method. + */ + public static final int ASSERT = 7; + + private Log() { + } + + /** + * Send a {@link #VERBOSE} log message. + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public static int v(String tag, String msg) { + return println(VERBOSE, tag, msg); + } + + /** + * Send a {@link #VERBOSE} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + * @param tr An exception to log + */ + public static int v(String tag, String msg, Throwable tr) { + return println(VERBOSE, tag, msg + '\n' + getStackTraceString(tr)); + } + + /** + * Send a {@link #DEBUG} log message. + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public static int d(String tag, String msg) { + return println(DEBUG, tag, msg); + } + + /** + * Send a {@link #DEBUG} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + * @param tr An exception to log + */ + public static int d(String tag, String msg, Throwable tr) { + return println(DEBUG, tag, msg + '\n' + getStackTraceString(tr)); + } + + /** + * Send an {@link #INFO} log message. + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public static int i(String tag, String msg) { + return println(INFO, tag, msg); + } + + /** + * Send a {@link #INFO} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + * @param tr An exception to log + */ + public static int i(String tag, String msg, Throwable tr) { + return println(INFO, tag, msg + '\n' + getStackTraceString(tr)); + } + + /** + * Send a {@link #WARN} log message. + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public static int w(String tag, String msg) { + return println(WARN, tag, msg); + } + + /** + * Send a {@link #WARN} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + * @param tr An exception to log + */ + public static int w(String tag, String msg, Throwable tr) { + return println(WARN, tag, msg + '\n' + getStackTraceString(tr)); + } + + /** + * Checks to see whether or not a log for the specified tag is loggable at the specified level. + * + * The default level of any tag is set to INFO. This means that any level above and including + * INFO will be logged. Before you make any calls to a logging method you should check to see + * if your tag should be logged. You can change the default level by setting a system property: + * 'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>' + * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPRESS will + * turn off all logging for your tag. You can also create a local.prop file that with the + * following in it: + * 'log.tag.<YOUR_LOG_TAG>=<LEVEL>' + * and place that in /data/local.prop. + * + * @param tag The tag to check. + * @param level The level to check. + * @return Whether or not that this is allowed to be logged. + * @throws IllegalArgumentException is thrown if the tag.length() > 23. + */ + public static native boolean isLoggable(String tag, int level); + + /* + * Send a {@link #WARN} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param tr An exception to log + */ + public static int w(String tag, Throwable tr) { + return println(WARN, tag, getStackTraceString(tr)); + } + + /** + * Send an {@link #ERROR} log message. + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public static int e(String tag, String msg) { + return println(ERROR, tag, msg); + } + + /** + * Send a {@link #ERROR} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + * @param tr An exception to log + */ + public static int e(String tag, String msg, Throwable tr) { + int r = println(ERROR, tag, msg + '\n' + getStackTraceString(tr)); + RuntimeInit.reportException(tag, tr, false); // asynchronous + return r; + } + + /** + * Handy function to get a loggable stack trace from a Throwable + * @param tr An exception to log + */ + public static String getStackTraceString(Throwable tr) { + if (tr == null) { + return ""; + } + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + tr.printStackTrace(pw); + return sw.toString(); + } + + /** + * Low-level logging call. + * @param priority The priority/type of this log message + * @param tag Used to identify the source of a log message. It usually identfies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + * @return The number of bytes written. + */ + public static native int println(int priority, String tag, String msg); +} diff --git a/core/java/android/util/LogPrinter.java b/core/java/android/util/LogPrinter.java new file mode 100644 index 0000000..643b8d3 --- /dev/null +++ b/core/java/android/util/LogPrinter.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +/** + * Implementation of a {@link android.util.Printer} that sends its output + * to the system log. + */ +public class LogPrinter implements Printer { + private final int mPriority; + private final String mTag; + + /** + * Create a new Printer that sends to the log with the given priority + * and tag. + * + * @param priority The desired log priority: + * {@link android.util.Log#VERBOSE Log.VERBOSE}, + * {@link android.util.Log#DEBUG Log.DEBUG}, + * {@link android.util.Log#INFO Log.INFO}, + * {@link android.util.Log#WARN Log.WARN}, or + * {@link android.util.Log#ERROR Log.ERROR}. + * @param tag A string tag to associate with each printed log statement. + */ + public LogPrinter(int priority, String tag) { + mPriority = priority; + mTag = tag; + } + + public void println(String x) { + Log.println(mPriority, mTag, x); + } +} diff --git a/core/java/android/util/MonthDisplayHelper.java b/core/java/android/util/MonthDisplayHelper.java new file mode 100644 index 0000000..c3f13fc --- /dev/null +++ b/core/java/android/util/MonthDisplayHelper.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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 android.util; + +import java.util.Calendar; + +/** + * Helps answer common questions that come up when displaying a month in a + * 6 row calendar grid format. + * + * Not thread safe. + */ +public class MonthDisplayHelper { + + // display pref + private final int mWeekStartDay; + + // holds current month, year, helps compute display + private Calendar mCalendar; + + // cached computed stuff that helps with display + private int mNumDaysInMonth; + private int mNumDaysInPrevMonth; + private int mOffset; + + + /** + * @param year The year. + * @param month The month. + * @param weekStartDay What day of the week the week should start. + */ + public MonthDisplayHelper(int year, int month, int weekStartDay) { + + if (weekStartDay < Calendar.SUNDAY || weekStartDay > Calendar.SATURDAY) { + throw new IllegalArgumentException(); + } + mWeekStartDay = weekStartDay; + + mCalendar = Calendar.getInstance(); + mCalendar.set(Calendar.YEAR, year); + mCalendar.set(Calendar.MONTH, month); + mCalendar.set(Calendar.DAY_OF_MONTH, 1); + mCalendar.set(Calendar.HOUR_OF_DAY, 0); + mCalendar.set(Calendar.MINUTE, 0); + mCalendar.set(Calendar.SECOND, 0); + mCalendar.getTimeInMillis(); + + recalculate(); + } + + + public MonthDisplayHelper(int year, int month) { + this(year, month, Calendar.SUNDAY); + } + + + public int getYear() { + return mCalendar.get(Calendar.YEAR); + } + + public int getMonth() { + return mCalendar.get(Calendar.MONTH); + } + + + public int getWeekStartDay() { + return mWeekStartDay; + } + + /** + * @return The first day of the month using a constants such as + * {@link java.util.Calendar#SUNDAY}. + */ + public int getFirstDayOfMonth() { + return mCalendar.get(Calendar.DAY_OF_WEEK); + } + + /** + * @return The number of days in the month. + */ + public int getNumberOfDaysInMonth() { + return mNumDaysInMonth; + } + + + /** + * @return The offset from displaying everything starting on the very first + * box. For example, if the calendar is set to display the first day of + * the week as Sunday, and the month starts on a Wednesday, the offset is 3. + */ + public int getOffset() { + return mOffset; + } + + + /** + * @param row Which row (0-5). + * @return the digits of the month to display in one + * of the 6 rows of a calendar month display. + */ + public int[] getDigitsForRow(int row) { + if (row < 0 || row > 5) { + throw new IllegalArgumentException("row " + row + + " out of range (0-5)"); + } + + int [] result = new int[7]; + for (int column = 0; column < 7; column++) { + result[column] = getDayAt(row, column); + } + + return result; + } + + /** + * @param row The row, 0-5, starting from the top. + * @param column The column, 0-6, starting from the left. + * @return The day at a particular row, column + */ + public int getDayAt(int row, int column) { + + if (row == 0 && column < mOffset) { + return mNumDaysInPrevMonth + column - mOffset + 1; + } + + int day = 7 * row + column - mOffset + 1; + + return (day > mNumDaysInMonth) ? + day - mNumDaysInMonth : day; + } + + /** + * @return Which row day is in. + */ + public int getRowOf(int day) { + return (day + mOffset - 1) / 7; + } + + /** + * @return Which column day is in. + */ + public int getColumnOf(int day) { + return (day + mOffset - 1) % 7; + } + + /** + * Decrement the month. + */ + public void previousMonth() { + mCalendar.add(Calendar.MONTH, -1); + recalculate(); + } + + /** + * Increment the month. + */ + public void nextMonth() { + mCalendar.add(Calendar.MONTH, 1); + recalculate(); + } + + /** + * @return Whether the row and column fall within the month. + */ + public boolean isWithinCurrentMonth(int row, int column) { + + if (row < 0 || column < 0 || row > 5 || column > 6) { + return false; + } + + if (row == 0 && column < mOffset) { + return false; + } + + int day = 7 * row + column - mOffset + 1; + if (day > mNumDaysInMonth) { + return false; + } + return true; + } + + + // helper method that recalculates cached values based on current month / year + private void recalculate() { + + mNumDaysInMonth = mCalendar.getActualMaximum(Calendar.DAY_OF_MONTH); + + mCalendar.add(Calendar.MONTH, -1); + mNumDaysInPrevMonth = mCalendar.getActualMaximum(Calendar.DAY_OF_MONTH); + mCalendar.add(Calendar.MONTH, 1); + + int firstDayOfMonth = getFirstDayOfMonth(); + int offset = firstDayOfMonth - mWeekStartDay; + if (offset < 0) { + offset += 7; + } + mOffset = offset; + } +} diff --git a/core/java/android/util/PrintWriterPrinter.java b/core/java/android/util/PrintWriterPrinter.java new file mode 100644 index 0000000..82c4d03 --- /dev/null +++ b/core/java/android/util/PrintWriterPrinter.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +import java.io.PrintWriter; + +/** + * Implementation of a {@link android.util.Printer} that sends its output + * to a {@link java.io.PrintWriter}. + */ +public class PrintWriterPrinter implements Printer { + private final PrintWriter mPW; + + /** + * Create a new Printer that sends to a PrintWriter object. + * + * @param pw The PrintWriter where you would like output to go. + */ + public PrintWriterPrinter(PrintWriter pw) { + mPW = pw; + } + + public void println(String x) { + mPW.println(x); + } +} diff --git a/core/java/android/util/Printer.java b/core/java/android/util/Printer.java new file mode 100644 index 0000000..595cf70 --- /dev/null +++ b/core/java/android/util/Printer.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +/** + * Simple interface for printing text, allowing redirection to various + * targets. Standard implementations are {@link android.util.LogPrinter}, + * {@link android.util.StringBuilderPrinter}, and + * {@link android.util.PrintWriterPrinter}. + */ +public interface Printer { + /** + * Write a line of text to the output. There is no need to terminate + * the given string with a newline. + */ + void println(String x); +} diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java new file mode 100644 index 0000000..1c8b330 --- /dev/null +++ b/core/java/android/util/SparseArray.java @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +import com.android.internal.util.ArrayUtils; + +/** + * SparseArrays map integers to Objects. Unlike a normal array of Objects, + * there can be gaps in the indices. It is intended to be more efficient + * than using a HashMap to map Integers to Objects. + */ +public class SparseArray<E> { + private static final Object DELETED = new Object(); + private boolean mGarbage = false; + + /** + * Creates a new SparseArray containing no mappings. + */ + public SparseArray() { + this(10); + } + + /** + * Creates a new SparseArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public SparseArray(int initialCapacity) { + initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); + + mKeys = new int[initialCapacity]; + mValues = new Object[initialCapacity]; + mSize = 0; + } + + /** + * Gets the Object mapped from the specified key, or <code>null</code> + * if no such mapping has been made. + */ + public E get(int key) { + return get(key, null); + } + + /** + * Gets the Object mapped from the specified key, or the specified Object + * if no such mapping has been made. + */ + public E get(int key, E valueIfKeyNotFound) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i < 0 || mValues[i] == DELETED) { + return valueIfKeyNotFound; + } else { + return (E) mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(int key) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + if (mValues[i] != DELETED) { + mValues[i] = DELETED; + mGarbage = true; + } + } + } + + /** + * Alias for {@link #delete(int)}. + */ + public void remove(int key) { + delete(key); + } + + private void gc() { + // Log.e("SparseArray", "gc start with " + mSize); + + int n = mSize; + int o = 0; + int[] keys = mKeys; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + Object val = values[i]; + + if (val != DELETED) { + if (i != o) { + keys[o] = keys[i]; + values[o] = val; + } + + o++; + } + } + + mGarbage = false; + mSize = o; + + // Log.e("SparseArray", "gc end with " + mSize); + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(int key, E value) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (i < mSize && mValues[i] == DELETED) { + mKeys[i] = key; + mValues[i] = value; + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + + // Search again because indices may have changed. + i = ~binarySearch(mKeys, 0, mSize, key); + } + + if (mSize >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(mSize + 1); + + int[] nkeys = new int[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseArray + * currently stores. + */ + public int size() { + if (mGarbage) { + gc(); + } + + return mSize; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the key from the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public int keyAt(int index) { + if (mGarbage) { + gc(); + } + + return mKeys[index]; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the value from the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public E valueAt(int index) { + if (mGarbage) { + gc(); + } + + return (E) mValues[index]; + } + + /** + * Given an index in the range <code>0...size()-1</code>, sets a new + * value for the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public void setValueAt(int index, E value) { + if (mGarbage) { + gc(); + } + + mValues[index] = value; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(int key) { + if (mGarbage) { + gc(); + } + + return binarySearch(mKeys, 0, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(E value) { + if (mGarbage) { + gc(); + } + + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseArray. + */ + public void clear() { + int n = mSize; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + values[i] = null; + } + + mSize = 0; + mGarbage = false; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(int key, E value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(pos + 1); + + int[] nkeys = new int[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + private static int binarySearch(int[] a, int start, int len, int key) { + int high = start + len, low = start - 1, guess; + + while (high - low > 1) { + guess = (high + low) / 2; + + if (a[guess] < key) + low = guess; + else + high = guess; + } + + if (high == start + len) + return ~(start + len); + else if (a[high] == key) + return high; + else + return ~high; + } + + private void checkIntegrity() { + for (int i = 1; i < mSize; i++) { + if (mKeys[i] <= mKeys[i - 1]) { + for (int j = 0; j < mSize; j++) { + Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]); + } + + throw new RuntimeException(); + } + } + } + + private int[] mKeys; + private Object[] mValues; + private int mSize; +} diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java new file mode 100644 index 0000000..f7799de --- /dev/null +++ b/core/java/android/util/SparseBooleanArray.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +import com.android.internal.util.ArrayUtils; + +/** + * SparseBooleanArrays map integers to booleans. + * Unlike a normal array of booleans + * there can be gaps in the indices. It is intended to be more efficient + * than using a HashMap to map Integers to Booleans. + */ +public class SparseBooleanArray { + /** + * Creates a new SparseBooleanArray containing no mappings. + */ + public SparseBooleanArray() { + this(10); + } + + /** + * Creates a new SparseBooleanArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public SparseBooleanArray(int initialCapacity) { + initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); + + mKeys = new int[initialCapacity]; + mValues = new boolean[initialCapacity]; + mSize = 0; + } + + /** + * Gets the boolean mapped from the specified key, or <code>false</code> + * if no such mapping has been made. + */ + public boolean get(int key) { + return get(key, false); + } + + /** + * Gets the boolean mapped from the specified key, or the specified value + * if no such mapping has been made. + */ + public boolean get(int key, boolean valueIfKeyNotFound) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i < 0) { + return valueIfKeyNotFound; + } else { + return mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(int key) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + System.arraycopy(mKeys, i + 1, mKeys, i, mSize - (i + 1)); + System.arraycopy(mValues, i + 1, mValues, i, mSize - (i + 1)); + mSize--; + } + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(int key, boolean value) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (mSize >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(mSize + 1); + + int[] nkeys = new int[n]; + boolean[] nvalues = new boolean[n]; + + // Log.e("SparseBooleanArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseBooleanArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseBooleanArray + * currently stores. + */ + public int size() { + return mSize; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the key from the <code>index</code>th key-value mapping that this + * SparseBooleanArray stores. + */ + public int keyAt(int index) { + return mKeys[index]; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the value from the <code>index</code>th key-value mapping that this + * SparseBooleanArray stores. + */ + public boolean valueAt(int index) { + return mValues[index]; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(int key) { + return binarySearch(mKeys, 0, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(boolean value) { + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseBooleanArray. + */ + public void clear() { + mSize = 0; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(int key, boolean value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(pos + 1); + + int[] nkeys = new int[n]; + boolean[] nvalues = new boolean[n]; + + // Log.e("SparseBooleanArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + private static int binarySearch(int[] a, int start, int len, int key) { + int high = start + len, low = start - 1, guess; + + while (high - low > 1) { + guess = (high + low) / 2; + + if (a[guess] < key) + low = guess; + else + high = guess; + } + + if (high == start + len) + return ~(start + len); + else if (a[high] == key) + return high; + else + return ~high; + } + + private void checkIntegrity() { + for (int i = 1; i < mSize; i++) { + if (mKeys[i] <= mKeys[i - 1]) { + for (int j = 0; j < mSize; j++) { + Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]); + } + + throw new RuntimeException(); + } + } + } + + private int[] mKeys; + private boolean[] mValues; + private int mSize; +} diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java new file mode 100644 index 0000000..610cfd4 --- /dev/null +++ b/core/java/android/util/SparseIntArray.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +import com.android.internal.util.ArrayUtils; + +/** + * SparseIntArrays map integers to integers. Unlike a normal array of integers, + * there can be gaps in the indices. It is intended to be more efficient + * than using a HashMap to map Integers to Integers. + */ +public class SparseIntArray { + /** + * Creates a new SparseIntArray containing no mappings. + */ + public SparseIntArray() { + this(10); + } + + /** + * Creates a new SparseIntArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public SparseIntArray(int initialCapacity) { + initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); + + mKeys = new int[initialCapacity]; + mValues = new int[initialCapacity]; + mSize = 0; + } + + /** + * Gets the int mapped from the specified key, or <code>0</code> + * if no such mapping has been made. + */ + public int get(int key) { + return get(key, 0); + } + + /** + * Gets the int mapped from the specified key, or the specified value + * if no such mapping has been made. + */ + public int get(int key, int valueIfKeyNotFound) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i < 0) { + return valueIfKeyNotFound; + } else { + return mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(int key) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + System.arraycopy(mKeys, i + 1, mKeys, i, mSize - (i + 1)); + System.arraycopy(mValues, i + 1, mValues, i, mSize - (i + 1)); + mSize--; + } + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(int key, int value) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (mSize >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(mSize + 1); + + int[] nkeys = new int[n]; + int[] nvalues = new int[n]; + + // Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseIntArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseIntArray + * currently stores. + */ + public int size() { + return mSize; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the key from the <code>index</code>th key-value mapping that this + * SparseIntArray stores. + */ + public int keyAt(int index) { + return mKeys[index]; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the value from the <code>index</code>th key-value mapping that this + * SparseIntArray stores. + */ + public int valueAt(int index) { + return mValues[index]; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(int key) { + return binarySearch(mKeys, 0, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(int value) { + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseIntArray. + */ + public void clear() { + mSize = 0; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(int key, int value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(pos + 1); + + int[] nkeys = new int[n]; + int[] nvalues = new int[n]; + + // Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + private static int binarySearch(int[] a, int start, int len, int key) { + int high = start + len, low = start - 1, guess; + + while (high - low > 1) { + guess = (high + low) / 2; + + if (a[guess] < key) + low = guess; + else + high = guess; + } + + if (high == start + len) + return ~(start + len); + else if (a[high] == key) + return high; + else + return ~high; + } + + private void checkIntegrity() { + for (int i = 1; i < mSize; i++) { + if (mKeys[i] <= mKeys[i - 1]) { + for (int j = 0; j < mSize; j++) { + Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]); + } + + throw new RuntimeException(); + } + } + } + + private int[] mKeys; + private int[] mValues; + private int mSize; +} diff --git a/core/java/android/util/StateSet.java b/core/java/android/util/StateSet.java new file mode 100644 index 0000000..f3d8159 --- /dev/null +++ b/core/java/android/util/StateSet.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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 android.util; + +import com.android.internal.R; + +/** + * State sets are arrays of positive ints where each element + * represents the state of a {@link android.view.View} (e.g. focused, + * selected, visible, etc.). A {@link android.view.View} may be in + * one or more of those states. + * + * A state spec is an array of signed ints where each element + * represents a required (if positive) or an undesired (if negative) + * {@link android.view.View} state. + * + * Utils dealing with state sets. + * + * In theory we could encapsulate the state set and state spec arrays + * and not have static methods here but there is some concern about + * performance since these methods are called during view drawing. + */ + +public class StateSet { + + public static final int[] WILD_CARD = new int[0]; + + /** + * Return whether the stateSetOrSpec is matched by all StateSets. + * + * @param stateSetOrSpec a state set or state spec. + */ + public static boolean isWildCard(int[] stateSetOrSpec) { + return stateSetOrSpec.length == 0 || stateSetOrSpec[0] == 0; + } + + /** + * Return whether the stateSet matches the desired stateSpec. + * + * @param stateSpec an array of required (if positive) or + * prohibited (if negative) {@link android.view.View} states. + * @param stateSet an array of {@link android.view.View} states + */ + public static boolean stateSetMatches(int[] stateSpec, int[] stateSet) { + if (stateSet == null) { + return (stateSpec == null || isWildCard(stateSpec)); + } + int stateSpecSize = stateSpec.length; + int stateSetSize = stateSet.length; + for (int i = 0; i < stateSpecSize; i++) { + int stateSpecState = stateSpec[i]; + if (stateSpecState == 0) { + // We've reached the end of the cases to match against. + return true; + } + final boolean mustMatch; + if (stateSpecState > 0) { + mustMatch = true; + } else { + // We use negative values to indicate must-NOT-match states. + mustMatch = false; + stateSpecState = -stateSpecState; + } + boolean found = false; + for (int j = 0; j < stateSetSize; j++) { + final int state = stateSet[j]; + if (state == 0) { + // We've reached the end of states to match. + if (mustMatch) { + // We didn't find this must-match state. + return false; + } else { + // Continue checking other must-not-match states. + break; + } + } + if (state == stateSpecState) { + if (mustMatch) { + found = true; + // Continue checking other other must-match states. + break; + } else { + // Any match of a must-not-match state returns false. + return false; + } + } + } + if (mustMatch && !found) { + // We've reached the end of states to match and we didn't + // find a must-match state. + return false; + } + } + return true; + } + + /** + * Return whether the state matches the desired stateSpec. + * + * @param stateSpec an array of required (if positive) or + * prohibited (if negative) {@link android.view.View} states. + * @param state a {@link android.view.View} state + */ + public static boolean stateSetMatches(int[] stateSpec, int state) { + int stateSpecSize = stateSpec.length; + for (int i = 0; i < stateSpecSize; i++) { + int stateSpecState = stateSpec[i]; + if (stateSpecState == 0) { + // We've reached the end of the cases to match against. + return true; + } + if (stateSpecState > 0) { + if (state != stateSpecState) { + return false; + } + } else { + // We use negative values to indicate must-NOT-match states. + if (state == -stateSpecState) { + // We matched a must-not-match case. + return false; + } + } + } + return true; + } + + public static int[] trimStateSet(int[] states, int newSize) { + if (states.length == newSize) { + return states; + } + + int[] trimmedStates = new int[newSize]; + System.arraycopy(states, 0, trimmedStates, 0, newSize); + return trimmedStates; + } + + public static String dump(int[] states) { + StringBuilder sb = new StringBuilder(); + + int count = states.length; + for (int i = 0; i < count; i++) { + + switch (states[i]) { + case R.attr.state_window_focused: + sb.append("W "); + break; + case R.attr.state_pressed: + sb.append("P "); + break; + case R.attr.state_selected: + sb.append("S "); + break; + case R.attr.state_focused: + sb.append("F "); + break; + case R.attr.state_enabled: + sb.append("E "); + break; + } + } + + return sb.toString(); + } +} diff --git a/core/java/android/util/StringBuilderPrinter.java b/core/java/android/util/StringBuilderPrinter.java new file mode 100644 index 0000000..d0fc1e7 --- /dev/null +++ b/core/java/android/util/StringBuilderPrinter.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +/** + * Implementation of a {@link android.util.Printer} that sends its output + * to a {@link StringBuilder}. + */ +public class StringBuilderPrinter implements Printer { + private final StringBuilder mBuilder; + + /** + * Create a new Printer that sends to a StringBuilder object. + * + * @param builder The StringBuilder where you would like output to go. + */ + public StringBuilderPrinter(StringBuilder builder) { + mBuilder = builder; + } + + public void println(String x) { + mBuilder.append(x); + int len = x.length(); + if (len <= 0 || x.charAt(len-1) != '\n') { + mBuilder.append('\n'); + } + } +} diff --git a/core/java/android/util/TimeFormatException.java b/core/java/android/util/TimeFormatException.java new file mode 100644 index 0000000..d7a898b --- /dev/null +++ b/core/java/android/util/TimeFormatException.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +public class TimeFormatException extends RuntimeException +{ + TimeFormatException(String s) + { + super(s); + } +} + diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java new file mode 100644 index 0000000..3c4e337 --- /dev/null +++ b/core/java/android/util/TimeUtils.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +import android.content.res.Resources; +import android.content.res.XmlResourceParser; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.TimeZone; +import java.util.Date; + +import com.android.internal.util.XmlUtils; + +public class TimeUtils { + /** + * Tries to return a time zone that would have had the specified offset + * and DST value at the specified moment in the specified country. + * Returns null if no suitable zone could be found. + */ + public static TimeZone getTimeZone(int offset, boolean dst, long when, + String country) { + if (country == null) { + return null; + } + + TimeZone best = null; + + Resources r = Resources.getSystem(); + XmlResourceParser parser = r.getXml(com.android.internal.R.xml.time_zones_by_country); + Date d = new Date(when); + + TimeZone current = TimeZone.getDefault(); + String currentName = current.getID(); + int currentOffset = current.getOffset(when); + boolean currentDst = current.inDaylightTime(d); + + try { + XmlUtils.beginDocument(parser, "timezones"); + + while (true) { + XmlUtils.nextElement(parser); + + String element = parser.getName(); + if (element == null || !(element.equals("timezone"))) { + break; + } + + String code = parser.getAttributeValue(null, "code"); + + if (country.equals(code)) { + if (parser.next() == XmlPullParser.TEXT) { + String maybe = parser.getText(); + + // If the current time zone is from the right country + // and meets the other known properties, keep it + // instead of changing to another one. + + if (maybe.equals(currentName)) { + if (currentOffset == offset && currentDst == dst) { + return current; + } + } + + // Otherwise, take the first zone from the right + // country that has the correct current offset and DST. + // (Keep iterating instead of returning in case we + // haven't encountered the current time zone yet.) + + if (best == null) { + TimeZone tz = TimeZone.getTimeZone(maybe); + + if (tz.getOffset(when) == offset && + tz.inDaylightTime(d) == dst) { + best = tz; + } + } + } + } + } + } catch (XmlPullParserException e) { + Log.e("TimeUtils", + "Got exception while getting preferred time zone.", e); + } catch (IOException e) { + Log.e("TimeUtils", + "Got exception while getting preferred time zone.", e); + } finally { + parser.close(); + } + + return best; + } +} diff --git a/core/java/android/util/TimingLogger.java b/core/java/android/util/TimingLogger.java new file mode 100644 index 0000000..0f39c97 --- /dev/null +++ b/core/java/android/util/TimingLogger.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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 android.util; + +import java.util.ArrayList; + +import android.os.SystemClock; + +/** + * A utility class to help log timings splits throughout a method call. + * Typical usage is: + * + * TimingLogger timings = new TimingLogger(TAG, "methodA"); + * ... do some work A ... + * timings.addSplit("work A"); + * ... do some work B ... + * timings.addSplit("work B"); + * ... do some work C ... + * timings.addSplit("work C"); + * timings.dumpToLog(); + * + * The dumpToLog call would add the following to the log: + * + * D/TAG ( 3459): methodA: begin + * D/TAG ( 3459): methodA: 9 ms, work A + * D/TAG ( 3459): methodA: 1 ms, work B + * D/TAG ( 3459): methodA: 6 ms, work C + * D/TAG ( 3459): methodA: end, 16 ms + */ +public class TimingLogger { + + /** + * The Log tag to use for checking Log.isLoggable and for + * logging the timings. + */ + private String mTag; + + /** A label to be included in every log. */ + private String mLabel; + + /** Used to track whether Log.isLoggable was enabled at reset time. */ + private boolean mDisabled; + + /** Stores the time of each split. */ + ArrayList<Long> mSplits; + + /** Stores the labels for each split. */ + ArrayList<String> mSplitLabels; + + /** + * Create and initialize a TimingLogger object that will log using + * the specific tag. If the Log.isLoggable is not enabled to at + * least the Log.VERBOSE level for that tag at creation time then + * the addSplit and dumpToLog call will do nothing. + * @param tag the log tag to use while logging the timings + * @param label a string to be displayed with each log + */ + public TimingLogger(String tag, String label) { + reset(tag, label); + } + + /** + * Clear and initialize a TimingLogger object that will log using + * the specific tag. If the Log.isLoggable is not enabled to at + * least the Log.VERBOSE level for that tag at creation time then + * the addSplit and dumpToLog call will do nothing. + * @param tag the log tag to use while logging the timings + * @param label a string to be displayed with each log + */ + public void reset(String tag, String label) { + mTag = tag; + mLabel = label; + reset(); + } + + /** + * Clear and initialize a TimingLogger object that will log using + * the tag and label that was specified previously, either via + * the constructor or a call to reset(tag, label). If the + * Log.isLoggable is not enabled to at least the Log.VERBOSE + * level for that tag at creation time then the addSplit and + * dumpToLog call will do nothing. + */ + public void reset() { + mDisabled = !Log.isLoggable(mTag, Log.VERBOSE); + if (mDisabled) return; + if (mSplits == null) { + mSplits = new ArrayList<Long>(); + mSplitLabels = new ArrayList<String>(); + } else { + mSplits.clear(); + mSplitLabels.clear(); + } + addSplit(null); + } + + /** + * Add a split for the current time, labeled with splitLabel. If + * Log.isLoggable was not enabled to at least the Log.VERBOSE for + * the specified tag at construction or reset() time then this + * call does nothing. + * @param splitLabel a label to associate with this split. + */ + public void addSplit(String splitLabel) { + if (mDisabled) return; + long now = SystemClock.elapsedRealtime(); + mSplits.add(now); + mSplitLabels.add(splitLabel); + } + + /** + * Dumps the timings to the log using Log.d(). If Log.isLoggable was + * not enabled to at least the Log.VERBOSE for the specified tag at + * construction or reset() time then this call does nothing. + */ + public void dumpToLog() { + if (mDisabled) return; + Log.d(mTag, mLabel + ": begin"); + final long first = mSplits.get(0); + long now = first; + for (int i = 1; i < mSplits.size(); i++) { + now = mSplits.get(i); + final String splitLabel = mSplitLabels.get(i); + final long prev = mSplits.get(i - 1); + + Log.d(mTag, mLabel + ": " + (now - prev) + " ms, " + splitLabel); + } + Log.d(mTag, mLabel + ": end, " + (now - first) + " ms"); + } +} diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java new file mode 100644 index 0000000..a4ee35a --- /dev/null +++ b/core/java/android/util/TypedValue.java @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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 android.util; + +import android.util.Config; +import android.util.Log; + +/** + * Container for a dynamically typed data value. Primarily used with + * {@link android.content.res.Resources} for holding resource values. + */ +public class TypedValue { + /** The value contains no data. */ + public static final int TYPE_NULL = 0x00; + + /** The <var>data</var> field holds a resource identifier. */ + public static final int TYPE_REFERENCE = 0x01; + /** The <var>data</var> field holds an attribute resource + * identifier (referencing an attribute in the current theme + * style, not a resource entry). */ + public static final int TYPE_ATTRIBUTE = 0x02; + /** The <var>string</var> field holds string data. In addition, if + * <var>data</var> is non-zero then it is the string block + * index of the string and <var>assetCookie</var> is the set of + * assets the string came from. */ + public static final int TYPE_STRING = 0x03; + /** The <var>data</var> field holds an IEEE 754 floating point number. */ + public static final int TYPE_FLOAT = 0x04; + /** The <var>data</var> field holds a complex number encoding a + * dimension value. */ + public static final int TYPE_DIMENSION = 0x05; + /** The <var>data</var> field holds a complex number encoding a fraction + * of a container. */ + public static final int TYPE_FRACTION = 0x06; + + /** Identifies the start of plain integer values. Any type value + * from this to {@link #TYPE_LAST_INT} means the + * <var>data</var> field holds a generic integer value. */ + public static final int TYPE_FIRST_INT = 0x10; + + /** The <var>data</var> field holds a number that was + * originally specified in decimal. */ + public static final int TYPE_INT_DEC = 0x10; + /** The <var>data</var> field holds a number that was + * originally specified in hexadecimal (0xn). */ + public static final int TYPE_INT_HEX = 0x11; + /** The <var>data</var> field holds 0 or 1 that was originally + * specified as "false" or "true". */ + public static final int TYPE_INT_BOOLEAN = 0x12; + + /** Identifies the start of integer values that were specified as + * color constants (starting with '#'). */ + public static final int TYPE_FIRST_COLOR_INT = 0x1c; + + /** The <var>data</var> field holds a color that was originally + * specified as #aarrggbb. */ + public static final int TYPE_INT_COLOR_ARGB8 = 0x1c; + /** The <var>data</var> field holds a color that was originally + * specified as #rrggbb. */ + public static final int TYPE_INT_COLOR_RGB8 = 0x1d; + /** The <var>data</var> field holds a color that was originally + * specified as #argb. */ + public static final int TYPE_INT_COLOR_ARGB4 = 0x1e; + /** The <var>data</var> field holds a color that was originally + * specified as #rgb. */ + public static final int TYPE_INT_COLOR_RGB4 = 0x1f; + + /** Identifies the end of integer values that were specified as color + * constants. */ + public static final int TYPE_LAST_COLOR_INT = 0x1f; + + /** Identifies the end of plain integer values. */ + public static final int TYPE_LAST_INT = 0x1f; + + /* ------------------------------------------------------------ */ + + /** Complex data: bit location of unit information. */ + public static final int COMPLEX_UNIT_SHIFT = 0; + /** Complex data: mask to extract unit information (after shifting by + * {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as + * defined below. */ + public static final int COMPLEX_UNIT_MASK = 0xf; + + /** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */ + public static final int COMPLEX_UNIT_PX = 0; + /** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent + * Pixels. */ + public static final int COMPLEX_UNIT_DIP = 1; + /** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */ + public static final int COMPLEX_UNIT_SP = 2; + /** {@link #TYPE_DIMENSION} complex unit: Value is in points. */ + public static final int COMPLEX_UNIT_PT = 3; + /** {@link #TYPE_DIMENSION} complex unit: Value is in inches. */ + public static final int COMPLEX_UNIT_IN = 4; + /** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */ + public static final int COMPLEX_UNIT_MM = 5; + + /** {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall + * size. */ + public static final int COMPLEX_UNIT_FRACTION = 0; + /** {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. */ + public static final int COMPLEX_UNIT_FRACTION_PARENT = 1; + + /** Complex data: where the radix information is, telling where the decimal + * place appears in the mantissa. */ + public static final int COMPLEX_RADIX_SHIFT = 4; + /** Complex data: mask to extract radix information (after shifting by + * {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point + * representations as defined below. */ + public static final int COMPLEX_RADIX_MASK = 0x3; + + /** Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 */ + public static final int COMPLEX_RADIX_23p0 = 0; + /** Complex data: the mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn */ + public static final int COMPLEX_RADIX_16p7 = 1; + /** Complex data: the mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn */ + public static final int COMPLEX_RADIX_8p15 = 2; + /** Complex data: the mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn */ + public static final int COMPLEX_RADIX_0p23 = 3; + + /** Complex data: bit location of mantissa information. */ + public static final int COMPLEX_MANTISSA_SHIFT = 8; + /** Complex data: mask to extract mantissa information (after shifting by + * {@link #COMPLEX_MANTISSA_SHIFT}). This gives us 23 bits of precision; + * the top bit is the sign. */ + public static final int COMPLEX_MANTISSA_MASK = 0xffffff; + + /* ------------------------------------------------------------ */ + + /** The type held by this value, as defined by the constants here. + * This tells you how to interpret the other fields in the object. */ + public int type; + + /** If the value holds a string, this is it. */ + public CharSequence string; + + /** Basic data in the value, interpreted according to {@link #type} */ + public int data; + + /** Additional information about where the value came from; only + * set for strings. */ + public int assetCookie; + + /** If Value came from a resource, this holds the corresponding resource id. */ + public int resourceId; + + /** If Value came from a resource, these are the configurations for which + * its contents can change. */ + public int changingConfigurations = -1; + + /* ------------------------------------------------------------ */ + + /** Return the data for this value as a float. Only use for values + * whose type is {@link #TYPE_FLOAT}. */ + public final float getFloat() { + return Float.intBitsToFloat(data); + } + + private static final float MANTISSA_MULT = + 1.0f / (1<<TypedValue.COMPLEX_MANTISSA_SHIFT); + private static final float[] RADIX_MULTS = new float[] { + 1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT, + 1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT + }; + + /** + * Retrieve the base value from a complex data integer. This uses the + * {@link #COMPLEX_MANTISSA_MASK} and {@link #COMPLEX_RADIX_MASK} fields of + * the data to compute a floating point representation of the number they + * describe. The units are ignored. + * + * @param complex A complex data value. + * + * @return A floating point value corresponding to the complex data. + */ + public static float complexToFloat(int complex) + { + return (complex&(TypedValue.COMPLEX_MANTISSA_MASK + <<TypedValue.COMPLEX_MANTISSA_SHIFT)) + * RADIX_MULTS[(complex>>TypedValue.COMPLEX_RADIX_SHIFT) + & TypedValue.COMPLEX_RADIX_MASK]; + } + + /** + * Converts a complex data value holding a dimension to its final floating + * point value. The given <var>data</var> must be structured as a + * {@link #TYPE_DIMENSION}. + * + * @param data A complex data value holding a unit, magnitude, and + * mantissa. + * @param metrics Current display metrics to use in the conversion -- + * supplies display density and scaling information. + * + * @return The complex floating point value multiplied by the appropriate + * metrics depending on its unit. + */ + public static float complexToDimension(int data, DisplayMetrics metrics) + { + return applyDimension( + (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK, + complexToFloat(data), + metrics); + } + + /** + * Converts a complex data value holding a dimension to its final value + * as an integer pixel offset. This is the same as + * {@link #complexToDimension}, except the raw floating point value is + * truncated to an integer (pixel) value. + * The given <var>data</var> must be structured as a + * {@link #TYPE_DIMENSION}. + * + * @param data A complex data value holding a unit, magnitude, and + * mantissa. + * @param metrics Current display metrics to use in the conversion -- + * supplies display density and scaling information. + * + * @return The number of pixels specified by the data and its desired + * multiplier and units. + */ + public static int complexToDimensionPixelOffset(int data, + DisplayMetrics metrics) + { + return (int)applyDimension( + (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK, + complexToFloat(data), + metrics); + } + + /** + * Converts a complex data value holding a dimension to its final value + * as an integer pixel size. This is the same as + * {@link #complexToDimension}, except the raw floating point value is + * converted to an integer (pixel) value for use as a size. A size + * conversion involves rounding the base value, and ensuring that a + * non-zero base value is at least one pixel in size. + * The given <var>data</var> must be structured as a + * {@link #TYPE_DIMENSION}. + * + * @param data A complex data value holding a unit, magnitude, and + * mantissa. + * @param metrics Current display metrics to use in the conversion -- + * supplies display density and scaling information. + * + * @return The number of pixels specified by the data and its desired + * multiplier and units. + */ + public static int complexToDimensionPixelSize(int data, + DisplayMetrics metrics) + { + final float value = complexToFloat(data); + final float f = applyDimension( + (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK, + value, + metrics); + final int res = (int)(f+0.5f); + if (res != 0) return res; + if (value == 0) return 0; + if (value > 0) return 1; + return -1; + } + + public static float complexToDimensionNoisy(int data, DisplayMetrics metrics) + { + float res = complexToDimension(data, metrics); + System.out.println( + "Dimension (0x" + ((data>>TypedValue.COMPLEX_MANTISSA_SHIFT) + & TypedValue.COMPLEX_MANTISSA_MASK) + + "*" + (RADIX_MULTS[(data>>TypedValue.COMPLEX_RADIX_SHIFT) + & TypedValue.COMPLEX_RADIX_MASK] / MANTISSA_MULT) + + ")" + DIMENSION_UNIT_STRS[(data>>COMPLEX_UNIT_SHIFT) + & COMPLEX_UNIT_MASK] + + " = " + res); + return res; + } + + /** + * Converts an unpacked complex data value holding a dimension to its final floating + * point value. The two parameters <var>unit</var> and <var>value</var> + * are as in {@link #TYPE_DIMENSION}. + * + * @param unit The unit to convert from. + * @param value The value to apply the unit to. + * @param metrics Current display metrics to use in the conversion -- + * supplies display density and scaling information. + * + * @return The complex floating point value multiplied by the appropriate + * metrics depending on its unit. + */ + public static float applyDimension(int unit, float value, + DisplayMetrics metrics) + { + switch (unit) { + case COMPLEX_UNIT_PX: + return value; + case COMPLEX_UNIT_DIP: + return value * metrics.density; + case COMPLEX_UNIT_SP: + return value * metrics.scaledDensity; + case COMPLEX_UNIT_PT: + return value * metrics.xdpi * (1.0f/72); + case COMPLEX_UNIT_IN: + return value * metrics.xdpi; + case COMPLEX_UNIT_MM: + return value * metrics.xdpi * (1.0f/25.4f); + } + return 0; + } + + /** + * Return the data for this value as a dimension. Only use for values + * whose type is {@link #TYPE_DIMENSION}. + * + * @param metrics Current display metrics to use in the conversion -- + * supplies display density and scaling information. + * + * @return The complex floating point value multiplied by the appropriate + * metrics depending on its unit. + */ + public float getDimension(DisplayMetrics metrics) + { + return complexToDimension(data, metrics); + } + + /** + * Converts a complex data value holding a fraction to its final floating + * point value. The given <var>data</var> must be structured as a + * {@link #TYPE_FRACTION}. + * + * @param data A complex data value holding a unit, magnitude, and + * mantissa. + * @param base The base value of this fraction. In other words, a + * standard fraction is multiplied by this value. + * @param pbase The parent base value of this fraction. In other + * words, a parent fraction (nn%p) is multiplied by this + * value. + * + * @return The complex floating point value multiplied by the appropriate + * base value depending on its unit. + */ + public static float complexToFraction(int data, float base, float pbase) + { + switch ((data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK) { + case COMPLEX_UNIT_FRACTION: + return complexToFloat(data) * base; + case COMPLEX_UNIT_FRACTION_PARENT: + return complexToFloat(data) * pbase; + } + return 0; + } + + /** + * Return the data for this value as a fraction. Only use for values whose + * type is {@link #TYPE_FRACTION}. + * + * @param base The base value of this fraction. In other words, a + * standard fraction is multiplied by this value. + * @param pbase The parent base value of this fraction. In other + * words, a parent fraction (nn%p) is multiplied by this + * value. + * + * @return The complex floating point value multiplied by the appropriate + * base value depending on its unit. + */ + public float getFraction(float base, float pbase) + { + return complexToFraction(data, base, pbase); + } + + /** + * Regardless of the actual type of the value, try to convert it to a + * string value. For example, a color type will be converted to a + * string of the form #aarrggbb. + * + * @return CharSequence The coerced string value. If the value is + * null or the type is not known, null is returned. + */ + public final CharSequence coerceToString() + { + int t = type; + if (t == TYPE_STRING) { + return string; + } + return coerceToString(t, data); + } + + private static final String[] DIMENSION_UNIT_STRS = new String[] { + "px", "dip", "sp", "pt", "in", "mm" + }; + private static final String[] FRACTION_UNIT_STRS = new String[] { + "%", "%p" + }; + + /** + * Perform type conversion as per {@link #coerceToString()} on an + * explicitly supplied type and data. + * + * @param type The data type identifier. + * @param data The data value. + * + * @return String The coerced string value. If the value is + * null or the type is not known, null is returned. + */ + public static final String coerceToString(int type, int data) + { + switch (type) { + case TYPE_NULL: + return null; + case TYPE_REFERENCE: + return "@" + data; + case TYPE_ATTRIBUTE: + return "?" + data; + case TYPE_FLOAT: + return Float.toString(Float.intBitsToFloat(data)); + case TYPE_DIMENSION: + return Float.toString(complexToFloat(data)) + DIMENSION_UNIT_STRS[ + (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; + case TYPE_FRACTION: + return Float.toString(complexToFloat(data)*100) + FRACTION_UNIT_STRS[ + (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; + case TYPE_INT_HEX: + return "0x" + Integer.toHexString(data); + case TYPE_INT_BOOLEAN: + return data != 0 ? "true" : "false"; + } + + if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) { + return "#" + Integer.toHexString(data); + } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) { + return Integer.toString(data); + } + + return null; + } + + public void setTo(TypedValue other) + { + type = other.type; + string = other.string; + data = other.data; + assetCookie = other.assetCookie; + resourceId = other.resourceId; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("TypedValue{t=0x").append(Integer.toHexString(type)); + sb.append("/d=0x").append(Integer.toHexString(data)); + if (type == TYPE_STRING) { + sb.append(" \"").append(string != null ? string : "<null>").append("\""); + } + if (assetCookie != 0) { + sb.append(" a=").append(assetCookie); + } + if (resourceId != 0) { + sb.append(" r=0x").append(Integer.toHexString(resourceId)); + } + sb.append("}"); + return sb.toString(); + } +}; + diff --git a/core/java/android/util/Xml.java b/core/java/android/util/Xml.java new file mode 100644 index 0000000..a2b69a5 --- /dev/null +++ b/core/java/android/util/Xml.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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 android.util; + +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; + +import org.apache.harmony.xml.ExpatPullParser; +import org.apache.harmony.xml.ExpatReader; + +/** + * XML utility methods. + */ +public class Xml { + + /** + * {@link org.xmlpull.v1.XmlPullParser} "relaxed" feature name. + * + * @see <a href="http://xmlpull.org/v1/doc/features.html#relaxed"> + * specification</a> + */ + public static String FEATURE_RELAXED = ExpatPullParser.FEATURE_RELAXED; + + /** + * Parses the given xml string and fires events on the given SAX handler. + */ + public static void parse(String xml, ContentHandler contentHandler) + throws SAXException { + try { + XMLReader reader = new ExpatReader(); + reader.setContentHandler(contentHandler); + reader.parse(new InputSource(new StringReader(xml))); + } + catch (IOException e) { + throw new AssertionError(e); + } + } + + /** + * Parses xml from the given reader and fires events on the given SAX + * handler. + */ + public static void parse(Reader in, ContentHandler contentHandler) + throws IOException, SAXException { + XMLReader reader = new ExpatReader(); + reader.setContentHandler(contentHandler); + reader.parse(new InputSource(in)); + } + + /** + * Parses xml from the given input stream and fires events on the given SAX + * handler. + */ + public static void parse(InputStream in, Encoding encoding, + ContentHandler contentHandler) throws IOException, SAXException { + try { + XMLReader reader = new ExpatReader(); + reader.setContentHandler(contentHandler); + InputSource source = new InputSource(in); + source.setEncoding(encoding.expatName); + reader.parse(source); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + /** + * Creates a new pull parser with namespace support. + * + * <p><b>Note:</b> This is actually slower than the SAX parser, and it's not + * fully implemented. If you need a fast, mostly implemented pull parser, + * use this. If you need a complete implementation, use KXML. + */ + public static XmlPullParser newPullParser() { + ExpatPullParser parser = new ExpatPullParser(); + parser.setNamespaceProcessingEnabled(true); + return parser; + } + + /** + * Creates a new xml serializer. + */ + public static XmlSerializer newSerializer() { + try { + return XmlSerializerFactory.instance.newSerializer(); + } catch (XmlPullParserException e) { + throw new AssertionError(e); + } + } + + /** Factory for xml serializers. Initialized on demand. */ + static class XmlSerializerFactory { + static final String TYPE + = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer"; + static final XmlPullParserFactory instance; + static { + try { + instance = XmlPullParserFactory.newInstance(TYPE, null); + } catch (XmlPullParserException e) { + throw new AssertionError(e); + } + } + } + + /** + * Supported character encodings. + */ + public enum Encoding { + + US_ASCII("US-ASCII"), + UTF_8("UTF-8"), + UTF_16("UTF-16"), + ISO_8859_1("ISO-8859-1"); + + final String expatName; + + Encoding(String expatName) { + this.expatName = expatName; + } + } + + /** + * Finds an encoding by name. Returns UTF-8 if you pass {@code null}. + */ + public static Encoding findEncodingByName(String encodingName) + throws UnsupportedEncodingException { + if (encodingName == null) { + return Encoding.UTF_8; + } + + for (Encoding encoding : Encoding.values()) { + if (encoding.expatName.equalsIgnoreCase(encodingName)) + return encoding; + } + throw new UnsupportedEncodingException(encodingName); + } + + /** + * Return an AttributeSet interface for use with the given XmlPullParser. + * If the given parser itself implements AttributeSet, that implementation + * is simply returned. Otherwise a wrapper class is + * instantiated on top of the XmlPullParser, as a proxy for retrieving its + * attributes, and returned to you. + * + * @param parser The existing parser for which you would like an + * AttributeSet. + * + * @return An AttributeSet you can use to retrieve the + * attribute values at each of the tags as the parser moves + * through its XML document. + * + * @see AttributeSet + */ + public static AttributeSet asAttributeSet(XmlPullParser parser) { + return (parser instanceof AttributeSet) + ? (AttributeSet) parser + : new XmlPullAttributes(parser); + } +} diff --git a/core/java/android/util/XmlPullAttributes.java b/core/java/android/util/XmlPullAttributes.java new file mode 100644 index 0000000..12d6dd9 --- /dev/null +++ b/core/java/android/util/XmlPullAttributes.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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 android.util; + +import org.xmlpull.v1.XmlPullParser; + +import android.util.AttributeSet; +import com.android.internal.util.XmlUtils; + +/** + * Provides an implementation of AttributeSet on top of an XmlPullParser. + */ +class XmlPullAttributes implements AttributeSet { + public XmlPullAttributes(XmlPullParser parser) { + mParser = parser; + } + + public int getAttributeCount() { + return mParser.getAttributeCount(); + } + + public String getAttributeName(int index) { + return mParser.getAttributeName(index); + } + + public String getAttributeValue(int index) { + return mParser.getAttributeValue(index); + } + + public String getAttributeValue(String namespace, String name) { + return mParser.getAttributeValue(namespace, name); + } + + public String getPositionDescription() { + return mParser.getPositionDescription(); + } + + public int getAttributeNameResource(int index) { + return 0; + } + + public int getAttributeListValue(String namespace, String attribute, + String[] options, int defaultValue) { + return XmlUtils.convertValueToList( + getAttributeValue(namespace, attribute), options, defaultValue); + } + + public boolean getAttributeBooleanValue(String namespace, String attribute, + boolean defaultValue) { + return XmlUtils.convertValueToBoolean( + getAttributeValue(namespace, attribute), defaultValue); + } + + public int getAttributeResourceValue(String namespace, String attribute, + int defaultValue) { + return XmlUtils.convertValueToInt( + getAttributeValue(namespace, attribute), defaultValue); + } + + public int getAttributeIntValue(String namespace, String attribute, + int defaultValue) { + return XmlUtils.convertValueToInt( + getAttributeValue(namespace, attribute), defaultValue); + } + + public int getAttributeUnsignedIntValue(String namespace, String attribute, + int defaultValue) { + return XmlUtils.convertValueToUnsignedInt( + getAttributeValue(namespace, attribute), defaultValue); + } + + public float getAttributeFloatValue(String namespace, String attribute, + float defaultValue) { + String s = getAttributeValue(namespace, attribute); + if (s != null) { + return Float.parseFloat(s); + } + return defaultValue; + } + + public int getAttributeListValue(int index, + String[] options, int defaultValue) { + return XmlUtils.convertValueToList( + getAttributeValue(index), options, defaultValue); + } + + public boolean getAttributeBooleanValue(int index, boolean defaultValue) { + return XmlUtils.convertValueToBoolean( + getAttributeValue(index), defaultValue); + } + + public int getAttributeResourceValue(int index, int defaultValue) { + return XmlUtils.convertValueToInt( + getAttributeValue(index), defaultValue); + } + + public int getAttributeIntValue(int index, int defaultValue) { + return XmlUtils.convertValueToInt( + getAttributeValue(index), defaultValue); + } + + public int getAttributeUnsignedIntValue(int index, int defaultValue) { + return XmlUtils.convertValueToUnsignedInt( + getAttributeValue(index), defaultValue); + } + + public float getAttributeFloatValue(int index, float defaultValue) { + String s = getAttributeValue(index); + if (s != null) { + return Float.parseFloat(s); + } + return defaultValue; + } + + public String getIdAttribute() { + return getAttributeValue(null, "id"); + } + + public String getClassAttribute() { + return getAttributeValue(null, "class"); + } + + public int getIdAttributeResourceValue(int defaultValue) { + return getAttributeResourceValue(null, "id", defaultValue); + } + + public int getStyleAttribute() { + return getAttributeResourceValue(null, "style", 0); + } + + private XmlPullParser mParser; +} diff --git a/core/java/android/util/package.html b/core/java/android/util/package.html new file mode 100644 index 0000000..d918d69 --- /dev/null +++ b/core/java/android/util/package.html @@ -0,0 +1,6 @@ +<HTML> +<BODY> +Provides common utility methods such as date/time manipulation, base64 encoders +and decoders, string and number conversion methods, and XML utilities. +</BODY> +</HTML>
\ No newline at end of file |