Home | History | Annotate | Download | only in shadows
      1 package org.robolectric.shadows;
      2 
      3 import static org.robolectric.res.android.AttributeResolution.STYLE_ASSET_COOKIE;
      4 import static org.robolectric.res.android.AttributeResolution.STYLE_CHANGING_CONFIGURATIONS;
      5 import static org.robolectric.res.android.AttributeResolution.STYLE_DATA;
      6 import static org.robolectric.res.android.AttributeResolution.STYLE_DENSITY;
      7 import static org.robolectric.res.android.AttributeResolution.STYLE_NUM_ENTRIES;
      8 import static org.robolectric.res.android.AttributeResolution.STYLE_RESOURCE_ID;
      9 import static org.robolectric.res.android.AttributeResolution.STYLE_TYPE;
     10 import static org.robolectric.shadow.api.Shadow.directlyOn;
     11 
     12 import android.annotation.StyleableRes;
     13 import android.content.res.Resources;
     14 import android.content.res.TypedArray;
     15 import android.os.Build;
     16 import android.util.TypedValue;
     17 import com.google.common.base.Strings;
     18 import com.google.common.collect.ImmutableMap;
     19 import org.robolectric.RuntimeEnvironment;
     20 import org.robolectric.annotation.HiddenApi;
     21 import org.robolectric.annotation.Implementation;
     22 import org.robolectric.annotation.Implements;
     23 import org.robolectric.annotation.RealObject;
     24 import org.robolectric.shadow.api.Shadow;
     25 import org.robolectric.util.ReflectionHelpers;
     26 import org.robolectric.util.ReflectionHelpers.ClassParameter;
     27 
     28 @SuppressWarnings({"UnusedDeclaration"})
     29 @Implements(TypedArray.class)
     30 public class ShadowTypedArray {
     31   @RealObject private TypedArray realTypedArray;
     32   private CharSequence[] stringData;
     33   public String positionDescription;
     34 
     35   public static TypedArray create(Resources realResources, int[] attrs, int[] data, int[] indices, int len, CharSequence[] stringData) {
     36     TypedArray typedArray;
     37     if (RuntimeEnvironment.getApiLevel() >= Build.VERSION_CODES.O) {
     38       typedArray = ReflectionHelpers.callConstructor(TypedArray.class,
     39           ClassParameter.from(Resources.class, realResources));
     40       ReflectionHelpers.setField(typedArray, "mData", data);
     41       ReflectionHelpers.setField(typedArray, "mLength", len);
     42       ReflectionHelpers.setField(typedArray, "mIndices", indices);
     43     } else {
     44       typedArray = ReflectionHelpers.callConstructor(TypedArray.class,
     45           ClassParameter.from(Resources.class, realResources),
     46           ClassParameter.from(int[].class, data),
     47           ClassParameter.from(int[].class, indices),
     48           ClassParameter.from(int.class, len));
     49     }
     50 
     51     ShadowTypedArray shadowTypedArray = Shadow.extract(typedArray);
     52     shadowTypedArray.stringData = stringData;
     53     return typedArray;
     54   }
     55 
     56   @HiddenApi @Implementation
     57   protected CharSequence loadStringValueAt(int index) {
     58     if (ShadowAssetManager.useLegacy()) {
     59       return stringData[index / STYLE_NUM_ENTRIES];
     60     } else {
     61       return directlyOn(realTypedArray, TypedArray.class, "loadStringValueAt",
     62           new ClassParameter(int.class, index));
     63     }
     64   }
     65 
     66   @Implementation
     67   protected String getNonResourceString(@StyleableRes int index) {
     68     return directlyOn(realTypedArray, TypedArray.class).getString(index);
     69   }
     70 
     71   @Implementation
     72   protected String getNonConfigurationString(@StyleableRes int index, int allowedChangingConfigs) {
     73     return directlyOn(realTypedArray, TypedArray.class).getString(index);
     74   }
     75 
     76   @Implementation
     77   protected String getPositionDescription() {
     78     if (ShadowAssetManager.useLegacy()) {
     79       return positionDescription;
     80     } else {
     81       return directlyOn(realTypedArray, TypedArray.class, "getPositionDescription");
     82     }
     83   }
     84 
     85   public static void dump(TypedArray typedArray) {
     86     int[] data = ReflectionHelpers.getField(typedArray, "mData");
     87 
     88     StringBuilder result = new StringBuilder();
     89     for (int index = 0; index < data.length; index+= STYLE_NUM_ENTRIES) {
     90       final int type = data[index+STYLE_TYPE];
     91       result.append("Index: ").append(index / STYLE_NUM_ENTRIES).append(System.lineSeparator());
     92       result.append(Strings.padEnd("Type: ", 25, ' ')).append(TYPE_MAP.get(type)).append(System.lineSeparator());
     93       if (type != TypedValue.TYPE_NULL) {
     94         result.append(Strings.padEnd("Style data: ", 25, ' ')).append(data[index+ STYLE_DATA]).append(System.lineSeparator());
     95         result.append(Strings.padEnd("Asset cookie ", 25, ' ')).append(data[index+STYLE_ASSET_COOKIE]).append(System.lineSeparator());
     96         result.append(Strings.padEnd("Style resourceId: ", 25, ' ')).append(data[index+ STYLE_RESOURCE_ID]).append(System.lineSeparator());
     97         result.append(Strings.padEnd("Changing configurations ", 25, ' ')).append(data[index+STYLE_CHANGING_CONFIGURATIONS]).append(System.lineSeparator());
     98         result.append(Strings.padEnd("Style density: ", 25, ' ')).append(data[index+STYLE_DENSITY]).append(System.lineSeparator());
     99         if (type == TypedValue.TYPE_STRING) {
    100           ShadowTypedArray shadowTypedArray = Shadow.extract(typedArray);
    101           result.append(Strings.padEnd("Style value: ", 25, ' ')).append(shadowTypedArray.loadStringValueAt(index)).append(System.lineSeparator());
    102         }
    103       }
    104       result.append(System.lineSeparator());
    105     }
    106     System.out.println(result.toString());
    107   }
    108 
    109   private static final ImmutableMap<Integer, String> TYPE_MAP = ImmutableMap.<Integer, String>builder()
    110           .put(TypedValue.TYPE_NULL, "TYPE_NULL")
    111           .put(TypedValue.TYPE_REFERENCE, "TYPE_REFERENCE")
    112           .put(TypedValue.TYPE_ATTRIBUTE, "TYPE_ATTRIBUTE")
    113           .put(TypedValue.TYPE_STRING, "TYPE_STRING")
    114           .put(TypedValue.TYPE_FLOAT, "TYPE_FLOAT")
    115           .put(TypedValue.TYPE_DIMENSION, "TYPE_DIMENSION")
    116           .put(TypedValue.TYPE_FRACTION, "TYPE_FRACTION")
    117           .put(TypedValue.TYPE_INT_DEC, "TYPE_INT_DEC")
    118           .put(TypedValue.TYPE_INT_HEX, "TYPE_INT_HEX")
    119           .put(TypedValue.TYPE_INT_BOOLEAN, "TYPE_INT_BOOLEAN")
    120           .put(TypedValue.TYPE_INT_COLOR_ARGB8, "TYPE_INT_COLOR_ARGB8")
    121           .put(TypedValue.TYPE_INT_COLOR_RGB8, "TYPE_INT_COLOR_RGB8")
    122           .put(TypedValue.TYPE_INT_COLOR_ARGB4, "TYPE_INT_COLOR_ARGB4")
    123           .put(TypedValue.TYPE_INT_COLOR_RGB4, "TYPE_INT_COLOR_RGB4")
    124           .build();
    125 
    126 }
    127