10d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamypackage android.view; 20d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 30d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamyimport android.annotation.NonNull; 40d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamyimport android.annotation.Nullable; 50d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 60d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamyimport java.io.ByteArrayOutputStream; 70d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamyimport java.io.DataOutputStream; 80d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamyimport java.io.IOException; 90d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamyimport java.nio.charset.Charset; 100d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamyimport java.util.HashMap; 110d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamyimport java.util.Map; 120d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 130d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy/** 140d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * {@link ViewHierarchyEncoder} is a serializer that is tailored towards writing out 150d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * view hierarchies (the view tree, along with the properties for each view) to a stream. 160d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * 170d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * It is typically used as follows: 180d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * <pre> 190d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * ViewHierarchyEncoder e = new ViewHierarchyEncoder(); 200d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * 210d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * for (View view : views) { 220d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * e.beginObject(view); 230d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * e.addProperty("prop1", value); 240d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * ... 250d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * e.endObject(); 260d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * } 270d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * 280d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * // repeat above snippet for each view, finally end with: 290d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * e.endStream(); 300d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * </pre> 310d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * 320d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * <p>On the stream, a snippet such as the above gets encoded as a series of Map's (one 330d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * corresponding to each view) with the property name as the key and the property value 340d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * as the value. 350d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * 360d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * <p>Since the property names are practically the same across all views, rather than using 370d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * the property name directly as the key, we use a short integer id corresponding to each 380d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * property name as the key. A final map is added at the end which contains the mapping 390d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * from the integer to its property name. 400d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * 410d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * <p>A value is encoded as a single byte type identifier followed by the encoding of the 420d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * value. Only primitive types are supported as values, in addition to the Map type. 430d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * 440d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * @hide 450d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy */ 460d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamypublic class ViewHierarchyEncoder { 470d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy // Prefixes for simple primitives. These match the JNI definitions. 480d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private static final byte SIG_BOOLEAN = 'Z'; 490d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private static final byte SIG_BYTE = 'B'; 500d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private static final byte SIG_SHORT = 'S'; 510d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private static final byte SIG_INT = 'I'; 520d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private static final byte SIG_LONG = 'J'; 530d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private static final byte SIG_FLOAT = 'F'; 540d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private static final byte SIG_DOUBLE = 'D'; 550d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 560d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy // Prefixes for some commonly used objects 570d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private static final byte SIG_STRING = 'R'; 580d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 590d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private static final byte SIG_MAP = 'M'; // a map with an short key 600d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private static final short SIG_END_MAP = 0; 610d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 620d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private final DataOutputStream mStream; 630d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 640d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private final Map<String,Short> mPropertyNames = new HashMap<String, Short>(200); 650d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private short mPropertyId = 1; 660d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private Charset mCharset = Charset.forName("utf-8"); 670d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 680d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy public ViewHierarchyEncoder(@NonNull ByteArrayOutputStream stream) { 690d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream = new DataOutputStream(stream); 700d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 710d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 720d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy public void beginObject(@NonNull Object o) { 730d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy startPropertyMap(); 740d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy addProperty("meta:__name__", o.getClass().getName()); 750d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy addProperty("meta:__hash__", o.hashCode()); 760d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 770d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 780d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy public void endObject() { 790d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy endPropertyMap(); 800d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 810d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 820d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy public void endStream() { 830d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy // write out the string table 840d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy startPropertyMap(); 850d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy addProperty("__name__", "propertyIndex"); 860d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy for (Map.Entry<String,Short> entry : mPropertyNames.entrySet()) { 870d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeShort(entry.getValue()); 880d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeString(entry.getKey()); 890d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 900d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy endPropertyMap(); 910d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 920d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 930d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy public void addProperty(@NonNull String name, boolean v) { 940d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeShort(createPropertyIndex(name)); 950d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeBoolean(v); 960d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 970d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 980d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy public void addProperty(@NonNull String name, short s) { 990d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeShort(createPropertyIndex(name)); 1000d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeShort(s); 1010d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1020d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1030d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy public void addProperty(@NonNull String name, int v) { 1040d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeShort(createPropertyIndex(name)); 1050d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeInt(v); 1060d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1070d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1080d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy public void addProperty(@NonNull String name, float v) { 1090d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeShort(createPropertyIndex(name)); 1100d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeFloat(v); 1110d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1120d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1130d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy public void addProperty(@NonNull String name, @Nullable String s) { 1140d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeShort(createPropertyIndex(name)); 1150d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeString(s); 1160d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1170d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1180d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy /** 1190d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * Writes the given name as the property name, and leaves it to the callee 1200d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy * to fill in value for this property. 1210d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy */ 1220d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy public void addPropertyKey(@NonNull String name) { 1230d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeShort(createPropertyIndex(name)); 1240d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1250d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1260d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private short createPropertyIndex(@NonNull String name) { 1270d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy Short index = mPropertyNames.get(name); 1280d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy if (index == null) { 1290d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy index = mPropertyId++; 1300d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mPropertyNames.put(name, index); 1310d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1320d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1330d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy return index; 1340d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1350d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1360d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private void startPropertyMap() { 1370d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy try { 1380d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.write(SIG_MAP); 1390d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } catch (IOException e) { 1400d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy // does not happen since the stream simply wraps a ByteArrayOutputStream 1410d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1420d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1430d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1440d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private void endPropertyMap() { 1450d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy writeShort(SIG_END_MAP); 1460d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1470d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1480d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private void writeBoolean(boolean v) { 1490d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy try { 1500d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.write(SIG_BOOLEAN); 1510d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.write(v ? 1 : 0); 1520d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } catch (IOException e) { 1530d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy // does not happen since the stream simply wraps a ByteArrayOutputStream 1540d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1550d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1560d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1570d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private void writeShort(short s) { 1580d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy try { 1590d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.write(SIG_SHORT); 1600d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.writeShort(s); 1610d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } catch (IOException e) { 1620d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy // does not happen since the stream simply wraps a ByteArrayOutputStream 1630d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1640d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1650d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1660d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private void writeInt(int i) { 1670d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy try { 1680d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.write(SIG_INT); 1690d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.writeInt(i); 1700d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } catch (IOException e) { 1710d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy // does not happen since the stream simply wraps a ByteArrayOutputStream 1720d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1730d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1740d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1750d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private void writeFloat(float v) { 1760d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy try { 1770d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.write(SIG_FLOAT); 1780d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.writeFloat(v); 1790d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } catch (IOException e) { 1800d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy // does not happen since the stream simply wraps a ByteArrayOutputStream 1810d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1820d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1830d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1840d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy private void writeString(@Nullable String s) { 1850d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy if (s == null) { 1860d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy s = ""; 1870d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 1880d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1890d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy try { 1900d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.write(SIG_STRING); 1910d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy byte[] bytes = s.getBytes(mCharset); 1920d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1930d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy short len = (short)Math.min(bytes.length, Short.MAX_VALUE); 1940d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.writeShort(len); 1950d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy 1960d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy mStream.write(bytes, 0, len); 1970d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } catch (IOException e) { 1980d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy // does not happen since the stream simply wraps a ByteArrayOutputStream 1990d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 2000d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy } 2010d857b9028f2702ce439e13feccde8182d40e1e5Siva Velusamy} 202