分类 Android 下的文章

XposedHelpers.findAndHookMethod(ClassLoader.class,
        "loadClass", String.class,
        new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                if ("com.example.MainActivity".equals(param.args[0])) {
                    param.setResult(getClass().getClassLoader()
                            .loadClass("com.example.TargetActivity"));
                }
            }
        });

阅读全文

摸索了一天,Method.invoke() 会无限套娃,不知道还有没有其他方法。

public class MainHook implements IXposedHookLoadPackage {

    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {

        // 要 Hook 的子类
        Class<?> clazzActivity = XposedHelpers.findClassIfExists(
            "com.example.MainActivity",
            lpparam.classLoader
        );

        // 要 Hook 的父类
        Class<?> clazzSuperActivity = clazzActivity.getSuperclass();

        // Hook 父类方法
        XposedHelpers.findAndHookMethod(clazzSuperActivity,
                "onCreate", Bundle.class,
                new XC_MethodHook() {});

        // Hook 子类方法
        XposedHelpers.findAndHookMethod(clazzActivity,
                "onCreate", Bundle.class,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        param.setResult(null);
                        Method method = clazzSuperActivity.getDeclaredMethod("onCreate", Bundle.class);
                        XposedBridge.invokeOriginalMethod(method, param.thisObject, param.args);
                    }
                });
    }
}

阅读全文

为 ssh 设置 shell

最近用 termux + tsu + sshd + zsh 远程,发现连接后使用的是 bash 而不是我默认的 zsh。

修改 /ect/profile~/.profile

# Force use zsh
zsh="/data/data/com.termux/files/usr/bin/zsh"
if [ "$SHELL" != "$zsh" -a -n "$SSH_TTY" -a -x "$zsh" ]; then
        export SHELL="$zsh"
        exec $zsh -l
fi

# ......

参考:bash - specify shell for ssh session

ssh 密码连接 Termux

网上都说只能通过秘钥连接,实则不然。用 passwd 命令设置一下密码就行了,用户名都不用。

# 设置 Termux 密码
$ passwd
New password:
Retype new password:
New password was successfully set.

# ssh 密码连接 Termux
$ ssh -p 8022 example.com
The authenticity of host '[example.com]:8022' can't be established.
ECDSA key fingerprint is SHA256:ABCDEFGHIJKLMNOPQRSTUVWXYZ+0123456789abcdef.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
[email protected]'s password: 

Welcome to Termux!

阅读全文

Android中默认情况下,几个Button并排在一个布局里会发出警告:

Buttons in button bars should be borderless

Buttons in button bars should be borderless; use style="?android:attr/buttonBarButtonStyle" (and ?android:attr/buttonBarStyle on the parent)

Button bars typically use a borderless style for the buttons. Set the style="?android:attr/buttonBarButtonStyle" attribute on each of the buttons, and set style="?android:attr/buttonBarStyle" on the parent layout
http://developer.android.com/design/building-blocks/buttons.html

网上搜得的答案:

原因:两个Buttons放在一个布局里会被判断为按钮栏,需要添加样式取消它的边框。

解决方法: 在Buttons上添加属性style="?android:attr/buttonBarButtonStyle" 。系统提示也可以在按钮的父布局上添加 style="?android:attr/buttonBarStyle" 属性

可是这样的话你的Button就没有边框了
然而你不是想让Button没有边框,只要加上属性style="@style/Widget.AppCompat.Button"或者其他样式就可以了

所以我对这种情况的理解是:
几个Button并排在一个布局里,没有设置style属性的Button会被判断为按钮栏的按钮
然而按钮栏的按钮需要取消它的边框(即添加属性style="?android:attr/buttonBarButtonStyle"

so,几个Button并排在一个布局里设置style属性就不会有问题了

阅读全文

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;
import android.util.DisplayMetrics;

import java.io.File;
import java.lang.reflect.Method;
import java.util.List;

/**
 * apk、app 签名类
 */
public class AppSignature {

    /**
     * 获取未安装Apk的签名
     *
     * @param apkPath
     * @return
     */
    public static String byApkPath(String apkPath) {
        String PATH_PackageParser = "android.content.pm.PackageParser";
        try {
            // apk包的文件路径
            // 这是一个Package 解释器, 是隐藏的
            // 构造函数的参数只有一个, apk文件的路径
            Class<?> pkgParserCls = Class.forName(PATH_PackageParser);
            Object pkgParser;
            if (Build.VERSION.SDK_INT > 19)
                pkgParser = pkgParserCls.newInstance();
            else
                pkgParser = pkgParserCls.getConstructor(String.class).newInstance(apkPath);

            // 这个是与显示有关的, 里面涉及到一些像素显示等等, 我们使用默认的情况
            DisplayMetrics metrics = new DisplayMetrics();
            metrics.setToDefaults();

            Object pkgParserPkg = null;
            if (Build.VERSION.SDK_INT > 19) {
                Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod("parsePackage", File.class, int.class);
                pkgParser_parsePackageMtd.setAccessible(true);
                pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser, new File(apkPath), PackageManager.GET_SIGNATURES);
            } else {
                Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod("parsePackage", File.class, String.class, DisplayMetrics.class, int.class);
                pkgParser_parsePackageMtd.setAccessible(true);
                pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser, new File(apkPath), apkPath, metrics, PackageManager.GET_SIGNATURES);
            }

            pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), int.class).invoke(pkgParser, pkgParserPkg, PackageManager.GET_SIGNATURES);

            // 应用程序信息包, 这个公开的, 不过有些函数, 变量没公开
            Signature[] info = (Signature[]) pkgParserPkg.getClass().getDeclaredField("mSignatures").get(pkgParserPkg);
            return info[0].toCharsString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取已安装apk签名
     *
     * @param context
     * @param packName
     * @return
     */
    public static String byPackageName(Context context, String packName) {
        PackageManager pm = context.getPackageManager();
        List<PackageInfo> apps = pm.getInstalledPackages(PackageManager.GET_SIGNATURES);

        try {
            return pm.getPackageInfo(packName, PackageManager.GET_SIGNATURES).signatures[0].toCharsString();
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 检测签名是否相同
     *
     * @param context
     * @param packName
     * @param apkPath
     * @return
     */
    public static boolean test(Context context, String packName, String apkPath) {
        return byApkPath(apkPath).equals(byPackageName(context, packName));
    }
}

阅读全文