diff options
| -rw-r--r-- | Android.bp | 4 | ||||
| -rw-r--r-- | api/current.txt | 80 | ||||
| -rw-r--r-- | core/java/android/view/inspector/ChildTraverser.java | 46 | ||||
| -rw-r--r-- | core/java/android/view/inspector/InspectableChildren.java | 46 | ||||
| -rw-r--r-- | core/java/android/view/inspector/InspectableNodeName.java | 2 | ||||
| -rw-r--r-- | core/java/android/view/inspector/InspectableProperty.java | 175 | ||||
| -rw-r--r-- | core/java/android/view/inspector/InspectionCompanion.java | 95 | ||||
| -rw-r--r-- | core/java/android/view/inspector/InspectionHelper.java | 145 | ||||
| -rw-r--r-- | core/java/android/view/inspector/IntEnumMapping.java | 118 | ||||
| -rw-r--r-- | core/java/android/view/inspector/IntFlagMapping.java | 155 | ||||
| -rw-r--r-- | core/java/android/view/inspector/PropertyMapper.java | 82 | ||||
| -rw-r--r-- | core/java/android/view/inspector/PropertyReader.java | 62 |
12 files changed, 753 insertions, 257 deletions
diff --git a/Android.bp b/Android.bp index cbc67d0e3e20..a59359c56a4c 100644 --- a/Android.bp +++ b/Android.bp @@ -781,9 +781,11 @@ java_library { java_library_host { name: "inspector-annotation", srcs: [ - "core/java/android/view/inspector/InspectableChildren.java", "core/java/android/view/inspector/InspectableNodeName.java", "core/java/android/view/inspector/InspectableProperty.java", + // Needed for the ResourceId.ID_NULL constant + "core/java/android/content/res/ResourceId.java", + "core/java/android/annotation/AnyRes.java", ], } diff --git a/api/current.txt b/api/current.txt index f9cebad7c080..ebf23394bf46 100644 --- a/api/current.txt +++ b/api/current.txt @@ -51982,6 +51982,86 @@ package android.view.inputmethod { } +package android.view.inspector { + + public abstract interface InspectionCompanion<T> { + method public default java.lang.String getNodeName(); + method public abstract void mapProperties(android.view.inspector.PropertyMapper); + method public abstract void readProperties(T, android.view.inspector.PropertyReader); + } + + public static class InspectionCompanion.UninitializedPropertyMapException extends java.lang.RuntimeException { + ctor public InspectionCompanion.UninitializedPropertyMapException(); + } + + public final class IntEnumMapping { + method public java.lang.String nameOf(int); + } + + public static final class IntEnumMapping.Builder { + ctor public IntEnumMapping.Builder(); + method public android.view.inspector.IntEnumMapping.Builder addValue(java.lang.String, int); + method public android.view.inspector.IntEnumMapping build(); + method public void clear(); + } + + public final class IntFlagMapping { + method public java.lang.String[] namesOf(int); + } + + public static final class IntFlagMapping.Builder { + ctor public IntFlagMapping.Builder(); + method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int); + method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int, int); + method public android.view.inspector.IntFlagMapping build(); + method public void clear(); + } + + public abstract interface PropertyMapper { + method public abstract int mapBoolean(java.lang.String, int); + method public abstract int mapByte(java.lang.String, int); + method public abstract int mapChar(java.lang.String, int); + method public abstract int mapColor(java.lang.String, int); + method public abstract int mapDouble(java.lang.String, int); + method public abstract int mapFloat(java.lang.String, int); + method public abstract int mapGravity(java.lang.String, int); + method public abstract int mapInt(java.lang.String, int); + method public abstract int mapIntEnum(java.lang.String, int, android.view.inspector.IntEnumMapping); + method public abstract int mapIntFlag(java.lang.String, int, android.view.inspector.IntFlagMapping); + method public abstract int mapLong(java.lang.String, int); + method public abstract int mapObject(java.lang.String, int); + method public abstract int mapShort(java.lang.String, int); + } + + public static class PropertyMapper.PropertyConflictException extends java.lang.RuntimeException { + ctor public PropertyMapper.PropertyConflictException(java.lang.String, java.lang.String, java.lang.String); + } + + public abstract interface PropertyReader { + method public abstract void readBoolean(int, boolean); + method public abstract void readByte(int, byte); + method public abstract void readChar(int, char); + method public abstract void readColor(int, int); + method public abstract void readColor(int, long); + method public abstract void readColor(int, android.graphics.Color); + method public abstract void readDouble(int, double); + method public abstract void readFloat(int, float); + method public abstract void readGravity(int, int); + method public abstract void readInt(int, int); + method public abstract void readIntEnum(int, int); + method public abstract void readIntFlag(int, int); + method public abstract void readLong(int, long); + method public abstract void readObject(int, java.lang.Object); + method public abstract void readShort(int, short); + } + + public static class PropertyReader.PropertyTypeMismatchException extends java.lang.RuntimeException { + ctor public PropertyReader.PropertyTypeMismatchException(int, java.lang.String, java.lang.String, java.lang.String); + ctor public PropertyReader.PropertyTypeMismatchException(int, java.lang.String, java.lang.String); + } + +} + package android.view.intelligence { public final class ContentCaptureManager { diff --git a/core/java/android/view/inspector/ChildTraverser.java b/core/java/android/view/inspector/ChildTraverser.java deleted file mode 100644 index b775de503d98..000000000000 --- a/core/java/android/view/inspector/ChildTraverser.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2018 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.view.inspector; - -import android.annotation.NonNull; - -/** - * Interface for visiting all the child nodes of an inspectable object. - * - * Inspectable objects may return a collection of children as an array, an {@link Iterable} or an - * {@link java.util.Iterator}. This provides a unified API for traversing across all the children - * of an inspectable node. - * - * This interface is consumed by {@link InspectionHelper#traverseChildren(Object, ChildTraverser)} - * and may be implemented as a lambda. - * - * @see InspectionHelper#traverseChildren(Object, ChildTraverser) - * @hide - */ -@FunctionalInterface -public interface ChildTraverser { - /** - * Visit one child object of a parent inspectable object. - * - * The iteration interface will filter null values out before passing them to this method, but - * some child objects may not be inspectable. It is up to the implementor to determine their - * inspectablity and what to do with them. - * - * @param child A child object, guaranteed not to be null. - */ - void traverseChild(@NonNull Object child); -} diff --git a/core/java/android/view/inspector/InspectableChildren.java b/core/java/android/view/inspector/InspectableChildren.java deleted file mode 100644 index de8fa296e8c5..000000000000 --- a/core/java/android/view/inspector/InspectableChildren.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2018 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.view.inspector; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Marks a getter for an inspectable node's inspectable children. - * - * This annotation can be applied to any getter that returns a collection of objects, either an - * array, an {@link Iterable} or a {@link java.util.Iterator}. The getter may return null, which - * will be treated as an empty collection. Additionally, the inspector will discard any null - * entries in the collection. - * - * By default, this annotation is inherited. At runtime, the inspector introspects on the class - * hierachy and uses the annotated getter from the bottommost class, if different from any - * annoated getters of the parent class. If a class inherits from a parent class with an annotated - * getter, but does not include this annotation, the child class will be traversed using the - * getter annotated on the parent. This holds true even if the child class overrides the getter. - * - * @see InspectionHelper#traverseChildren(Object, ChildTraverser) - * @see InspectionHelper#hasChildTraversal() - * @hide - */ -@Target({METHOD}) -@Retention(SOURCE) -public @interface InspectableChildren { -} diff --git a/core/java/android/view/inspector/InspectableNodeName.java b/core/java/android/view/inspector/InspectableNodeName.java index 716409c9af3a..ea94ad4c5df8 100644 --- a/core/java/android/view/inspector/InspectableNodeName.java +++ b/core/java/android/view/inspector/InspectableNodeName.java @@ -34,7 +34,7 @@ import java.lang.annotation.Target; * This annotation does not inherit. If a class extends an annotated parent class, but does not * annotate itself, its node name will be inferred from its Java name. * - * @see InspectionHelper#getNodeName() + * @see InspectionCompanion#getNodeName() * @hide */ @Target({TYPE}) diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java index b0fd5032ba56..5b95715681fc 100644 --- a/core/java/android/view/inspector/InspectableProperty.java +++ b/core/java/android/view/inspector/InspectableProperty.java @@ -17,8 +17,11 @@ package android.view.inspector; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.SOURCE; +import android.content.res.ResourceId; + import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -31,8 +34,8 @@ import java.lang.annotation.Target; * but on a different getter, the inspector will use the child's getter when inspecting instances * of the child, and the parent's otherwise. * - * @see InspectionHelper#mapProperties(PropertyMapper) - * @see InspectionHelper#readProperties(Object, PropertyReader) + * @see InspectionCompanion#mapProperties(PropertyMapper) + * @see InspectionCompanion#readProperties(Object, PropertyReader) * @hide */ @Target({METHOD}) @@ -46,5 +49,171 @@ public @interface InspectableProperty { * * @return The name of the property. */ - String value() default ""; + String name() default ""; + + /** + * If the property is inflated from XML, the resource ID of its XML attribute. + * + * If left as {ID_NULL}, and {@link #hasAttributeId()} is true, the attribute ID will be + * inferred from {@link #name()}. + * + * @return The attribute ID of the property or {@link ResourceId#ID_NULL} + */ + int attributeId() default ResourceId.ID_NULL; + + /** + * If this property has an attribute ID. + * + * Set to false if the annotated property does not have an attribute ID, that is, it is not + * inflated from an XML attribute. This will prevent the automatic inference of the attribute + * ID if {@link #attributeId()} is set to {@link ResourceId#ID_NULL}. + * + * @return Whether to infer an attribute ID if not supplied + */ + boolean hasAttributeId() default true; + + /** + * Specify how to interpret a value type packed into a primitive integer. + * + * @return A {@link ValueType} + */ + ValueType valueType() default ValueType.INFERRED; + + /** + * For enumerations packed into primitive {int} properties, map the values to string names. + * + * Note that {@link #enumMapping()} cannot be used simultaneously with {@link #flagMapping()}. + * + * @return An array of {@link EnumMap}, empty if not applicable + * @see android.annotation.IntDef + * @see IntEnumMapping + */ + EnumMap[] enumMapping() default {}; + + /** + * For flags packed into primitive {int} properties, model the string names of the flags. + * + * Note that {@link #flagMapping()} cannot be used simultaneously with {@link #enumMapping()}. + * + * @return An array of {@link FlagMap}, empty if not applicable + * @see android.annotation.IntDef + * @see IntFlagMapping + */ + FlagMap[] flagMapping() default {}; + + + /** + * One entry in an enumeration packed into a primitive {int}. + * + * @see IntEnumMapping + * @hide + */ + @Target({TYPE}) + @Retention(SOURCE) + @interface EnumMap { + /** + * The string name of this enumeration value. + * + * @return A string name + */ + String name(); + + /** + * The integer value of this enumeration value. + * + * @return An integer value + */ + int value(); + } + + /** + * One flag value of many that may be packed into a primitive {int}. + * + * @see IntFlagMapping + * @hide + */ + @Target({TYPE}) + @Retention(SOURCE) + @interface FlagMap { + /** + * The string name of this flag. + * + * @return A string name + */ + String name(); + + /** + * A target value that the property's value must equal after masking. + * + * If a mask is not supplied (i.e., {@link #mask()} is 0), the target will be reused as the + * mask. This handles the common case where no flags mutually exclude each other. + * + * @return The target value to compare against + */ + int target(); + + /** + * A mask that the property will be bitwise anded with before comparing to the target. + * + * If set to 0 (the default), the value of {@link #target()} will be used as a mask. Zero + * was chosen as the default since bitwise and with zero is always zero. + * + * @return A mask, or 0 to use the target as a mask + */ + int mask() default 0; + } + + /** + * The type of value packed into a primitive {int}. + * + * @hide + */ + enum ValueType { + /** + * No special handling, property is considered to be a numeric value. + */ + NONE, + + /** + * The default the annotation processor infers the value type from context. + */ + INFERRED, + + /** + * Value packs an enumeration. + * + * This is inferred if {@link #enumMapping()} is specified. + * + * @see EnumMap + */ + INT_ENUM, + + /** + * Value packs flags, of which many may be enabled at once. + * + * This is inferred if {@link #flagMapping()} is specified. + * + * @see FlagMap + */ + INT_FLAG, + + /** + * Value packs color information. + * + * This is inferred from {@link android.annotation.ColorInt}, or + * {@link android.annotation.ColorLong} on the getter method. + * + * @see android.graphics.Color + */ + COLOR, + + /** + * Value packs gravity information. + * + * This type is not inferred, and is non-trivial to represent using {@link FlagMap}. + * + * @see android.view.Gravity + */ + GRAVITY + } } diff --git a/core/java/android/view/inspector/InspectionCompanion.java b/core/java/android/view/inspector/InspectionCompanion.java new file mode 100644 index 000000000000..ce0aee8f2d84 --- /dev/null +++ b/core/java/android/view/inspector/InspectionCompanion.java @@ -0,0 +1,95 @@ +/* + * Copyright 2018 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.view.inspector; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +/** + * An interface for companion objects used to inspect views. + * + * Inspection companions only need to handle the properties and node name of the specific class + * they are defined for, not anything from a parent class. At runtime, the inspector instantiates + * one instance of each inspection companion, and handles visiting them in the correct inheritance + * order for each type it inspects. + * + * Properties are read from the top of the type tree to the bottom, so that classes that override + * a property in their parent class can overwrite it in the reader. In general, properties will + * cleanly inherit through their getters, and the inspector runtime will read the properties of a + * parent class via the parent's inspection companion, and the child companion will only read + * properties added or changed since the parent was defined. + * + * Only one child traversal is considered for each class. If a descendant class defines a + * different child traversal than its parent, only the bottom traversal is used. If a class does + * not define its own child traversal, but one of its ancestors does, the bottom-most ancestor's + * traversal will be used. + * + * @param <T> The type of inspectable this is the companion to + */ +public interface InspectionCompanion<T> { + /** + * Map the string names of the properties this companion knows about to integer IDs. + * + * Each companion is responsible for storing the integer IDs of all its properties. This is the + * only method that is allowed to modify the stored IDs. + * + * Calling {@link #readProperties(T, PropertyReader)} before calling this results in + * undefined behavior. + * + * @param propertyMapper A {@link PropertyMapper} maps string names to IDs. + */ + void mapProperties(@NonNull PropertyMapper propertyMapper); + + /** + * Read the values of an instance of this companion's type into a {@link PropertyReader}. + * + * This method needs to return the property IDs stored by + * {@link #mapProperties(PropertyMapper)}. Implementations should track if their properties + * have been mapped and throw a {@link UninitializedPropertyMapException} if this method is + * called before {mapProperties}. + * + * @param inspectable A object of type {@link T} to read the properties of. + * @param propertyReader An object which receives the property IDs and values. + */ + void readProperties(@NonNull T inspectable, @NonNull PropertyReader propertyReader); + + /** + * Get an optional name to display to developers for inspection nodes of this companion's type. + * + * The default implementation returns null, which will cause the runtime to use the class's + * simple name as defined by {@link Class#getSimpleName()} as the node name. + * + * If the type of this companion is inflated from XML, this method should be overridden to + * return the string used as the tag name for this type in XML. + * + * @return A string to use as the node name, or null to use the simple class name fallback. + */ + @Nullable + default String getNodeName() { + return null; + } + + /** + * Thrown by {@link #readProperties(Object, PropertyReader)} if called before + * {@link #mapProperties(PropertyMapper)}. + */ + class UninitializedPropertyMapException extends RuntimeException { + public UninitializedPropertyMapException() { + super("Unable to read properties of an inspectable before mapping their IDs."); + } + } +} diff --git a/core/java/android/view/inspector/InspectionHelper.java b/core/java/android/view/inspector/InspectionHelper.java deleted file mode 100644 index 27a97040926c..000000000000 --- a/core/java/android/view/inspector/InspectionHelper.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2018 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.view.inspector; - -import android.annotation.NonNull; -import android.annotation.Nullable; - -/** - * An interface for companion objects used to inspect views. - * - * Inspection helpers only need to handle the properties, name and traversal of the specific class - * they are defined for, not anything from a parent class. At runtime, the inspector instantiates - * one instance of each inspection helper, and handles visiting them in the correct inheritance - * order for each type it inspects. - * - * Properties are read from the top of the type tree to the bottom, so that classes that override - * a property in their parent class can overwrite it in the reader. In general, properties will - * cleanly inherit through their getters, and the inspector runtime will read the properties of a - * parent class via the parent's inspection helper, and the child helper will only read properties - * added or changed since the parent was defined. - * - * Only one child traversal is considered for each class. If a descendant class defines a - * different child traversal than its parent, only the bottom traversal is used. If a class does - * not define its own child traversal, but one of its ancestors does, the bottom-most ancestor's - * traversal will be used. - * - * @param <T> The type of inspectable this helper operates on - * @hide - */ -public interface InspectionHelper<T> { - /** - * Map the string names of the properties this helper knows about to integer IDs. - * - * Each helper is responsible for storing the integer IDs of all its properties. This is the - * only method that is allowed to modify the stored IDs. - * - * Calling {@link #readProperties(T, PropertyReader)} before calling this results in - * undefined behavior. - * - * @param propertyMapper A {@link PropertyMapper} or lambda which maps string names to IDs. - */ - void mapProperties(@NonNull PropertyMapper propertyMapper); - - /** - * Read the values of an instance of this helper's type into a {@link PropertyReader}. - * - * This method needs to return the property IDs stored by - * {@link #mapProperties(PropertyMapper)}. Implementations should track if their properties - * have been mapped and throw a {@link UninitializedPropertyMapException} if this method is - * called before {mapProperties}. - * - * @param inspectable A object of type {@link T} to read the properties of. - * @param propertyReader An object which receives the property IDs and values. - */ - void readProperties(@NonNull T inspectable, @NonNull PropertyReader propertyReader); - - /** - * Query if this inspectable type can potentially have child nodes. - * - * E.g.: any descendant of {@link android.view.ViewGroup} can have child nodes, but a leaf - * view like {@link android.widget.ImageView} may not. - * - * The default implementation always returns false. If an implementing class overrides this, it - * should also define {@link #traverseChildren(T, ChildTraverser)}. - * - * @return True if this inspectable type can potentially have child nodes, false otherwise. - */ - default boolean hasChildTraversal() { - return false; - } - - /** - * Traverse the child nodes of an instance of this helper's type into a {@link ChildTraverser}. - * - * This provides the ability to traverse over a variety of collection APIs (e.g.: arrays, - * {@link Iterable}, or {@link java.util.Iterator}) in a uniform fashion. The traversal must be - * in the order defined by this helper's type. If the getter returns null, the helper must - * treat it as an empty collection. - * - * The default implementation throws a {@link NoChildTraversalException}. If - * {@link #hasChildTraversal()} returns is overriden to return true, it is expected that the - * implementing class will also override this method and provide a traversal. - * - * @param inspectable An object of type {@link T} to traverse the child nodes of. - * @param childTraverser A {@link ChildTraverser} or lamba to receive the children in order. - * @throws NoChildTraversalException If there is no defined child traversal - */ - default void traverseChildren( - @NonNull T inspectable, - @SuppressWarnings("unused") @NonNull ChildTraverser childTraverser) { - throw new NoChildTraversalException(inspectable.getClass()); - } - - /** - * Get an optional name to display to developers for inspection nodes of this helper's type. - * - * The default implementation returns null, which will cause the runtime to use the class's - * simple name as defined by {@link Class#getSimpleName()} as the node name. - * - * If the type of this helper is inflated from XML, this method should be overridden to return - * the string used as the tag name for this type in XML. - * - * @return A string to use as the node name, or null to use the simple class name fallback. - */ - @Nullable - default String getNodeName() { - return null; - } - - /** - * Thrown by {@link #readProperties(Object, PropertyReader)} if called before - * {@link #mapProperties(PropertyMapper)}. - */ - class UninitializedPropertyMapException extends RuntimeException { - public UninitializedPropertyMapException() { - super("Unable to read properties of an inspectable before mapping their IDs."); - } - } - - /** - * Thrown by {@link #traverseChildren(Object, ChildTraverser)} if no child traversal exists. - */ - class NoChildTraversalException extends RuntimeException { - public NoChildTraversalException(Class cls) { - super(String.format( - "Class %s does not have a defined child traversal. Cannot traverse children.", - cls.getCanonicalName() - )); - } - } -} diff --git a/core/java/android/view/inspector/IntEnumMapping.java b/core/java/android/view/inspector/IntEnumMapping.java new file mode 100644 index 000000000000..69f6dce94a81 --- /dev/null +++ b/core/java/android/view/inspector/IntEnumMapping.java @@ -0,0 +1,118 @@ +/* + * Copyright 2018 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.view.inspector; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.util.ArrayList; + +/** + * Maps the values of an {int} property to string names for properties that encode enumerations. + * + * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper} + * for enumerations packed into primitive {int} properties. + * + * This class is immutable, and must be constructed by a {@link Builder}. + * + * @see PropertyMapper#mapIntEnum(String, int, IntEnumMapping) + */ +public final class IntEnumMapping { + private final Value[] mValues; + + /** + * Map from a property value to a string name. + * + * @param value The value of a property + * @return The name of the enumeration value, null if the value is not mapped + */ + @Nullable + public String nameOf(int value) { + for (Value valueTuple : mValues) { + if (valueTuple.mValue == value) { + return valueTuple.mName; + } + } + + return null; + } + + /** + * Create a new instance from a builder. + * + * This constructor is private, use {@link Builder#build()} instead. + * + * @param builder A builder to create from + */ + private IntEnumMapping(Builder builder) { + mValues = builder.mValues.toArray(new Value[builder.mValues.size()]); + } + + /** + * A builder for {@link IntEnumMapping} + */ + public static final class Builder { + private final ArrayList<Value> mValues; + + public Builder() { + mValues = new ArrayList<>(); + } + + /** + * Add a new entry to this mapping. + * + * @param name Name of the enumeration value + * @param value Int value of the enumeration value + * @return This builder + */ + @NonNull + public Builder addValue(@NonNull String name, int value) { + mValues.add(new Value(name, value)); + return this; + } + + /** + * Clear the builder, allowing for recycling. + */ + public void clear() { + mValues.clear(); + } + + /** + * Build a new {@link IntEnumMapping} from this builder + * + * @return A new mapping + */ + @NonNull + public IntEnumMapping build() { + return new IntEnumMapping(this); + } + } + + /** + * Inner class that holds the name and value of an enumeration value. + */ + private static final class Value { + @NonNull private final String mName; + private final int mValue; + + private Value(@NonNull String name, int value) { + mName = name; + mValue = value; + } + } +} diff --git a/core/java/android/view/inspector/IntFlagMapping.java b/core/java/android/view/inspector/IntFlagMapping.java new file mode 100644 index 000000000000..dcb87e18ae5e --- /dev/null +++ b/core/java/android/view/inspector/IntFlagMapping.java @@ -0,0 +1,155 @@ +/* + * Copyright 2018 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.view.inspector; + +import android.annotation.NonNull; + +import java.util.ArrayList; + +/** + * Maps the values of an {int} property to arrays of string for properties that encode flags. + * + * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper} + * for flag values packed into primitive {int} properties. + * + * Each flag has a + * + * This class is immutable, and must be constructed by a {@link Builder}. + * + * @see PropertyMapper#mapIntFlag(String, int, IntFlagMapping) + */ +public final class IntFlagMapping { + private final Flag[] mFlags; + + /** + * Get an array of the names of enabled flags for a given property value. + * + * @param value The value of the property + * @return The names of the enabled flags + */ + @NonNull + public String[] namesOf(int value) { + ArrayList<String> enabledFlagNames = new ArrayList<>(mFlags.length); + + for (Flag flag : mFlags) { + if (flag.isEnabledFor(value)) { + enabledFlagNames.add(flag.mName); + } + } + + return enabledFlagNames.toArray(new String[enabledFlagNames.size()]); + } + + /** + * Create a new instance from a builder. + * + * This constructor is private, use {@link Builder#build()} instead. + * + * @param builder A builder to create from + */ + private IntFlagMapping(Builder builder) { + mFlags = builder.mFlags.toArray(new Flag[builder.mFlags.size()]); + } + + /** + * A builder for {@link IntFlagMapping}. + */ + public static final class Builder { + private ArrayList<Flag> mFlags; + + public Builder() { + mFlags = new ArrayList<>(); + } + + /** + * Add a new flag without a mask. + * + * The target value will be used as a mask, to handle the common case where flag values + * are not mutually exclusive. The flag will be considered enabled for a property value if + * the result of bitwise anding the target and the value equals the target, that is: + * {(value & target) == target}. + * + * @param name The name of the flag + * @param target The value to compare against + * @return This builder + */ + @NonNull + public Builder addFlag(@NonNull String name, int target) { + mFlags.add(new Flag(name, target, target)); + return this; + } + + /** + * Add a new flag with a mask. + * + * The flag will be considered enabled for a property value if the result of bitwise anding + * the value and the mask equals the target, that is: {(value & mask) == target}. + * + * @param name The name of the flag + * @param target The value to compare against + * @param mask A bit mask + * @return This builder + */ + @NonNull + public Builder addFlag(@NonNull String name, int target, int mask) { + mFlags.add(new Flag(name, target, mask)); + return this; + } + + /** + * Clear the builder, allowing for recycling. + */ + public void clear() { + mFlags.clear(); + } + + /** + * Build a new {@link IntFlagMapping} from this builder. + * + * @return A new mapping + */ + @NonNull + public IntFlagMapping build() { + return new IntFlagMapping(this); + } + } + + /** + * Inner class that holds the name, mask, and target value of a flag + */ + private static final class Flag { + @NonNull private final String mName; + private final int mTarget; + private final int mMask; + + private Flag(@NonNull String name, int target, int mask) { + mName = name; + mTarget = target; + mMask = mask; + } + + /** + * Compare the supplied property value against the mask and taget. + * + * @param value The value to check + * @return True if this flag is enabled + */ + private boolean isEnabledFor(int value) { + return (value & mMask) == mTarget; + } + } +} diff --git a/core/java/android/view/inspector/PropertyMapper.java b/core/java/android/view/inspector/PropertyMapper.java index 35550bd45b30..5fb291b34219 100644 --- a/core/java/android/view/inspector/PropertyMapper.java +++ b/core/java/android/view/inspector/PropertyMapper.java @@ -16,102 +16,160 @@ package android.view.inspector; +import android.annotation.AttrRes; import android.annotation.NonNull; /** * An interface for mapping the string names of inspectable properties to integer identifiers. * - * This interface is consumed by {@link InspectionHelper#mapProperties(PropertyMapper)}. + * This interface is consumed by {@link InspectionCompanion#mapProperties(PropertyMapper)}. * * Mapping properties to IDs enables quick comparisons against shadow copies of inspectable * objects without performing a large number of string comparisons. * - * @see InspectionHelper#mapProperties(PropertyMapper) - * @hide + * @see InspectionCompanion#mapProperties(PropertyMapper) */ public interface PropertyMapper { /** * Map a string name to an integer ID for a primitive boolean property. * * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property * @return An integer ID for the property * @throws PropertyConflictException If the property name is already mapped as another type. */ - int mapBoolean(@NonNull String name); + int mapBoolean(@NonNull String name, @AttrRes int attributeId); /** * Map a string name to an integer ID for a primitive byte property. * * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property * @return An integer ID for the property * @throws PropertyConflictException If the property name is already mapped as another type. */ - int mapByte(@NonNull String name); + int mapByte(@NonNull String name, @AttrRes int attributeId); /** * Map a string name to an integer ID for a primitive char property. * * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property * @return An integer ID for the property * @throws PropertyConflictException If the property name is already mapped as another type. */ - int mapChar(@NonNull String name); + int mapChar(@NonNull String name, @AttrRes int attributeId); /** * Map a string name to an integer ID for a primitive double property. * * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property * @return An integer ID for the property * @throws PropertyConflictException If the property name is already mapped as another type. */ - int mapDouble(@NonNull String name); + int mapDouble(@NonNull String name, @AttrRes int attributeId); /** * Map a string name to an integer ID for a primitive float property. * * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property * @return An integer ID for the property * @throws PropertyConflictException If the property name is already mapped as another type. */ - int mapFloat(@NonNull String name); + int mapFloat(@NonNull String name, @AttrRes int attributeId); /** * Map a string name to an integer ID for a primitive int property. * * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property * @return An integer ID for the property * @throws PropertyConflictException If the property name is already mapped as another type. */ - int mapInt(@NonNull String name); + int mapInt(@NonNull String name, @AttrRes int attributeId); /** * Map a string name to an integer ID for a primitive long property. * * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property * @return An integer ID for the property * @throws PropertyConflictException If the property name is already mapped as another type. */ - int mapLong(@NonNull String name); + int mapLong(@NonNull String name, @AttrRes int attributeId); /** * Map a string name to an integer ID for a primitive short property. * * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property * @return An integer ID for the property * @throws PropertyConflictException If the property name is already mapped as another type. */ - int mapShort(@NonNull String name); + int mapShort(@NonNull String name, @AttrRes int attributeId); /** * Map a string name to an integer ID for an object property. * * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property * @return An integer ID for the property * @throws PropertyConflictException If the property name is already mapped as another type. */ - int mapObject(@NonNull String name); + int mapObject(@NonNull String name, @AttrRes int attributeId); /** + * Map a string name to an integer ID for a color property. + * + * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property + * @return An integer ID for the property + * @throws PropertyConflictException If the property name is already mapped as another type. + * @see android.graphics.Color + */ + int mapColor(@NonNull String name, @AttrRes int attributeId); + + /** + * Map a string name to an integer ID for a gravity property. + * + * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property + * @return An integer ID for the property + * @throws PropertyConflictException If the property name is already mapped as another type. + * @see android.view.Gravity + */ + int mapGravity(@NonNull String name, @AttrRes int attributeId); + + /** + * Map a string name to an integer ID for an enumeration packed into an int property. + * + * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property + * @param mapping A mapping from int to String + * @return An integer ID for the property + * @throws PropertyConflictException If the property name is already mapped as another type. + */ + int mapIntEnum( + @NonNull String name, + @AttrRes int attributeId, + @NonNull IntEnumMapping mapping); + + /** + * Map a string name to an integer ID for a flag set packed into an int property. + * + * @param name The name of the property + * @param attributeId If the property is from an XML attribute, the resource ID of the property + * @param mapping A mapping from int to an array of strings + * @return An integer ID for the property + * @throws PropertyConflictException If the property name is already mapped as another type. + */ + int mapIntFlag( + @NonNull String name, + @AttrRes int attributeId, + @NonNull IntFlagMapping mapping); + /** * Thrown from a map method if a property name is already mapped as different type. */ class PropertyConflictException extends RuntimeException { diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java index df81c102dbad..fd83e8df6c3a 100644 --- a/core/java/android/view/inspector/PropertyReader.java +++ b/core/java/android/view/inspector/PropertyReader.java @@ -16,19 +16,21 @@ package android.view.inspector; +import android.annotation.ColorInt; +import android.annotation.ColorLong; import android.annotation.NonNull; import android.annotation.Nullable; +import android.graphics.Color; /** * An interface for reading the properties of an inspectable object. * - * Used as the parameter for {@link InspectionHelper#readProperties(Object, PropertyReader)}. + * Used as the parameter for {@link InspectionCompanion#readProperties(Object, PropertyReader)}. * It has separate methods for all primitive types to avoid autoboxing overhead if a concrete * implementation is able to work with primitives. Implementations should be prepared to accept * {null} as the value of {@link PropertyReader#readObject(int, Object)}. * - * @see InspectionHelper#readProperties(Object, PropertyReader) - * @hide + * @see InspectionCompanion#readProperties(Object, PropertyReader) */ public interface PropertyReader { /** @@ -115,6 +117,60 @@ public interface PropertyReader { void readObject(int id, @Nullable Object value); /** + * Read a color packed into a {@link ColorInt} as a property. + * + * @param id Identifier of the property from a {@link PropertyMapper} + * @param value Value of the property + * @throws PropertyTypeMismatchException If the property ID is not mapped as a color + */ + void readColor(int id, @ColorInt int value); + + /** + * Read a color packed into a {@link ColorLong} as a property. + * + * @param id Identifier of the property from a {@link PropertyMapper} + * @param value Value of the property + * @throws PropertyTypeMismatchException If the property ID is not mapped as a color + */ + void readColor(int id, @ColorLong long value); + + /** + * Read a {@link Color} object as a property. + * + * @param id Identifier of the property from a {@link PropertyMapper} + * @param value Value of the property + * @throws PropertyTypeMismatchException If the property ID is not mapped as a color + */ + void readColor(int id, @Nullable Color value); + + /** + * Read {@link android.view.Gravity} packed into an primitive {int}. + * + * @param id Identifier of the property from a {@link PropertyMapper} + * @param value Value of the property + * @throws PropertyTypeMismatchException If the property ID is not mapped as a gravity property + */ + void readGravity(int id, int value); + + /** + * Read an enumeration packed into a primitive {int}. + * + * @param id Identifier of the property from a {@link PropertyMapper} + * @param value Value of the property + * @throws PropertyTypeMismatchException If the property ID is not mapped as an object + */ + void readIntEnum(int id, int value); + + /** + * Read a flag packed into a primitive {int}. + * + * @param id Identifier of the property from a {@link PropertyMapper} + * @param value Value of the property + * @throws PropertyTypeMismatchException If the property ID is not mapped as an object + */ + void readIntFlag(int id, int value); + + /** * Thrown if a client calls a typed read method for a property of a different type. */ class PropertyTypeMismatchException extends RuntimeException { |