预置Sougou输入法并设为默认

需求:

  1. 预置SouGou输入法
  2. 设置SouGou输入法为默认输入法,并移除多余的输入法

环境

平台: android7.0 TV amlogic

修改过程

  1. 预置apk

    1. 在vendor/目录下添加自定义的一个文件夹SouGou,文件中放入Android.mk以及apk
    2. 在对应项目的device.mk中加入apk
    PRODUCT_PACKAGES += \
        SougouInput
    

    此处的SougouInput来自Android.mk中的

    LOCAL_MODULE := SougouInput
    

    以上完成了预置

  2. 设为默认输入法

    1. 当前系统中包含了两种输入法(LatinIME及遥控输入法),在预置成功并设为默认之前这两种输入法建议先保留。参考他人修改方案,开始设置默认。先将apk的component名保存在一字符中
    
    <string name="config_default_input_method" translatable="false">com.sohu.inputmethod.sogouoem/.SogouIMEstring>
    <string name="def_enabled_input_methods" translatable="false">com.sohu.inputmethod.sogouoem/.SogouIMEstring>
    
    

    注意此处包名,由于之前是参考他人修改,通常所使用的包名均为(com.sohu.inputmethod.sogou),故之前没注意包名修改后出现预置成功后的输入法没有成功调起,个人建议对于apk最好还是自己反编译或者通过studio查看其清单文件中的包名及类名进行获取
    2. 添加后的字段需要在数据库中存入
    /frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java

    //add begin
    loadStringSetting(stmt, Settings.Secure.DEFAULT_INPUT_METHOD,
            R.string.config_default_input_method);
    loadStringSetting(stmt, Settings.Secure.ENABLED_INPUT_METHODS,
            R.string.def_enabled_input_methods);
    //add end
    

    注意: DEFAULT_INPUT_METHOD及ENABLED_INPUT_METHODS是原先已经经过定义的字段,在/frameworks/base/core/java/android/provider/Settings.java中已做定义

    /**
     * Setting to record the input method used by default, holding the ID
     * of the desired method.
     */
    public static final String DEFAULT_INPUT_METHOD = "default_input_method";
    
    Settings.Secure.ENABLED_INPUT_METHODS 使用/启用输入法, 这会显示在设置中的输入法列表
    Settings.Secure.DEFAULT_INPUT_METHOD 默认输入法
    

    因此在数据库中做初始化处理,设置默认值为所需要的值,系统中会从数据库中获取默认值

    以上修改若在Android6.0及以前就完成了需求。在Android7.0及之后还需要做接下来的修改才会起作用

  3. 在 /frameworks/base/services/core/java/com/android/server/InputMethodManagerService.java的方法buildInputMethodListLocked(boolean resetDefaultEnabledIme)中做以下修改

    // TODO: The following code should find better place to live.
    /* add begin
    if (!resetDefaultEnabledIme) {
        boolean enabledImeFound = false;
        final List enabledImes = mSettings.getEnabledInputMethodListLocked();
        final int N = enabledImes.size();
        for (int i = 0; i < N; ++i) {
            final InputMethodInfo imi = enabledImes.get(i);
            if (mMethodList.contains(imi)) {
                enabledImeFound = true;
                break;
            }
        }
        if (!enabledImeFound) {
            Slog.i(TAG, "All the enabled IMEs are gone. Reset default enabled IMEs.");
            resetDefaultEnabledIme = true;
            resetSelectedInputMethodAndSubtypeLocked("");
        }
    }
    */ add end
    

    将以上代码进行注释是为了不让其进行重置输入法,继而替换
    另外,在 /frameworks/base/core/java/com/android/internal/inputmethod/InputMethodUtils.java的方法enableAllIMEsIfThereIsNoEnabledIME()中做以下修改

    // At the initial boot, the settings for input methods are not set,
        // so we need to enable IME in that case.
        public void enableAllIMEsIfThereIsNoEnabledIME() {
            //modify begin
            if (!TextUtils.isEmpty(getEnabledInputMethodsStr())) {
            //if (TextUtils.isEmpty(getEnabledInputMethodsStr())) {
            //modify end
                StringBuilder sb = new StringBuilder();
                final int N = mMethodList.size();
                for (int i = 0; i < N; i++) {
                    InputMethodInfo imi = mMethodList.get(i);
                    Slog.i(TAG, "Adding: " + imi.getId());
                    if (i > 0) sb.append(':');
                    sb.append(imi.getId());
                }
                putEnabledInputMethodsStr(sb.toString());
            }
        }
    
    

    此方法在InputMethodManagerService中的构造方法中进行了调用,用于在初始引导时,未设置输入方法的设置,需要在这种情况下启用IME。如不做修改,不能在系统启动后自己调起

  4. 以上修改已经完成了默认的设置,但在设置的键盘下存在多余的输入法,需要将其移除,进入到项目的mk下进行注释
    在此处是code/build/target/product/core.mk

    -    LatinIME \
    

    code/device/amlogic/common/core_amlogic.mk

    -    RemoteIME \
    -    LatinIME \
    

现在已经完成了需求,多次测试(切换语言/下载其他输入法等)没有问题

可以通过adb进入系统的数据库进行查看

/data/system/users/0 # cat settings_secure.xml

会发现

<setting id="32" name="enabled_input_methods" value="com.sohu.inputmethod.sogouoem/.SogouIME" package="android" />
<setting id="31" name="default_input_method" value="com.sohu.inputmethod.sogouoem/.SogouIME" package="android" />

附一

由于以上修改均是在eng版本进行验证的,之后会发现在user/userdebug版本出现启动后搜狗输入法停止运行的错误,一开始想到的是权限问题,当然抓取串口log发现其中确实有一串权限的log

01-01 08:00:18.180  4475  4475 W PackageParser: No actions in intent filter at /system/app/SougouInput/SougouInput.apk Binary XML file line #420
01-01 08:00:18.353  4475  4475 I PackageManager: ===>Package com.sohu.inputmethod.sogouoem granting android.permission.SYSTEM_ALERT_WINDOW
01-01 08:00:18.353  4475  4475 I PackageManager: ===>Package com.sohu.inputmethod.sogouoem granting android.permission.WRITE_SETTINGS
01-01 08:00:18.353  4475  4475 I PackageManager: ===>Package com.sohu.inputmethod.sogouoem granting android.permission.ACCESS_MOCK_LOCATION
01-01 08:00:18.354  4475  4475 I PackageManager: ===>Package com.sohu.inputmethod.sogouoem granting android.permission.READ_LOGS
01-01 08:00:18.354  4475  4475 I PackageManager: ===>Package com.sohu.inputmethod.sogouoem granting android.permission.CLEAR_APP_CACHE

然后一直在试图向搜狗输入法给与默认权限的相关修改,然而并没有用,在PackageManagerService中修改

final int permsSize = pkg.requestedPermissions.size();
for (int i=0; i

之后发现log中的关于权限的log仅仅是一些info,不是error,后来发现一下log

01-01 08:00:30.230  5533  5533 E AndroidRuntime: FATAL EXCEPTION: main
01-01 08:00:30.230  5533  5533 E AndroidRuntime: Process: com.sohu.inputmethod.sogouoem, PID: 5533
01-01 08:00:30.230  5533  5533 E AndroidRuntime: java.lang.RuntimeException: Unable to instantiate application com.sohu.inputmethod.sogouoem.SogouAppApplication: java.lang.ClassNotFoundException: Didn't find class "com.sohu.inputmethod.sogouoem.SogouAppApplication" on path: DexPathList[[zip file "/system/app/SougouInput/SougouInput.apk"],nativeLibraryDirectories=[/system/app/SougouInput/lib/arm, /system/fake-libs, /system/app/SougouInput/SougouInput.apk!/lib/armeabi, /system/lib, /vendor/lib, /system/lib, /vendor/lib]]
...
01-01 08:00:30.230  5533  5533 E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: Didn't find class "com.sohu.inputmethod.sogouoem.SogouAppApplication" on path: DexPathList[[zip file "/system/app/SougouInput/SougouInput.apk"],nativeLibraryDirectories=[/system/app/SougouInput/lib/arm, /system/fake-libs, /system/app/SougouInput/SougouInput.apk!/lib/armeabi, /system/lib, /vendor/lib, /system/lib, /vendor/lib]]
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 	at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 	at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 	at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 	at android.app.Instrumentation.newApplication(Instrumentation.java:992)
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 	at android.app.LoadedApk.makeApplication(LoadedApk.java:796)
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 	... 9 more
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 	Suppressed: java.io.IOException: No original dex files found for dex location /system/app/SougouInput/SougouInput.apk
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 		at dalvik.system.DexFile.openDexFileNative(Native Method)
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 		at dalvik.system.DexFile.openDexFile(DexFile.java:367)
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 		at dalvik.system.DexFile.<init>(DexFile.java:112)
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 		at dalvik.system.DexFile.<init>(DexFile.java:77)
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 		at dalvik.system.DexPathList.loadDexFile(DexPathList.java:359)
01-01 08:00:30.230  5533  5533 E AndroidRuntime: 		at dalvik.system.DexPathList.makeElements(DexPathList.java:323)
...

以上log与dex有关,查看源码中的编译规则

user_variant := $(filter user userdebug,$(TARGET_BUILD_VARIANT))
ifneq (,$(user_variant))
WITH_DEXPREOPT := true
WITH_DEXPREOPT_PIC := true
endif

当判断到build版本是user/userdebug时会默认打开odex

ODEX是安卓上的应用程序apk中提取出来的可运行文件,即将APK中的classes.dex文件通过dex优化过程将其优化生成一个·dex文件单独存放,原APK中的classes.dex文件会保留的。
这样做可以加快软件的启动速度,预先提取,减少对RAM的占用,因为没有odex的话,系统要从apk包中提取dex再运行。

故先进行简单的验证修改,先将

WITH_DEXPREOPT := false
WITH_DEXPREOPT_PIC := false

将odex关闭,编译后刷机发现不再报错,但这样关闭了所有应用的odex,会导致第一次启动apk慢,故可以关闭个别apk

在SouGou输入法预置的文件中的Android.mk添加

LOCAL_DEX_PREOPT := false

验证后ok


附二

在设置搜狗输入法为默认过程中出现不能系统启动自己调起输入法问题,需要手动到设置下选择输入法才会起效

该问题建议先检查自己预置的apk的组建名是否正确,正如本人就犯了这个错误。

当然在网上发现了一篇文章https://blog.csdn.net/ansondroider/article/details/83820407,问题和本人一致,但不是本人犯得低级错误,且log与本人差不多,故在此做下记录

log:

01-02 03:14:58.469 system_process W/InputMethodManagerService: Couldn't create dir.: /data/system/inputmethod
01-02 03:14:58.479 system_process W/InputMethodManagerService: Default IME is uninstalled. Choose new default IME.
01-02 03:14:58.480 system_process W/InputMethodManagerService: Unknown input method from prefs: com.android.inputmethod.latin/.LatinIME
    java.lang.IllegalArgumentException: Unknown id: com.android.inputmethod.latin/.LatinIME
        at com.android.server.InputMethodManagerService.setInputMethodLocked(InputMethodManagerService.java:1806)
        at com.android.server.InputMethodManagerService.updateInputMethodsFromSettingsLocked(InputMethodManagerService.java:1756)
        at com.android.server.InputMethodManagerService.updateFromSettingsLocked(InputMethodManagerService.java:1715)
        at com.android.server.InputMethodManagerService.<init>(InputMethodManagerService.java:744)
        at com.android.server.SystemServer.startOtherServices(SystemServer.java:553)
        at com.android.server.SystemServer.run(SystemServer.java:261)
        at com.android.server.SystemServer.main(SystemServer.java:175)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:963)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:758)

该处的log输出位置在frameworks/base/services/core/java/com/android/server/InputMethodManagerService.java中

void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
    ...
    // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
    // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
    // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
    // enabled.
    String id = mSettings.getSelectedInputMethod();
    // There is no input method selected, try to choose new applicable input method.
    if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) {
        id = mSettings.getSelectedInputMethod();
    }
    if (!TextUtils.isEmpty(id)) {
        try {
            setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
        } catch (IllegalArgumentException e) {
            Slog.w(TAG, "Unknown input method from prefs: " + id, e);  <<<<<<<<<<<<----
            resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_IME_FAILED);
        }
        mShortcutInputMethodsAndSubtypes.clear();
    } else {
        // There is no longer an input method set, so stop any current one.
        resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_NO_IME);
    }
    ...
}

出错的位置, 加上重置输入法的代码:

Slog.w(TAG, "Unknown input method from prefs: " + id, e);  <<<<<<<<<<<<----
resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_IME_FAILED);
//AnsonCode clear input method after error.
//清空数据库中ENABLED_INPUT_METHODS已启用的输入法
Settings.Secure.putStringForUser(mContext.getContentResolver(), 
	Settings.Secure.ENABLED_INPUT_METHODS, "", mSettings.getCurrentUserId());
//重置启用输入法
mSettings.enableAllIMEsIfThereIsNoEnabledIME();
//重置默认输入法.
resetDefaultImeLocked(mContext);

作者提到没有去清空数据库, 导致, 不管如何重置输入法, 系统都无法正常启用所预置的输入

private void resetDefaultImeLocked(Context context) {
    // Do not reset the default (current) IME when it is a 3rd-party IME
    if (mCurMethodId != null && !InputMethodUtils.isSystemIme(mMethodMap.get(mCurMethodId))) {
        return;
    }
    final List<InputMethodInfo> suitableImes = InputMethodUtils.getDefaultEnabledImes(
            context, mSystemReady, mSettings.getEnabledInputMethodListLocked());
    //因为数据库中启用的输入法依然是latinIME, 所以, defIm一直为空.
    if (suitableImes.isEmpty()) {
        Slog.i(TAG, "No default found");
        return;
    }
    final InputMethodInfo defIm = suitableImes.get(0);
    Slog.i(TAG, "Default found, using " + defIm.getId());
    setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
}

以后遇到该问题可以尝试一下


附三

Android7.0设置默认输入法需要注意该点当手机已经通电开机但是用户并有解锁锁屏的时候,Android N运行于一个安全的模式,也就是Dierect Boot模式。一般情况下,应用是无法在Direct Boot模式下运行的,如果需要某个app能够在Direct Boot模式下运行,需要注册相关APP的组件。
应用组件申请在Direct Boot模式下运行:在AndroidManinfest.xml中设置 android:directBootAware=“true”。

在分析7.0过程中发现在启动Launcher之前会先启动一个FallbackHome,之后才会启动Launcher,通过调查发现FallbackHome属于Settings中的一个activity,Settings的android:directBootAware为true,并且FallbackHome在category中配置


    <activity android:name=".system.FallbackHome"
              android:excludeFromRecents="true"
              android:theme="@style/FallbackHome">
        <intent-filter android:priority="-1000">
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.HOME" />
            <category android:name="android.intent.category.DEFAULT" />
        intent-filter>
    activity>

所以在ActivityManagerService启动Home界面时,从PackageManagerService中获取到的Home界面就是FallbackHome。

这个安全模式就会导致我的输入法在系统起来的时候服务没起来,进而导致输入法弹不出来。

解决方法

在输入法的AndroidManifest.xml中声明android:directBootAware为true。

当然在原生的设置中已经包含该配置

你可能感兴趣的:(Android之旅)