Android.Settings类&设置默认输入法

Posted wkw1125

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android.Settings类&设置默认输入法相关的知识,希望对你有一定的参考价值。

android的所有系统设置项(如音量、触摸提示音、默认输入法等信息)均是保存到一个数据库。在界面上调整设置时将值保存到该数据库,开机时将从数据库读取值作为默认设置。这些读取、设置操作都可以通过API或adb命令进行。
P.S.本文仅作为学习记录,知识点、代码上下文并不完整与正确,以后再改善吧。

需求

机顶盒项目要求Android系统开机后默认启用指定的触宝输入法;默认关闭触摸声、锁屏声、电源充电声。
之前的做法似乎是修改固件实现,但由于早期固件已经用于生产,后续的OTA升级没有办法使修改生效,就导致无法对早期盒子方便地进行默认设置(除非烧写固件),因此考虑从应用层实现,因而有了本次实践。

Settings类API

系统设置主要使用的API位于android.provider.Settings,来看看Settings类的结构:

该类定义了许多常量,实际上就是每个设置项在数据库中的字段名;此外还有修改数据的put方法与读取的get方法。
需要着重注意的是,这些常量被细分到了System、Secure、Global三个内部类中,对应三种不同的权限或者说作用范围,在修改不同内部类的属性时,需调用对应内部类的put方法方能生效。

禁用触摸声的常量定义属于Settings.System范围,设置禁用的方法为:

String key = Settings.System.SOUND_EFFECTS_ENABLED;//"sound_effects_enabled"
int value = 0;//0禁用 1启用
boolean success = Settings.System.putInt(mContext.getContentResolver(), key, value);

再如,默认输入法常量定义属于Settings.Secure范围,设置方法为:
(注意此处putString()方法是Secure内部类的方法)

String key = Settings.Secure.DEFAULT_INPUT_METHOD;//"default_input_method"
String id = "com.android.inputmethod.latin/.LatinIME";//Android默认输入法ID
boolean success = Settings.Secure.putString(mContext.getContentResolver(), key, id);

为什么要强调使用的内部类呢?因为System、Secure、Global三个内部类都有同样的put、get方法,方法参数又只是String类型的键值,因此很容易用错方法。通过源码知道,当调用方法和常量不属于同一内部类时,最终会设置失败(返回值为false)。比如,System.putInt()最后会调用System.putStringForUser()进行处理,如果常量属于Secure或Global范围,将直接返回false而不会进行存储。

/** @hide */
public static boolean putStringForUser(ContentResolver resolver, String name, String value,
        int userHandle) 
    if (MOVED_TO_SECURE.contains(name)) 
        Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
                + " to android.provider.Settings.Secure, value is unchanged.");
        return false;
    
    if (MOVED_TO_GLOBAL.contains(name) || MOVED_TO_SECURE_THEN_GLOBAL.contains(name)) 
        Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
                + " to android.provider.Settings.Global, value is unchanged.");
        return false;
    
    return sNameValueCache.putStringForUser(resolver, name, value, userHandle);

Settings数据库

(本节所述均为个人的理解,时间关系没有去确认,如果误导请包涵和指正)
上节所做的设置,最后都被保存到系统数据库中,(不知道称其为数据库还是ContentProvider比较准确),系统在开机时读取这些值进行默认的设置。
Settings实际上是一个apk,负责所有系统级别属性的设置。我司正在Realtek芯片上做产品,Realtek就提供了他们包装的Settings作为demo供我们调试,从Settings的源码中可以看到很多定制的功能。

Settings数据库的位置位于

adb shell
cd /data/data/com.android.providers.settings/databases
- settings.db-backup
- settings.db-journal

将settings.db-backup改名为settings.db,即可使用sqlite工具查看,如下:

可以看到,该数据库中有三个表system、secure、global对应API中的定义的常量,此处触摸声”sound_effects_enabled”位于system表中,当前状态为0。

以上是我个人的推测,因为该数据库中并不存在默认输入法的字段常量“default_input_method”(然而却可以生效)。从文件名“settings.db-backup”可以知道,这并不是系统的实时数据,而仅是备份数据。并且,对于系统的设置的变更,该数据库也并没有实时改变,也验证了这一点。Settings设置的实时数据被存储到哪里,我目前没有找到,但八九不离十,由于并不影响功能实现,就暂且搁置。

P.S.此处也可以看到realtek关键字,可见不同的平台厂商对android有不同的定制,需要对症下药。

ADB修改数据库

除了调用android.provider.Settings的API来修改系统设置外,在开发阶段还可以使用adb命令方便地修改Settings数据库,并且界面上是立即生效的哦(视厂商刷新机制而定)。如,关闭触摸声、设置默认输入法,可以用以下adb命令立即生效(仍然需要注意区别system/secure/global):

settings put system sound_effects_enabled 0
settings put secure default_input_method com.android.inputmethod.latin/.LatinIME

可以用ADB命令来确认要修改的键值对。

设置默认输入法

有了上述的背景知识,就可以轻松地设置系统的默认输入法了。

代码

这里从工程中抽出了设置默认输入法的代码,可能并不完整,但思路很清晰了:

    import android.provider.Settings;//导入包

    /**
     * 若触宝输入法已安装,则设其为系统默认输入法
     * (写入Android系统数据库)
     */
    public static void setDefaultInputMethod(Context context)
        //获取系统已安装的输入法ID
        String[] methods = getInputMethodIdList(context);
        if (methods == null || methods.length == 0)
            EvLog.w(String.format("found no input method."));
            return;
        

        //检查是否安装触宝输入法
        //触宝输入法ID "com.cootek.smartinputv5/com.cootek.smartinput5.TouchPalIME";
        String targetKeyword = "TouchPal";
        String value = "";
        for (String m : methods)
            EvLog.d(String.format("find : %s", m));
            if (m.toLowerCase().contains(targetKeyword.toLowerCase()))
                value = m;//找到触宝输入法
            
        
        if (value == "") 
            EvLog.w(String.format("didn't find " + targetKeyword));
            return;
        

        //设置默认输入法
        String key = Settings.Secure.DEFAULT_INPUT_METHOD;
        boolean success = Settings.Secure.putString(context.getContentResolver(), key, value);
        EvLog.d(String.format("writeDbDefaultInputMethod(%s),result: %s", value,success));

        //读取默认输入法
        String current = Settings.Secure.getString(context.getContentResolver(),key);
        EvLog.d(String.format("current default: %s",current));      
    

    /**
     * 获取系统已安装的输入法ID
     * @param context
     * @return
     */
    public static String[] getInputMethodIdList(Context context)
        InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null && imm.getInputMethodList() != null)
            String[] methodIds = new String[imm.getInputMethodList().size()];
            for (int i = 0; i <imm.getInputMethodList().size(); i++) 
                methodIds[i] = imm.getInputMethodList().get(i).getId();
            
            return methodIds;
        
        return new String[];
    

输入法ID

设置Settings.Secure.DEFAULT_INPUT_METHOD的值时需要输入法的ID:

/**
 * 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";

输入法ID形式如:

com.android.inputmethod.latin/.LatinIME (缩写)
com.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME (完整)
com.sohu.inputmethod.sogou.xiaomi/.SogouIME (搜狗输入法小米版)

LatinIME是android自带输入法,斜杠/后的点.表示相同包名的缩写形式,但当IME的路径与包名并不完全相同时不能使用缩写形式,如触宝输入法: (注意到inputv5与input5的区别)

com.cootek.smartinputv5/com.cootek.smartinput5.TouchPalIME

不能缩写为

com.cootek.smartinputv5/.TouchPalIME (前缀不同,不能缩写)

getInputMethodIdList()方法展示了如何获取系统已安装的输入法ID。一开始因为同事提供了错误的触宝输入法ID缩写形式,导致设置默认输入法后,无法开启任何输入法(因为不存在上述缩写形式表示的输入法)。于是我通过getInputMethodIdList()获取系统安装的输入法ID,并通过关键字来确定要设置的输入法。
问题解决。

关联问题

接触到Settings类后,想起来以前处理的一个问题:通过定制的API修改系统分辨率后,重启后并不生效。
原因是在修改分辨率后,还要往Settings数据库中写入新的分辨率配置,否则,重启开机会根据Settings中的数据进行恢复。

以上是关于Android.Settings类&设置默认输入法的主要内容,如果未能解决你的问题,请参考以下文章

Android Settings 取消全选效果改为直接选中选项

Android Settings 修复多用户 修复添加访客界面卡住

Android 11.0 修复Settings→添加访客时界面卡住

从 QT 应用程序(com.android.settings)打开 android 设置

Android Studio 第五十六期 - Android之系统设置选项的包名

Android Settings(Preferences)开发