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中,如果你想检查每个输入法是否已安装以及是否允许设置默认输入法,你需要通过`InputMethodManager`和服务提供者`Settings`来进行操作。以下是一个大概的步骤: 1. **获取InputMethodManager实例**: 首先,你需要创建一个`InputMethodManager`的实例,以便管理全局的输入法状态。 ```java InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); ``` 2. **查询InputMethodInfo**: 使用`imm.getInputMethodList()`得到一个包含所有输入法信息的列表,然后遍历这个列表: ```java ArrayList<InputMethodInfo> methods = imm getInputMethodList(); for (InputMethodInfo method : methods) { // 对每个InputMethodInfo做进一步检查 } ``` 3. **检查安装和权限**: 对于每个`InputMethodInfo`,你可以查看其`hasSystemFeature(InputMethod.FEATURE_INPUT_METHOD)`属性,如果为真,那么该输入法系统内置的。至于是否允许设置默认,一般需要访问`Settings`服务,并找到对应的`InputMethodSubtype`来读取设置: ```java ContentResolver resolver = getContentResolver(); Cursor cursor = resolver.query(InputMethodSubtype.CONTENT_URI, null, null, null, null); while (cursor.moveToNext()) { String id = cursor.getString(cursor.getColumnIndex(InputMethodSubtype.ID)); int active = cursor.getInt(cursor.getColumnIndex(InputMethodSubtype.ACTIVE)); if (method.getId().equals(id)) { // active为1表示默认 boolean allowedAsDefault = active == 1; // ...其他处理 } cursor.close(); } ``` 请注意,上述代码示例并不是完整的,实际操作时可能需要处理各种异常和权限问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KwCoding

谢了老板您讷~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值