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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值