参看

小米全面屏键盘优化

这个功能还是不错的。我的左边放的是剪切板,右边方的是小爱语音输入。

但是它只针对了三款输入法有优化。

今天就去看看它里面是怎么搞的。

adb pull /system/framework/framework.jar c:/fw
把源代码拉出来,解压出来可以看到四个 .dex 文件
d2j-dex2jar.bat classes2.dex --force
把这个 .dex 文件反编译,转化成 classes2-dex2jar.jar 文件
使用 Luyten 查看这个 classes2-dex2jar.jar

可以找到这样一个文件 android.inputmethodservice.InputMethodServiceInjector

package android.inputmethodservice;

import java.util.*;
import android.os.*;
import android.view.inputmethod.*;
import android.content.*;
import android.util.*;
import android.content.pm.*;
import android.view.*;
import java.lang.reflect.*;

class InputMethodServiceInjector
{
    public static final String TAG = "InputMethodService";
    private static ClassLoader sClassLoader;
    private static HashMap<String, Integer> sImeMinVersionSupport;
    private static boolean sIsCanLoadPlugin;
    private static int sIsImeSupport;
    private static boolean sIsMiuiBottomSupport;
    private static int sVersionCode;
    
    static {
        InputMethodServiceInjector.sIsImeSupport = -1;
        InputMethodServiceInjector.sVersionCode = -1;
        InputMethodServiceInjector.sImeMinVersionSupport = new HashMap<String, Integer>();
        boolean sIsMiuiBottomSupport = false;
        if (SystemProperties.getInt("ro.miui.support_miui_ime_bottom", 0) == 1) {
            sIsMiuiBottomSupport = true;
        }
        InputMethodServiceInjector.sIsMiuiBottomSupport = sIsMiuiBottomSupport;
        InputMethodServiceInjector.sImeMinVersionSupport.put("com.iflytek.inputmethod.miui", 7912);
        InputMethodServiceInjector.sImeMinVersionSupport.put("com.sohu.inputmethod.sogou.xiaomi", 624);
        InputMethodServiceInjector.sImeMinVersionSupport.put("com.baidu.input_mi", 477);
        // 三个写死的输入法包名
    }
    
    public static void addMiuiBottomView(final LayoutInflater layoutInflater, final ViewGroup viewGroup, final ViewGroup viewGroup2, final View view, final ViewGroup viewGroup3, final InputMethodManager inputMethodManager, final InputMethodService inputMethodService) {
        final Context applicationContext = inputMethodService.getApplicationContext();
        if (InputMethodServiceInjector.sIsCanLoadPlugin = isCanLoadPlugin(applicationContext)) {
            final String apkPath = InputMethodModuleManager.getApkPath(applicationContext, "com.miui.phrase");
            try {
                final Context moduleContext = getModuleContext((Context)inputMethodService);
                if (moduleContext != null) {
                    InputMethodModuleManager.loadDex(moduleContext.getClassLoader(), apkPath);
                    InputMethodServiceInjector.sClassLoader = moduleContext.getClassLoader();
                    reflectModule("addMiuiBottomView", new Class[] { Context.class, LayoutInflater.class, ViewGroup.class, ViewGroup.class, View.class, ViewGroup.class, InputMethodManager.class, InputMethodService.class }, moduleContext, layoutInflater, viewGroup, viewGroup2, view, viewGroup3, inputMethodManager, inputMethodService);
                }
            }
            catch (Exception ex) {
                Log.e("InputMethodService", "load dex error!", (Throwable)ex);
            }
        }
    }
    
    public static void afterComputeInsets(final InputMethodService.Insets insets, final InputMethodService inputMethodService) {
        if (InputMethodServiceInjector.sIsCanLoadPlugin) {
            reflectModule("afterComputeInsets", new Class[] { InputMethodService.Insets.class }, insets);
        }
    }
    
    private static void customizeBottomViewColor(final boolean b, final int n, final int n2, final int n3) {
        final StringBuilder sb = new StringBuilder();
        sb.append("customizeBottomViewColor, isCustom : ");
        sb.append(b);
        Log.i("InputMethodService", sb.toString());
        if (InputMethodServiceInjector.sIsCanLoadPlugin) {
            reflectModule("customizeBottomViewColor", new Class[] { Boolean.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE }, b, n, n2, n3);
        }
    }
    
    private static int getImeVersionCode(final Context context) {
        final String packageName = context.getPackageName();
        try {
            if (InputMethodServiceInjector.sVersionCode == -1) {
                final PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0);
                if (packageInfo != null) {
                    InputMethodServiceInjector.sVersionCode = packageInfo.versionCode;
                }
            }
        }
        catch (PackageManager$NameNotFoundException ex) {
            final StringBuilder sb = new StringBuilder();
            sb.append("check ");
            sb.append(packageName);
            sb.append(" Version failed");
            Log.e("InputMethodService", sb.toString(), (Throwable)ex);
        }
        return InputMethodServiceInjector.sVersionCode;
    }
    
    private static Context getModuleContext(Context packageContext) {
        try {
            packageContext = packageContext.createPackageContext("com.miui.phrase", 0);
            return packageContext;
        }
        catch (PackageManager$NameNotFoundException ex) {
            Log.e("InputMethodService", "getModuleContext", (Throwable)ex);
            return null;
        }
    }
    
    private static boolean isCanLoadPlugin(final Context context) {
        return isImeSupport(context) && InputMethodServiceInjector.sIsMiuiBottomSupport;
    }
    
    private static boolean isImeSupport(final Context context) {
        final int sIsImeSupport = InputMethodServiceInjector.sIsImeSupport;
        boolean b = false;
        if (sIsImeSupport != -1) {
            if (sIsImeSupport == 1) {
                b = true;
            }
            return b;
        }
        final Integer n = InputMethodServiceInjector.sImeMinVersionSupport.get(context.getPackageName());
        if (n != null && getImeVersionCode(context) >= n) {// 判断包名和版本号
            InputMethodServiceInjector.sIsImeSupport = 1;
            return true;
        }
        InputMethodServiceInjector.sIsImeSupport = 0;
        return false;
    }
    
    static void onBadTokenException(final InputMethodService inputMethodService, final WindowManager$BadTokenException ex) {
        final StringBuilder sb = new StringBuilder();
        sb.append("Catch a BadTokeException: ");
        sb.append(ex.getMessage());
        Log.i("InputMethodService", sb.toString());
        inputMethodService.mWindow = new SoftInputWindow((Context)inputMethodService, "InputMethod", inputMethodService.mTheme, null, null, inputMethodService.mDispatcherState, 2011, 80, false);
        inputMethodService.initViews();
        inputMethodService.mWindow.getWindow().setLayout(-1, -2);
    }
    
    public static void onDestroy(final Context context) {
        if (InputMethodServiceInjector.sIsCanLoadPlugin) {
            reflectModule("onDestroy", new Class[0], new Object[0]);
        }
    }
    
    public static void onWindowHidden(final InputMethodService inputMethodService) {
        if (InputMethodServiceInjector.sIsCanLoadPlugin) {
            reflectModule("onWindowHidden", new Class[0], new Object[0]);
        }
    }
    
    public static void onWindowShown(final InputMethodService inputMethodService) {
        if (InputMethodServiceInjector.sIsCanLoadPlugin) {
            reflectModule("onWindowShown", new Class[0], new Object[0]);
        }
    }
    
    private static void reflectModule(final String s, final Class<?>[] array, final Object... array2) {
        try {
            final Method declaredMethod = Class.forName("com.miui.inputmethod.InputMethodBottomManager", true, InputMethodServiceInjector.sClassLoader).getDeclaredMethod(s, array);
            declaredMethod.setAccessible(true);
            declaredMethod.invoke(null, array2);
        }
        catch (Exception ex) {
            Log.e("InputMethodService", s, (Throwable)ex);
        }
    }
}

可以看出来,这里面写死了三个包名,而且开启优化也会去判断版本号。

但是,自己经常使用的是谷歌拼音输入法,但是还想用这个优化,怎么破。

第一步,下载谷歌拼音输入法的apk包

第二步,使用 apktool,反编译apk包

第三步,修改 AndroidManifest.xml 中,第一行 package="" 引号中改为三个写死包名中的一个

第四步,使用 apktool,重新打包出 apk

第五步,对 apk 进行签名,然后就可以安装了

使用 mt 管理器更方便