Settings启动以及加载第三方系统apk

Settings源码在\packages\apps\Settings目录下(vendor下可能也有oem定制的设置)。

查看AndroidManifest.xml可以看出Settings的主Activity是Settings,但是进入Settings.java发现里面只是声明了一些Activity,所有的Activity都继承SettingsActivity。

<activity android:name="Settings"
        android:taskAffinity="com.android.settings.root"
        android:label="@string/settings_label_launcher"
        android:launchMode="singleTask">
    <intent-filter android:priority="1">
        <action android:name="android.settings.SETTINGS" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
    <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
        android:value="true" />
</activity>

<!-- Alias for launcher activity only, as this belongs to each profile. -->
<activity-alias android:name="Settings"
        android:taskAffinity="com.android.settings.root"
        android:label="@string/settings_label_launcher"
        android:launchMode="singleTask"
        android:targetActivity="Settings">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
</activity-alias>

一、onCreate方法

先看下onCreate方法,通过savedState判断是否之前有保存任务,通过initialFragmentName判断是否有想要进入的Fragment,点击设置图标进入设置一级目录initialFragmentName为null,否则表示进入二级或者多级目录。

@Override
protected void onCreate(Bundle savedState) {
    super.onCreate(savedState);
……

    // Getting Intent properties can only be done after the super.onCreate(...)
    final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
    Log.d(LOG_TAG, "onCreate initialFragmentName="+initialFragmentName);
    final ComponentName cn = intent.getComponent();
    final String className = cn.getClassName();

    mIsShowingDashboard = className.equals(Settings.class.getName());

    // This is a "Sub Settings" when:
    // - this is a real SubSettings
    // - or :settings:show_fragment_as_subsetting is passed to the Intent
    final boolean isSubSettings = this instanceof SubSettings ||
            intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);

    // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content
    // insets
    if (isSubSettings) {
        setTheme(R.style.Theme_SubSettings);
    }

    setContentView(mIsShowingDashboard ?
            R.layout.settings_main_dashboard : R.layout.settings_main_prefs);

    mContent = findViewById(R.id.main_content);

    getFragmentManager().addOnBackStackChangedListener(this);
    Log.d(LOG_TAG, "onCreate savedState="+savedState);
    if (savedState != null) {
        // We are restarting from a previous saved state; used that to initialize, instead
        // of starting fresh.
        setTitleFromIntent(intent);

        ArrayList<DashboardCategory> categories =
                savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
        if (categories != null) {
            mCategories.clear();
            mCategories.addAll(categories);
            setTitleFromBackStack();
        }
    } else {
        launchSettingFragment(initialFragmentName, isSubSettings, intent);
    }

……
}

然后执行launchSettingFragment跳转到相应的Fragment或者Preference。

二、onResume方法

onResume比较简洁,mDevelopmentSettingsListener是监听是否打开开发者模式的本地广播,以及更新Title。

@Override
protected void onResume() {
    super.onResume();

    mDevelopmentSettingsListener = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            updateTilesList();
        }
    };
    LocalBroadcastManager.getInstance(this).registerReceiver(mDevelopmentSettingsListener,
            new IntentFilter(DevelopmentSettingsEnabler.DEVELOPMENT_SETTINGS_CHANGED_ACTION));

    registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));

    updateTilesList();
    updateDeviceIndex();
}

updateTilesList使用AsyncTask去判断title是否有变化,通过somethingChanged表示是否有变化,分别判断了wifi、蓝牙、设备连接、sim设置、电量、应用数据使用、时间、网络等是否变化,他们直接用或的关系连接。

private void updateTilesList() {
    // Generally the items that are will be changing from these updates will
    // not be in the top list of tiles, so run it in the background and the
    // SettingsDrawerActivity will pick up on the updates automatically.
    AsyncTask.execute(new Runnable() {
        @Override
        public void run() {
            doUpdateTilesList();
        }
    });
}

private void doUpdateTilesList() {
    PackageManager pm = getPackageManager();
    final UserManager um = UserManager.get(this);
    final boolean isAdmin = um.isAdminUser();
    final FeatureFactory featureFactory = FeatureFactory.getFactory(this);
    boolean somethingChanged = false;
    final String packageName = getPackageName();
    final StringBuilder changedList = new StringBuilder();
    somethingChanged = setTileEnabled(changedList,
            new ComponentName(packageName, WifiSettingsActivity.class.getName()),
            pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin) || somethingChanged;

    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.BluetoothSettingsActivity.class.getName()),
            pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin)
            || somethingChanged;


    // Enable DataUsageSummaryActivity if the data plan feature flag is turned on otherwise
    // enable DataPlanUsageSummaryActivity.
    somethingChanged = setTileEnabled(changedList,
            new ComponentName(packageName, Settings.DataUsageSummaryActivity.class.getName()),
            Utils.isBandwidthControlEnabled() /* enabled */,
            isAdmin) || somethingChanged;

    somethingChanged = setTileEnabled(changedList,
            new ComponentName(packageName,
                    Settings.ConnectedDeviceDashboardActivity.class.getName()),
            !UserManager.isDeviceInDemoMode(this) /* enabled */,
            isAdmin) || somethingChanged;

    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.SimSettingsActivity.class.getName()),
            Utils.showSimCardTile(this), isAdmin)
            || somethingChanged;

    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.PowerUsageSummaryActivity.class.getName()),
            mBatteryPresent, isAdmin) || somethingChanged;

    final boolean isDataUsageSettingsV2Enabled =
            FeatureFlagUtils.isEnabled(this, FeatureFlags.DATA_USAGE_SETTINGS_V2);
    // Enable new data usage page if v2 enabled
    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.DataUsageSummaryActivity.class.getName()),
            Utils.isBandwidthControlEnabled() && isDataUsageSettingsV2Enabled, isAdmin)
            || somethingChanged;
    // Enable legacy data usage page if v2 disabled
    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.DataUsageSummaryLegacyActivity.class.getName()),
            Utils.isBandwidthControlEnabled() && !isDataUsageSettingsV2Enabled, isAdmin)
            || somethingChanged;

    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.UserSettingsActivity.class.getName()),
            UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
                    && !Utils.isMonkeyRunning(), isAdmin)
            || somethingChanged;

    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.NetworkDashboardActivity.class.getName()),
            !UserManager.isDeviceInDemoMode(this), isAdmin)
            || somethingChanged;

    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.DateTimeSettingsActivity.class.getName()),
            !UserManager.isDeviceInDemoMode(this), isAdmin)
            || somethingChanged;

    final boolean showDev = DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this)
            && !Utils.isMonkeyRunning();
    final boolean isAdminOrDemo = um.isAdminUser() || um.isDemoUser();
    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.DevelopmentSettingsDashboardActivity.class.getName()),
            showDev, isAdminOrDemo)
            || somethingChanged;

    // Enable/disable backup settings depending on whether the user is admin.
    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
            BackupSettingsActivity.class.getName()), true, isAdmin)
            || somethingChanged;

    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.WifiDisplaySettingsActivity.class.getName()),
            WifiDisplaySettings.isAvailable(this), isAdmin)
            || somethingChanged;

    // Enable/disable the Me Card page.
    final boolean aboutPhoneV2Enabled = featureFactory
            .getAccountFeatureProvider()
            .isAboutPhoneV2Enabled(this);
    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.MyDeviceInfoActivity.class.getName()),
            aboutPhoneV2Enabled, isAdmin)
            || somethingChanged;
    somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
                    Settings.DeviceInfoSettingsActivity.class.getName()),
            !aboutPhoneV2Enabled, isAdmin)
            || somethingChanged;

    if (UserHandle.MU_ENABLED && !isAdmin) {

        // When on restricted users, disable all extra categories (but only the settings ones).
        final List<DashboardCategory> categories = mDashboardFeatureProvider.getAllCategories();
        synchronized (categories) {
            for (DashboardCategory category : categories) {
                final int tileCount = category.getTilesCount();
                for (int i = 0; i < tileCount; i++) {
                    final ComponentName component = category.getTile(i).intent.getComponent();
                    final String name = component.getClassName();
                    final boolean isEnabledForRestricted = ArrayUtils.contains(
                            SettingsGateway.SETTINGS_FOR_RESTRICTED, name) || (isAdminOrDemo
                            && Settings.DevelopmentSettingsDashboardActivity.class.getName()
                            .equals(name));
                    if (packageName.equals(component.getPackageName())
                            && !isEnabledForRestricted) {
                        somethingChanged =
                                setTileEnabled(changedList, component, false, isAdmin)
                                        || somethingChanged;
                    }
                }
            }
        }
    }

    // Final step, refresh categories.
    if (somethingChanged) {
        Log.d(LOG_TAG, "Enabled state changed for some tiles, reloading all categories "
                + changedList.toString());
        updateCategories();
    } else {
        Log.d(LOG_TAG, "No enabled state changed, skipping updateCategory call");
    }
}

如果somethingChanged为true,则调用updateCategories方法刷新目录,updateCategories方法定义在SettingsActivity的父类SettingsDrawerActivity.java中SettingsDrawerActivity.java在frameworks\base\packages\SettingsLib\src\com\android\settingslib\drawer目录下。updateCategories又执行了一个AsyncTask。

public void updateCategories() {
    new CategoriesUpdateTask().execute();
}
private class CategoriesUpdateTask extends AsyncTask<Void, Void, Void> {

    private final CategoryManager mCategoryManager;

    public CategoriesUpdateTask() {
        mCategoryManager = CategoryManager.get(SettingsDrawerActivity.this);
    }

    @Override
    protected Void doInBackground(Void... params) {
        mCategoryManager.reloadAllCategories(SettingsDrawerActivity.this, getSettingPkg());
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        mCategoryManager.updateCategoryFromBlacklist(sTileBlacklist);
        onCategoriesChanged();
    }
}

重点看下CategoriesUpdateTask的doInBackground中的后台任务reloadAllCategories,reloadAllCategories又调用了tryInitCategories方法,主要的目录信息是从TileUtils的getCategories得到,然后进行排序过滤得到最终的目录列表。

public synchronized void reloadAllCategories(Context context, String settingPkg) {
    final boolean forceClearCache = mInterestingConfigChanges.applyNewConfig(
            context.getResources());
    mCategories = null;
    tryInitCategories(context, forceClearCache, settingPkg);
}

TileUtils的getCategories方法通过getTilesForAction获得tile列表,getTilesForIntent方法中先通过PackageManager的queryIntentActivitiesAsUser方法获取到所有的应用,然后通过resolved.system对非系统app进行初始过滤,然后判断activityInfo.metaData是否包含"com.android.settings.category",如果包含则将这个应用或者action添加到列表中。

public static void getTilesForIntent(
        Context context, UserHandle user, Intent intent,
        Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,
        boolean usePriority, boolean checkCategory, boolean forceTintExternalIcon,
        boolean shouldUpdateTiles) {
    PackageManager pm = context.getPackageManager();
    List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
            PackageManager.GET_META_DATA, user.getIdentifier());
    Map<String, IContentProvider> providerMap = new HashMap<>();
    for (ResolveInfo resolved : results) {
        if (!resolved.system) {
            // Do not allow any app to add to settings, only system ones.
            continue;
        }
        ActivityInfo activityInfo = resolved.activityInfo;
        Bundle metaData = activityInfo.metaData;
        String categoryKey = defaultCategory;

        // Load category
        if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))
                && categoryKey == null) {
            Log.e(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "
                    + intent + " missing metadata "
                    + (metaData == null ? "" : EXTRA_CATEGORY_KEY));
            continue;
        } else {
            categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
            Log.e(LOG_TAG, "name: " + resolved.activityInfo.name + ", categoryKey:"+categoryKey);
        }

        Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
                activityInfo.name);
        Tile tile = addedCache.get(key);
        if (tile == null) {
            tile = new Tile();
            tile.intent = new Intent().setClassName(
                    activityInfo.packageName, activityInfo.name);
            tile.category = categoryKey;
            tile.priority = usePriority ? resolved.priority : 0;
            tile.metaData = activityInfo.metaData;
            updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
                    pm, providerMap, forceTintExternalIcon);
            if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
            addedCache.put(key, tile);
        } else if (shouldUpdateTiles) {
            updateSummaryAndTitle(context, providerMap, tile);
        }

        if (!tile.userHandle.contains(user)) {
            tile.userHandle.add(user);
        }
        if (!outTiles.contains(tile)) {
            outTiles.add(tile);
        }
    }
}

三、第三方系统app添加到设置

第二部分的最后提到metaData包含"com.android.settings.category"的系统app可以加入到设置中,那么理论上可以通过在AndroidManifest.xml中配置activity的meta-data属性为"com.android.settings.category"然后在源码编译下即可添加到设置。

其实还有一个限制,此限制也是指定第三方app在设置中的位置(是在一级目录还是二级目录),可以查看frameworks\base\packages\SettingsLib\src\com\android\settingslib\drawer\CategoryKey.java的实例key。

展示实例

<intent-filter android:priority="0">
    <action android:name="com.android.settings.action.SETTINGS" />
</intent-filter>

<meta-data
    android:name="com.android.settings.category"
    android:value="com.android.settings.category.ia.homepage" />
<meta-data
    android:name="com.android.settings.title"
    android:value="@string/app_name" />
<meta-data
    android:name="com.android.settings.summary"
    android:value="@string/app_name" />

 

<think>好的,用户问的是如何在Android 13系统中设置和使用第三方输入法。我需要根据提供的引用内容来组织答案。首先,用户可能想知道安装和启用的基本步骤,可能还需要涉及系统默认设置的修改,特别是因为之前的系统版本需要修改数据库或配置。 首先,安装第三方输入法。根据引用4,在Android 7.1中需要将输入法添加到系统编译中,可能需要系统签名。但如果是普通用户,可能只需要从应用商店下载安装。开发者可能需要内置到系统,这时候需要系统签名或者编译到ROM中,比如引用4提到的Android.mk文件。不过对于普通用户来说,可能不需要这部分,但用户的问题可能包括开发者的角度,所以需要区分两种情况。 然后是启用输入法。用户安装后,需要去设置里启用输入法。引用3提到输入法ID的形式是包名/类名,比如讯飞输入法的例子。用户可能需要在系统设置中找到语言与输入法,启用新安装的输入法。 接下来设置默认输入法。根据引用1、2、5,在Android 11、14、10中设置默认输入法需要修改系统数据库或配置文件,可能需要系统权限。对于普通用户,可能在设置里直接选择默认输入法即可,但如果是系统集成,可能需要修改SettingsProvider的代码,比如在DatabaseHelper.java中加载默认输入法ID,或者在defaults.xml中配置默认值。例如引用5提到在系统数据库加载时设置输入法ID,以及修改config中的ID。 另外,引用2提到Android 14中需要根据包名设置默认输入法,可能需要核心代码位于SettingsProvider的相关文件中。对于Android 13,可能方法类似,需要找到对应的文件进行修改,并确保输入法有系统权限。 可能还需要处理权限问题,比如系统签名或者特权权限,否则第三方输入法可能无法被设置为默认。例如引用4中需要将输入法编译到系统镜像中,可能需要系统签名。 用户可能遇到的问题包括如何获取输入法的完整ID,如包名/类名,这可能需要查看输入法的信息或者通过adb命令获取。 总结回答的结构应该是:安装方法(普通用户和开发者)、启用步骤、设置默认的方法(用户界面和系统集成),以及注意事项如权限和系统签名。同时,根据引用内容,需要指出不同Android版本可能有不同的实现方式,但核心逻辑类似,即修改系统数据库或配置文件中的默认输入法ID。 还需要生成相关问题,比如系统权限如何获取,如何内置到系统,不同Android版本的差异,以及adb命令的使用。</think>### Android 13系统中第三方输入法的设置与使用指南 #### 1. **安装第三方输入法** - **普通用户**:通过应用商店直接下载安装第三方输入法(如讯飞、搜狗等)。 - **系统集成**:若需将输入法内置到系统镜像中,需以下步骤: 1. 将输入法APK添加至系统编译工程,并赋予系统签名权限[^4]。 2. 在`Android.mk`文件中声明输入法模块,例如: ```makefile # 示例:讯飞输入法的系统集成配置 LOCAL_MODULE := FlyIME LOCAL_SRC_FILES := FlyIME.apk LOCAL_MODULE_CLASS := APPS LOCAL_PRIVILEGED_MODULE := true LOCAL_CERTIFICATE := platform ``` 3. 重新编译系统并刷入设备[^4]。 #### 2. **启用输入法** - 进入系统设置:**设置 > 系统 > 语言与输入法 > 虚拟键盘**。 - 找到已安装的第三方输入法(如`com.iflytek.inputmethod/.FlyIME`[^3]),启用开关。 #### 3. **设置为默认输入法** - **用户界面操作**:在**语言与输入法 > 默认键盘**中选择目标输入法。 - **系统级设置**(需系统权限): 1. 修改系统配置文件`defaults.xml`,添加默认输入法ID: ```xml <string name="default_input_method" translatable="false">com.iflytek.inputmethod/.FlyIME</string> ``` 2. 在`DatabaseHelper.java`中加载默认值,确保系统数据库初始化时写入该ID[^5]。 3. 重新启动设备生效[^2]。 #### 4. **权限与兼容性** - 第三方输入法需声明`android.permission.WRITE_SECURE_SETTINGS`权限[^5]。 - 若输入法未显示,检查是否缺少系统签名或未授予**安全输入**权限[^4]。 #### 5. **验证与调试** - 通过ADB命令查看当前默认输入法: ```bash adb shell settings get secure default_input_method ``` - 强制设置默认输入法(需Root): ```bash adb shell settings put secure default_input_method com.iflytek.inputmethod/.FlyIME ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值