package com.mixpanel.android.mpmetrics; import android.content.Context; import android.util.SparseArray; import com.mixpanel.android.util.MPLog; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; /** * This class is for internal use in the Mixpanel library, and should not be imported into * client code. */ public abstract class ResourceReader implements ResourceIds { public static class Ids extends ResourceReader { public Ids(String resourcePackageName, Context context) { super(context); mResourcePackageName = resourcePackageName; initialize(); } @Override protected Class<?> getSystemClass() { return android.R.id.class; } @Override protected String getLocalClassName(Context context) { return mResourcePackageName + ".R$id"; } private final String mResourcePackageName; } public static class Drawables extends ResourceReader { protected Drawables(String resourcePackageName, Context context) { super(context); mResourcePackageName = resourcePackageName; initialize(); } @Override protected Class<?> getSystemClass() { return android.R.drawable.class; } @Override protected String getLocalClassName(Context context) { return mResourcePackageName + ".R$drawable"; } private final String mResourcePackageName; } protected ResourceReader(Context context) { mContext = context; mIdNameToId = new HashMap<String, Integer>(); mIdToIdName = new SparseArray<String>(); } @Override public boolean knownIdName(String name) { return mIdNameToId.containsKey(name); } @Override public int idFromName(String name) { return mIdNameToId.get(name); } @Override public String nameForId(int id) { return mIdToIdName.get(id); } private static void readClassIds(Class<?> platformIdClass, String namespace, Map<String, Integer> namesToIds) { try { final Field[] fields = platformIdClass.getFields(); for (int i = 0; i < fields.length; i++) { final Field field = fields[i]; final int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers)) { final Class fieldType = field.getType(); if (fieldType == int.class) { final String name = field.getName(); final int value = field.getInt(null); final String namespacedName; if (null == namespace) { namespacedName = name; } else { namespacedName = namespace + ":" + name; } namesToIds.put(namespacedName, value); } } } } catch (IllegalAccessException e) { MPLog.e(LOGTAG, "Can't read built-in id names from " + platformIdClass.getName(), e); } } protected abstract Class<?> getSystemClass(); protected abstract String getLocalClassName(Context context); protected void initialize() { mIdNameToId.clear(); mIdToIdName.clear(); final Class<?> sysIdClass = getSystemClass(); readClassIds(sysIdClass, "android", mIdNameToId); final String localClassName = getLocalClassName(mContext); try { final Class<?> rIdClass = Class.forName(localClassName); readClassIds(rIdClass, null, mIdNameToId); } catch (ClassNotFoundException e) { MPLog.w(LOGTAG, "Can't load names for Android view ids from '" + localClassName + "', ids by name will not be available in the events editor."); MPLog.i(LOGTAG, "You may be missing a Resources class for your package due to your proguard configuration, " + "or you may be using an applicationId in your build that isn't the same as the package declared in your AndroidManifest.xml file.\n" + "If you're using proguard, you can fix this issue by adding the following to your proguard configuration:\n\n" + "-keep class **.R$* {\n" + " <fields>;\n" + "}\n\n" + "If you're not using proguard, or if your proguard configuration already contains the directive above, " + "you can add the following to your AndroidManifest.xml file to explicitly point the Mixpanel library to " + "the appropriate library for your resources class:\n\n" + "<meta-data android:name=\"com.mixpanel.android.MPConfig.ResourcePackageName\" android:value=\"YOUR_PACKAGE_NAME\" />\n\n" + "where YOUR_PACKAGE_NAME is the same string you use for the \"package\" attribute in your <manifest> tag." ); } for (Map.Entry<String, Integer> idMapping : mIdNameToId.entrySet()) { mIdToIdName.put(idMapping.getValue(), idMapping.getKey()); } } private final Context mContext; private final Map<String, Integer> mIdNameToId; private final SparseArray<String> mIdToIdName; @SuppressWarnings("unused") private static final String LOGTAG = "MixpanelAPI.RsrcReader"; }