Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money

Replace Boolean, Color, Integer, int[], String and String[] simple resources

1. Replace Android Framwork resources

Replace system framework Resources (for all app work) need to implement interface IXposedHookZygoteInit initZygote method, and the method invokes the Resources. The setSystemWideReplacement (…). Method to replace resources

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

import android.content.res.XResources;
import de.robv.android.xposed.IXposedHookZygoteInit;

public class Tutorial2  implements IXposedHookZygoteInit{

    @Override
    public void initZygote(StartupParam arg0) throws Throwable {
        XResources.setSystemWideReplacement("android"."bool"."config_unplugTurnsOnScreen".false); }}Copy the code

2. Replace app resources

Replace an app resources needed to implement IXposedHookInitPackageResources class andleInitPackageResources method, and the method invokes the res. SetReplacement (…). Way to replace resources, pay attention to in the method. Do not use XResources setSystemWideReplacement method

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

import android.content.res.XResources;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import de.robv.android.xposed.IXposedHookInitPackageResources;
import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam;

public class Tutorial3 implements  IXposedHookInitPackageResources  {

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

        // Different ways to replace resources
        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);
        resparam.res.setReplacement("com.android.systemui"."drawable"."status_bar_background".new XResources.DrawableLoader() {
            @Override
            public Drawable newDrawable(XResources res, int id) throws Throwable {
                return newColorDrawable(Color.WHITE); }});// You can't use the Drawble class directly, because the Drawble class can affect other imageViews that reference Ddrawble instances. It's better to use a wrapper.}}Copy the code

Replace complex resources

We can also replace duplicated resources such as animation resources, so let’s replace the Battery Icon

Animation Resource Layout


      
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
     android:oneshot="true" >
    <item android:drawable="@drawable/icon1" android:duration="150"></item>
    <item android:drawable="@drawable/icon2" android:duration="150"></item>
    <item android:drawable="@drawable/icon3" android:duration="150"></item>
    <item android:drawable="@drawable/icon4" android:duration="150"></item>
    <item android:drawable="@drawable/icon5" android:duration="150"></item>
    <item android:drawable="@drawable/icon6" android:duration="150"></item>

</animation-list>
123456789101112
Copy the code

Code:

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

import com.example.xposedmoduletest.R;

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 Tutorial4 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.animation));
        resparam.res.setReplacement("com.android.systemui"."drawable"."stat_sys_battery_charge", modRes.fwd(R.drawable.animation)); }}Copy the code

Xposed framework will module request resources request to your module resources

Replace the layout

You can replace the layout file by replacing the resource, but you will have to copy the entire layout file from the target APK to make changes, which will reduce Rom compatibility of the module. And if two or more modules modify the layout, the last module to modify the layout will take effect. More importantly, ids that point to other resources in a layout are difficult to determine. It is recommended to modify the layout using the following methods:

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

import android.graphics.Color;
import android.widget.TextView;
import de.robv.android.xposed.IXposedHookInitPackageResources;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam;
import de.robv.android.xposed.callbacks.XC_LayoutInflated;
import de.robv.android.xposed.callbacks.XC_LayoutInflated.LayoutInflatedParam;

public class Tutorial5 implements   IXposedHookInitPackageResources{

    @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);
                    XposedBridge.log("layout resNames.fullname:"+liparam.resNames.fullName);
                    XposedBridge.log("layout resNames.id:"+liparam.resNames.id);
                    XposedBridge.log("layout resNames.name:"+liparam.resNames.name);
                    XposedBridge.log("layout resNames.pkg:"+liparam.resNames.pkg);
                    XposedBridge.log("layout resNames.type:"+liparam.resNames.type);

                    XposedBridge.log("layout resNames.variant:"+liparam.variant);
                    XposedBridge.log("layout resNames.view:"+liparam.view); }}); }}Copy the code

The callback method HandlelayoutVehicle calls back after the Layout file is filled in. In the Method’s LayoutInflatedParam object parameter, you can find the View component you want to modify. You can also determine which layout file to load by calling resNames. Use variant to determine the directory ‘layout-land’ for loaded layouts. Res will also help you get resource ids and other resources.

5. Hook the method with reflection

Whenever the application is loaded, the handLoadPackage method of the IXposedHookLoadPackPage interface is called to execute. In order to execute in the correct process, we need to determine whether the loaded package is the correct package

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

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

public class Tutorial6 implements IXposedHookLoadPackage {

    @Override
    public void handleLoadPackage(LoadPackageParam param) throws Throwable {
        if(! param.packageName.equals("com.android.systemui"))
            return; }}Copy the code

Once we are in the correct process, we can use the ClassLoad in the param change to access the classes loaded in that process

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

import android.webkit.WebView.FindListener;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodHook.MethodHookParam;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
public class Tutorial6 implements IXposedHookLoadPackage {

    @Override
    public void handleLoadPackage(LoadPackageParam param) throws Throwable {
        if(! param.packageName.equals("com.android.systemui"))
            return;
         findAndHookMethod("com.android.systemui.statusbar.policy.Clock",param.classLoader, "updateClock".new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    // this will be called before the clock was updated by the original method
                }
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    // this will be called after the clock was updated by the original method}}); }}Copy the code

XposedHelpers is an important tool, recommend using Eclipse’s classmate static imports the class methods in import static DE. Robv. Android. Xposed. XposedHelpers. FindAndHookMethod; . This class can access methods, constructors, and fields via reflection. findAndHookMehthod(String packageName,Class clazz, String methodName, Object… Args)) method to Hook the function. If you Hook before and after a method, the last argument to the method needs to implement the beforeHookedMethod and afterHookedMethod methods of the XC_MethodHook class. If you want to replace the whole method, The replaceHookedMethod method of the XC_MethodReplacement class, XposedBridge, holds the callback method of each Hook method. Callback methods with higher priority are called a.before -> b.before -> original method -> b.after -> A.after

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

import android.graphics.Color;
import android.webkit.WebView.FindListener;
import android.widget.TextView;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodHook.MethodHookParam;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
public class Tutorial6 implements IXposedHookLoadPackage {

    @Override
    public void handleLoadPackage(LoadPackageParam param) throws Throwable {
        if(! param.packageName.equals("com.android.systemui"))
            return;
         findAndHookMethod("com.android.systemui.statusbar.policy.Clock",param.classLoader, "updateClock".new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    // this will be called before the clock was updated by the original method
                }
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    TextView tv = (TextView) param.thisObject;// Get the object on which the method class is called
                    String text = tv.getText().toString();
                    tv.setText(text + ":)"); tv.setTextColor(Color.RED); }}); }}Copy the code