預設輸入法刪除後無法自動切換到新輸入法
阿新 • • 發佈:2018-11-07
平臺
RK3288 + Android 5.1
問題
1. 系統初始版本為1.0.0, 包含了預設輸入法 LatinIME(拉丁輸入法) 和 OpenWnn(日文輸入法)
2. OTA升級中, 內建訊飛輸入法, 同時刪除1.0.0版本中的兩個輸入法.
3. OTA升級後, 文字輸入框無法正常調起輸入法軟鍵盤執行輸入.
4. 開啟設定 > 語言和輸入法 > 鍵盤和輸入法中, 當前輸入法為空, 輸入法中也沒有看到 訊飛輸入法
分析
Logcat 分析:
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)
相關程式碼如下:
|--frameworks/base/services/core/java/com/android/server/InputMethodManagerService.java
void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
if (enabledMayChange) {
List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
for (int i=0; i<enabled.size(); i++) {
// We allow the user to select "disabled until used" apps, so if they
// are enabling one of those here we now need to make it enabled.
InputMethodInfo imm = enabled.get(i);
try {
ApplicationInfo ai = mIPackageManager.getApplicationInfo(imm.getPackageName(),
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
mSettings.getCurrentUserId());
if (ai != null && ai.enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
if (DEBUG) {
Slog.d(TAG, "Update state(" + imm.getId()
+ "): DISABLED_UNTIL_USED -> DEFAULT");
}
mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(),
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId(),
mContext.getBasePackageName());
}
} catch (RemoteException e) {
}
}
}
// 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));
mPreScanHelper.addScanItem(PreScanHelper.SCAN_TYPE_IME, mContext, id);
mPreScanHelper.flushToFile();
} catch (IllegalArgumentException e) {
//LOG輸出的地方.
Slog.w(TAG, "Unknown input method from prefs: " + id, e);
mCurMethodId = null;
unbindCurrentMethodLocked(true, false);
}
mShortcutInputMethodsAndSubtypes.clear();
} else {
// There is no longer an input method set, so stop any current one.
mCurMethodId = null;
unbindCurrentMethodLocked(true, false);
}
// Here is not the perfect place to reset the switching controller. Ideally
// mSwitchingController and mSettings should be able to share the same state.
// TODO: Make sure that mSwitchingController and mSettings are sharing the
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
}
於是, 出錯的位置, 加上重置輸入法的程式碼:
//("start reset IME");
mCurMethodId = null;
unbindCurrentMethodLocked(true, false);
//AnsonCode clear input method after error.
//mSettings.putSelectedInputMethod("");
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS, "", mSettings.getCurrentUserId());
//("enable All IMES");
mSettings.enableAllIMEsIfThereIsNoEnabledIME();
//("reset Default IME");
resetDefaultImeLocked(mContext);
1. 清空資料庫中ENABLED_INPUT_METHODS已啟用的輸入法
2. 重置啟用輸入法
3. 重置預設輸入法.
最開始, 並沒有去清空資料庫, 導致, 不管如何重置輸入法, 系統都無法正常啟用訊飛.
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;
}
InputMethodInfo defIm = null;
for (InputMethodInfo imi : mMethodList) {
if (defIm == null) {
if (InputMethodUtils.isValidSystemDefaultIme(
mSystemReady, imi, context)) {
defIm = imi;
Slog.i(TAG, "Selected default: " + imi.getId());
}
}
}
if (defIm == null && mMethodList.size() > 0) {
defIm = InputMethodUtils.getMostApplicableDefaultIME(
mSettings.getEnabledInputMethodListLocked());
//在這裡, 因為資料庫中啟用的輸入法依然是latinIME, 所以, defIm一直為空.
if (defIm != null) {
Slog.i(TAG, "Default found, using " + defIm.getId());
} else {
Slog.i(TAG, "No default found");
}
}
if (defIm != null) {
setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
}
}
也就是說, 若不清空資料庫, 則會輸入LOG: No default found
defIm為空的原因, 見下面程式碼.
|--frameworks/base/core/java/com/android/internal/inputmethod/InputMethodUtils.java
public static InputMethodInfo getMostApplicableDefaultIME(List<InputMethodInfo> enabledImes) {
if (enabledImes == null || enabledImes.isEmpty()) {
return null;
}
// We'd prefer to fall back on a system IME, since that is safer.
int i = enabledImes.size();
int firstFoundSystemIme = -1;
while (i > 0) {
i--;
final InputMethodInfo imi = enabledImes.get(i);
if (InputMethodUtils.isSystemImeThatHasEnglishKeyboardSubtype(imi)
&& !imi.isAuxiliaryIme()) {
return imi;
}
if (firstFoundSystemIme < 0 && InputMethodUtils.isSystemIme(imi)
&& !imi.isAuxiliaryIme()) {
firstFoundSystemIme = i;
}
}
return enabledImes.get(Math.max(firstFoundSystemIme, 0));
}
public List<InputMethodInfo> getEnabledInputMethodListLocked() {
return createEnabledInputMethodListLocked(
getEnabledInputMethodsAndSubtypeListLocked());
}
public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
ArrayList<Pair<String, ArrayList<String>>> imsList
= new ArrayList<Pair<String, ArrayList<String>>>();
final String enabledInputMethodsStr = getEnabledInputMethodsStr();
if (TextUtils.isEmpty(enabledInputMethodsStr)) {
return imsList;
}
mInputMethodSplitter.setString(enabledInputMethodsStr);
while (mInputMethodSplitter.hasNext()) {
String nextImsStr = mInputMethodSplitter.next();
mSubtypeSplitter.setString(nextImsStr);
if (mSubtypeSplitter.hasNext()) {
ArrayList<String> subtypeHashes = new ArrayList<String>();
// The first element is ime id.
String imeId = mSubtypeSplitter.next();
while (mSubtypeSplitter.hasNext()) {
subtypeHashes.add(mSubtypeSplitter.next());
}
imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
}
}
return imsList;
}
public String getEnabledInputMethodsStr() {
mEnabledInputMethodsStrCache = Settings.Secure.getStringForUser(
mResolver, Settings.Secure.ENABLED_INPUT_METHODS, mCurrentUserId);
if (DEBUG) {
Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache
+ ", " + mCurrentUserId);
}
return mEnabledInputMethodsStrCache;
}
補充
跟輸入法相關的兩個資料庫變數:
Settings.Secure.ENABLED_INPUT_METHODS 使用/啟用輸入法, 這會顯示在設定中的輸入法列表
Settings.Secure.DEFAULT_INPUT_METHOD 預設輸入法
檢視資料庫:
sqlite3 /data/data/com.android.providers.settings/databases/settings.db
select * from secure;
可以看出當前輸入法的值:
50|enabled_input_methods|com.iflytek.inputmethod/.FlyIME
51|default_input_method|com.iflytek.inputmethod/.FlyIME