Android Protect-0.Xposed开发教程(官方Replacing resources)

Xposed可以轻松替换资源,例如图像或字符串。 方法如下:

简单的资源

@Override
public void initZygote(IXposedHookZygoteInit.StartupParam startupParam) throws Throwable {
	XResources.setSystemWideReplacement("android", "bool", "config_unplugTurnsOnScreen", false);
}

@Override
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
	// replacements only for SystemUI
	if (!resparam.packageName.equals("com.android.systemui"))
		return;
	
	// different ways to specify the resources to be replaced
	resparam.res.setReplacement(0x7f080083, "YEAH!"); // WLAN toggle text. You should not do this because the id is not fixed. Only for framework resources, you could use android.R.string.something
	resparam.res.setReplacement("com.android.systemui:string/quickpanel_bluetooth_text", "WOO!");
	resparam.res.setReplacement("com.android.systemui", "string", "quickpanel_gps_text", "HOO!");
	resparam.res.setReplacement("com.android.systemui", "integer", "config_maxLevelOfSignalStrengthIndicator", 6);
}

这就是“简单”的替换,通过这种方式你可以直接替换值。这种方式可以用于:Boolean, Color, Integer, int[], StringString[]

如您所见,有几种不同的方法可以设置替换资源。对于构成安卓框架一部分的所有地方都应该被替换的资源(对所有的app都可用),你可以在initZygote中调用XResources.setSystemWideReplacement(...)方法。对于特定的app资源,在验证你在正确的app之后,在hookInitPackageResources中调用res.setReplacement。这时你不应使用setSystemWideReplacement因为可能会产生你无法预料的副作用。

替换Drawable也采用相似的办法。然而你不能只使用Drawable作为替换物,因为这可能导致同一个Drawable实例被不同的ImageViews引用。因此,你需要使用包装器:

resparam.res.setReplacement("com.android.systemui", "drawable", "status_bar_background", new XResources.DrawableLoader() {
	@Override
	public Drawable newDrawable(XResources res, int id) throws Throwable {
		return new ColorDrawable(Color.WHITE);
	}
});

复杂资源

更加复杂的资源(比如动画类型的Drawable)必须从你的模块资源当中被引用。我们假设你想要替换电池图标。下面是代码:

package de.robv.android.xposed.mods.coloredcirclebattery;

import android.content.res.XModuleResources;
import de.robv.android.xposed.IXposedHookInitPackageResources;
import de.robv.android.xposed.IXposedHookZygoteInit;
import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam;

public class ColoredCircleBattery implements IXposedHookZygoteInit, IXposedHookInitPackageResources {
	private static String MODULE_PATH = null;
	
	@Override
	public void initZygote(StartupParam startupParam) throws Throwable {
		MODULE_PATH = startupParam.modulePath;
	}

	@Override
	public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
		if (!resparam.packageName.equals("com.android.systemui"))
			return;

		XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res);
		resparam.res.setReplacement("com.android.systemui", "drawable", "stat_sys_battery", modRes.fwd(R.drawable.battery_icon));
		resparam.res.setReplacement("com.android.systemui", "drawable", "stat_sys_battery_charge", modRes.fwd(R.drawable.battery_icon_charge));
	}
}

你可以随意命名你的替换资源。我选择 batterry_icon 替代 stat_sys_battery ,以便它们在本文中更好区分。

之后把 “battery_icon” 和 “battery_icon_charge” 这两个Drawable添加到你的模块当中。最简单的情况是添加 “res/drawables/battery_icon.png” 和 “res/drawables/battery_icon_charge.png”。但是你可以使用Android提供的所有方式来定义资源。所以对于动画图标,你可使用带有animation-list和其它Drawable资源引用的XML文件。当然这个XML文件也必须放在你的模块内。

通过这些替换,你能要求Xposed将所有指向特定资源的请求跳到你自己的模块。这同样意味着你可以利用qualifier,比如:如果你对landscape或更低的屏幕密度需要不同的资源。翻译也可以以相同的方式提供。同样,你也许需要这么做如果原来的资源使用qualifier。你不能仅仅替换一段文字的西班牙语版本。正如前面提到的,请求是向前的,所以它会完全被你的模块资源处理,并且不会察觉其它翻译的存在。

这个技巧基本上适用于所有资源类型。除了像主题这样的特定事物。

修改布局

尽管理论上你可以用之前提到的技巧彻底替换布局,但这有很多坏处。你必须从原有布局中复制整个布局,这会降低与其它ROM的兼容性。主题可能会丢失。只有一个模块可以代替布局。如果两个模块都尝试这么做,后者将会胜出。最重要的是,其它资源的ID和引用是很难定义的。因此,我不推荐这样做。

作为一种好的选择,你可以使用post-inflate hooks。下面是你怎么做:

@Override
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
	if (!resparam.packageName.equals("com.android.systemui"))
		return;

	resparam.res.hookLayout("com.android.systemui", "layout", "status_bar", new XC_LayoutInflated() {
		@Override
		public void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable {
			TextView clock = (TextView) liparam.view.findViewById(
					liparam.res.getIdentifier("clock", "id", "com.android.systemui"));
			clock.setTextColor(Color.RED);
		}
	}); 
}

每当“status_bar”布局被填充时,回调方法handleLayoutInflated就被调用。在你以参数形式得到的LayoutInflatedParam对象中,你可以找到刚被创建的View并在需要时修改它。您还可以获取resNames以确定调用该方法的布局(如果您对多个布局使用相同的方法)和variant,例如,如果它是已加载布局的版本,则可能包含layout-land。res可帮助您从布局中获取与源相同的ID或附加资源。

参考:
https://github.com/rovo89/XposedBridge/wiki/Replacing-resources

猜你喜欢

转载自blog.csdn.net/hgy413/article/details/86084051