You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CustoMIUIzer/app/src/main/java/name/mikanoshi/customiuizer/utils/ResourceHooks.java

175 lines
6.5 KiB
Java

package name.mikanoshi.customiuizer.utils;
import android.content.Context;
import android.content.res.Resources;
import android.util.Pair;
import android.util.SparseIntArray;
import java.util.concurrent.ConcurrentHashMap;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import name.mikanoshi.customiuizer.utils.Helpers.MethodHook;
public class ResourceHooks {
private boolean hooksApplied = false;
public enum ReplacementType {
ID,
DENSITY,
OBJECT
}
private final SparseIntArray fakes = new SparseIntArray();
private final ConcurrentHashMap<String, Pair<ReplacementType, Object>> replacements = new ConcurrentHashMap<String, Pair<ReplacementType, Object>>();
private static int getFakeResId(String resourceName) {
return 0x7e000000 | (resourceName.hashCode() & 0x00ffffff);
}
@SuppressWarnings("FieldCanBeLocal")
private final MethodHook mReplaceHook = new MethodHook() {
@Override
protected void before(MethodHookParam param) {
Context mContext = Helpers.findContext();
if (mContext == null) return;
String method = param.method.getName();
Object value = getFakeResource(mContext, method, param.args);
if (value == null) {
value = getResourceReplacement(mContext, (Resources)param.thisObject, method, param.args);
if (value == null) return;
if ("getDimensionPixelOffset".equals(method) || "getDimensionPixelSize".equals(method))
if (value instanceof Float) value = ((Float)value).intValue();
}
param.setResult(value);
}
};
public ResourceHooks() {}
private void applyHooks() {
if (hooksApplied) return;
hooksApplied = true;
Helpers.findAndHookMethod(Resources.class, "getInteger", int.class, mReplaceHook);
Helpers.findAndHookMethod(Resources.class, "getFraction", int.class, int.class, int.class, mReplaceHook);
Helpers.findAndHookMethod(Resources.class, "getBoolean", int.class, mReplaceHook);
Helpers.findAndHookMethod(Resources.class, "getDimension", int.class, mReplaceHook);
Helpers.findAndHookMethod(Resources.class, "getDimensionPixelOffset", int.class, mReplaceHook);
Helpers.findAndHookMethod(Resources.class, "getDimensionPixelSize", int.class, mReplaceHook);
Helpers.findAndHookMethod(Resources.class, "getText", int.class, mReplaceHook);
if (Helpers.isNougat())
Helpers.findAndHookMethod(Resources.class, "getDrawable", int.class, Resources.Theme.class, mReplaceHook);
Helpers.findAndHookMethod(Resources.class, "getDrawableForDensity", int.class, int.class, Resources.Theme.class, mReplaceHook);
Helpers.findAndHookMethod(Resources.class, "getIntArray", int.class, mReplaceHook);
Helpers.findAndHookMethod(Resources.class, "getStringArray", int.class, mReplaceHook);
Helpers.findAndHookMethod(Resources.class, "getTextArray", int.class, mReplaceHook);
Helpers.findAndHookMethod(Resources.class, "getAnimation", int.class, mReplaceHook);
}
public int addResource(String resName, int resId) {
try {
applyHooks();
int fakeResId = getFakeResId(resName);
fakes.put(fakeResId, resId);
return fakeResId;
} catch (Throwable t) {
XposedBridge.log(t);
return 0;
}
}
private Object getFakeResource(Context context, String method, Object[] args) {
try {
if (context == null) return null;
int modResId = fakes.get((int)args[0]);
if (modResId == 0) return null;
Object value;
Resources modRes = Helpers.getModuleRes(context);
if ("getDrawable".equals(method))
value = XposedHelpers.callMethod(modRes, method, modResId, args[1]);
else if ("getDrawableForDensity".equals(method) || "getFraction".equals(method))
value = XposedHelpers.callMethod(modRes, method, modResId, args[1], args[2]);
else
value = XposedHelpers.callMethod(modRes, method, modResId);
return value;
} catch (Throwable t) {
XposedBridge.log(t);
return null;
}
}
public void setResReplacement(String pkg, String type, String name, int replacementResId) {
try {
applyHooks();
replacements.put(pkg + ":" + type + "/" + name, new Pair<>(ReplacementType.ID, replacementResId));
} catch (Throwable t) {
XposedBridge.log(t);
}
}
public void setDensityReplacement(String pkg, String type, String name, Integer replacementResValue) {
try {
applyHooks();
replacements.put(pkg + ":" + type + "/" + name, new Pair<>(ReplacementType.DENSITY, replacementResValue));
} catch (Throwable t) {
XposedBridge.log(t);
}
}
public void setObjectReplacement(String pkg, String type, String name, Object replacementResValue) {
try {
applyHooks();
replacements.put(pkg + ":" + type + "/" + name, new Pair<>(ReplacementType.OBJECT, replacementResValue));
} catch (Throwable t) {
XposedBridge.log(t);
}
}
private Object getResourceReplacement(Context context, Resources res, String method, Object[] args) {
if (context == null) return null;
String pkgName = null;
String resType = null;
String resName = null;
try {
pkgName = res.getResourcePackageName((int)args[0]);
resType = res.getResourceTypeName((int)args[0]);
resName = res.getResourceEntryName((int)args[0]);
} catch (Throwable ignore) {}
if (pkgName == null || resType == null || resName == null) return null;
try {
Object value;
String resFullName = pkgName + ":" + resType + "/" + resName;
String resAnyPkgName = "*:" + resType + "/" + resName;
Integer modResId = null;
Pair<ReplacementType, Object> replacement = null;
if (replacements.containsKey(resFullName))
replacement = replacements.get(resFullName);
else if (replacements.containsKey(resAnyPkgName))
replacement = replacements.get(resAnyPkgName);
if (replacement != null)
if (replacement.first == ReplacementType.OBJECT) return replacement.second;
else if (replacement.first == ReplacementType.DENSITY) return (Integer)replacement.second * res.getDisplayMetrics().density;
else if (replacement.first == ReplacementType.ID) modResId = (Integer)replacement.second;
if (modResId == null) return null;
Resources modRes = Helpers.getModuleRes(context);
if ("getDrawable".equals(method))
value = XposedHelpers.callMethod(modRes, method, modResId, args[1]);
else if ("getDrawableForDensity".equals(method) || "getFraction".equals(method))
value = XposedHelpers.callMethod(modRes, method, modResId, args[1], args[2]);
else
value = XposedHelpers.callMethod(modRes, method, modResId);
return value;
} catch (Throwable t) {
XposedBridge.log(t);
return null;
}
}
}