Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab644cb3 authored by Jason Chiu's avatar Jason Chiu Committed by Android (Google) Code Review
Browse files

Merge "Refactor SettingsLib for injection v2 inline toggle"

parents 79426f45 8269e922
Loading
Loading
Loading
Loading
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.drawer;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Parcel;
import android.util.Log;

import java.util.List;
import java.util.Objects;

/**
 * Description of a single dashboard tile which is generated from an activity.
 */
public class ActivityTile extends Tile {
    private static final String TAG = "ActivityTile";

    public ActivityTile(ComponentInfo info, String category) {
        super(info, category);
        setMetaData(info.metaData);
    }

    ActivityTile(Parcel in) {
        super(in);
    }

    @Override
    public int getId() {
        return Objects.hash(getPackageName(), getComponentName());
    }

    @Override
    public String getDescription() {
        return getPackageName() + "/" + getComponentName();
    }

    @Override
    protected CharSequence getComponentLabel(Context context) {
        final PackageManager pm = context.getPackageManager();
        final ComponentInfo info = getComponentInfo(context);
        return info == null
                ? null
                : info.loadLabel(pm);
    }

    @Override
    protected ComponentInfo getComponentInfo(Context context) {
        if (mComponentInfo == null) {
            final PackageManager pm = context.getApplicationContext().getPackageManager();
            final Intent intent = getIntent();
            final List<ResolveInfo> infoList =
                    pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
            if (infoList != null && !infoList.isEmpty()) {
                mComponentInfo = infoList.get(0).activityInfo;
                setMetaData(mComponentInfo.metaData);
            } else {
                Log.e(TAG, "Cannot find package info for "
                        + intent.getComponent().flattenToString());
            }
        }
        return mComponentInfo;
    }
}
+47 −63
Original line number Diff line number Diff line
@@ -30,9 +30,8 @@ import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Icon;
@@ -47,13 +46,11 @@ import androidx.annotation.VisibleForTesting;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

/**
 * Description of a single dashboard tile that the user can select.
 */
public class Tile implements Parcelable {
public abstract class Tile implements Parcelable {

    private static final String TAG = "Tile";

@@ -64,28 +61,27 @@ public class Tile implements Parcelable {

    @VisibleForTesting
    long mLastUpdateTime;
    private final String mActivityPackage;
    private final String mActivityName;
    private final String mComponentPackage;
    private final String mComponentName;
    private final Intent mIntent;

    private ActivityInfo mActivityInfo;
    protected ComponentInfo mComponentInfo;
    private CharSequence mSummaryOverride;
    private Bundle mMetaData;
    private String mCategory;

    public Tile(ActivityInfo activityInfo, String category) {
        mActivityInfo = activityInfo;
        mActivityPackage = mActivityInfo.packageName;
        mActivityName = mActivityInfo.name;
        mMetaData = activityInfo.metaData;
    public Tile(ComponentInfo info, String category) {
        mComponentInfo = info;
        mComponentPackage = mComponentInfo.packageName;
        mComponentName = mComponentInfo.name;
        mCategory = category;
        mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
        mIntent = new Intent().setClassName(mComponentPackage, mComponentName);
    }

    Tile(Parcel in) {
        mActivityPackage = in.readString();
        mActivityName = in.readString();
        mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
        mComponentPackage = in.readString();
        mComponentName = in.readString();
        mIntent = new Intent().setClassName(mComponentPackage, mComponentName);
        final int number = in.readInt();
        for (int i = 0; i < number; i++) {
            userHandle.add(UserHandle.CREATOR.createFromParcel(in));
@@ -101,8 +97,8 @@ public class Tile implements Parcelable {

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mActivityPackage);
        dest.writeString(mActivityName);
        dest.writeString(mComponentPackage);
        dest.writeString(mComponentName);
        final int size = userHandle.size();
        dest.writeInt(size);
        for (int i = 0; i < size; i++) {
@@ -112,16 +108,22 @@ public class Tile implements Parcelable {
        dest.writeBundle(mMetaData);
    }

    public int getId() {
        return Objects.hash(mActivityPackage, mActivityName);
    }
    /**
     * Unique ID of the tile
     */
    public abstract int getId();

    public String getDescription() {
        return mActivityPackage + "/" + mActivityName;
    }
    /**
     * Human-readable description of the tile
     */
    public abstract String getDescription();

    public String getPackageName() {
        return mActivityPackage;
        return mComponentPackage;
    }

    public String getComponentName() {
        return mComponentName;
    }

    /**
@@ -154,7 +156,7 @@ public class Tile implements Parcelable {
    }

    /**
     * Check whether title has order.
     * Check whether tile has order.
     */
    public boolean hasOrder() {
        return mMetaData.containsKey(META_DATA_KEY_ORDER)
@@ -170,14 +172,14 @@ public class Tile implements Parcelable {
        final PackageManager packageManager = context.getPackageManager();
        if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
            if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE_URI)) {
                // If has as uri to provide dynamic summary, skip loading here. UI will later load
                // If has as uri to provide dynamic title, skip loading here. UI will later load
                // at tile binding time.
                return null;
            }
            if (mMetaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) {
                try {
                    final Resources res =
                            packageManager.getResourcesForApplication(mActivityPackage);
                            packageManager.getResourcesForApplication(mComponentPackage);
                    title = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_TITLE));
                } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
                    Log.w(TAG, "Couldn't find info", e);
@@ -186,18 +188,15 @@ public class Tile implements Parcelable {
                title = mMetaData.getString(META_DATA_PREFERENCE_TITLE);
            }
        }
        // Set the preference title to the activity's label if no
        // meta-data is found
        // Set the preference title by the component if no meta-data is found
        if (title == null) {
            final ActivityInfo activityInfo = getActivityInfo(context);
            if (activityInfo == null) {
                return null;
            }
            title = activityInfo.loadLabel(packageManager);
            title = getComponentLabel(context);
        }
        return title;
    }

    protected abstract CharSequence getComponentLabel(Context context);

    /**
     * Overrides the summary. This can happen when injected tile wants to provide dynamic summary.
     */
@@ -225,7 +224,7 @@ public class Tile implements Parcelable {
                if (mMetaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) {
                    try {
                        final Resources res =
                                packageManager.getResourcesForApplication(mActivityPackage);
                                packageManager.getResourcesForApplication(mComponentPackage);
                        summary = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_SUMMARY));
                    } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
                        Log.d(TAG, "Couldn't find info", e);
@@ -281,24 +280,24 @@ public class Tile implements Parcelable {
            return null;
        }
        ensureMetadataNotStale(context);
        final ActivityInfo activityInfo = getActivityInfo(context);
        if (activityInfo == null) {
            Log.w(TAG, "Cannot find ActivityInfo for " + getDescription());
        final ComponentInfo componentInfo = getComponentInfo(context);
        if (componentInfo == null) {
            Log.w(TAG, "Cannot find ComponentInfo for " + getDescription());
            return null;
        }

        int iconResId = mMetaData.getInt(META_DATA_PREFERENCE_ICON);
        // Set the icon
        if (iconResId == 0) {
            // Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
            // Only fallback to componentInfo.icon if metadata does not contain ICON_URI.
            // ICON_URI should be loaded in app UI when need the icon object. Handling IPC at this
            // level is too complex because we don't have a strong threading contract for this class
            if (!mMetaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
                iconResId = activityInfo.icon;
                iconResId = componentInfo.icon;
            }
        }
        if (iconResId != 0) {
            final Icon icon = Icon.createWithResource(activityInfo.packageName, iconResId);
            final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId);
            if (isIconTintable(context)) {
                final TypedArray a = context.obtainStyledAttributes(new int[]{
                        android.R.attr.colorControlNormal});
@@ -331,41 +330,26 @@ public class Tile implements Parcelable {
        final PackageManager pm = context.getApplicationContext().getPackageManager();

        try {
            final long lastUpdateTime = pm.getPackageInfo(mActivityPackage,
            final long lastUpdateTime = pm.getPackageInfo(mComponentPackage,
                    PackageManager.GET_META_DATA).lastUpdateTime;
            if (lastUpdateTime == mLastUpdateTime) {
                // All good. Do nothing
                return;
            }
            // App has been updated since we load metadata last time. Reload metadata.
            mActivityInfo = null;
            getActivityInfo(context);
            mComponentInfo = null;
            getComponentInfo(context);
            mLastUpdateTime = lastUpdateTime;
        } catch (PackageManager.NameNotFoundException e) {
            Log.d(TAG, "Can't find package, probably uninstalled.");
        }
    }

    private ActivityInfo getActivityInfo(Context context) {
        if (mActivityInfo == null) {
            final PackageManager pm = context.getApplicationContext().getPackageManager();
            final Intent intent = new Intent().setClassName(mActivityPackage, mActivityName);
            final List<ResolveInfo> infoList =
                    pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
            if (infoList != null && !infoList.isEmpty()) {
                mActivityInfo = infoList.get(0).activityInfo;
                mMetaData = mActivityInfo.metaData;
            } else {
                Log.e(TAG, "Cannot find package info for "
                        + intent.getComponent().flattenToString());
            }
        }
        return mActivityInfo;
    }
    protected abstract ComponentInfo getComponentInfo(Context context);

    public static final Creator<Tile> CREATOR = new Creator<Tile>() {
        public Tile createFromParcel(Parcel source) {
            return new Tile(source);
            return new ActivityTile(source);
        }

        public Tile[] newArray(int size) {
+81 −52
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.IContentProvider;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
@@ -127,6 +128,7 @@ public class TileUtils {
     */
    public static final String META_DATA_PREFERENCE_ICON_BACKGROUND_HINT =
            "com.android.settings.bg.hint";

    /**
     * Name of the meta-data item that should be set in the AndroidManifest.xml
     * to specify the icon background color as raw ARGB.
@@ -220,10 +222,11 @@ public class TileUtils {
    public static List<DashboardCategory> getCategories(Context context,
            Map<Pair<String, String>, Tile> cache) {
        final long startTime = System.currentTimeMillis();
        boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
                != 0;
        ArrayList<Tile> tiles = new ArrayList<>();
        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
        final boolean setup =
                Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0;
        final ArrayList<Tile> tiles = new ArrayList<>();
        final UserManager userManager = (UserManager) context.getSystemService(
                Context.USER_SERVICE);
        for (UserHandle user : userManager.getUserProfiles()) {
            // TODO: Needs much optimization, too many PM queries going on here.
            if (user.getIdentifier() == ActivityManager.getCurrentUser()) {
@@ -240,7 +243,7 @@ public class TileUtils {
            }
        }

        HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
        final HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
        for (Tile tile : tiles) {
            final String categoryKey = tile.getCategory();
            DashboardCategory category = categoryMap.get(categoryKey);
@@ -255,7 +258,7 @@ public class TileUtils {
            }
            category.addTile(tile);
        }
        ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
        final ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
        for (DashboardCategory category : categories) {
            category.sortTiles();
        }
@@ -275,33 +278,45 @@ public class TileUtils {
        if (requireSettings) {
            intent.setPackage(SETTING_PKG);
        }
        getActivityTiles(context, user, addedCache, defaultCategory, outTiles, intent);
    }

    private static void getActivityTiles(Context context,
            UserHandle user, Map<Pair<String, String>, Tile> addedCache,
            String defaultCategory, List<Tile> outTiles, Intent intent) {
        final PackageManager pm = context.getPackageManager();
        List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
        final List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
                PackageManager.GET_META_DATA, user.getIdentifier());
        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;
            final ActivityInfo activityInfo = resolved.activityInfo;
            final Bundle metaData = activityInfo.metaData;
            getTile(user, addedCache, defaultCategory, outTiles, intent, metaData, activityInfo);
        }
    }

    private static void getTile(UserHandle user, Map<Pair<String, String>, Tile> addedCache,
            String defaultCategory, List<Tile> outTiles, Intent intent, Bundle metaData,
            ComponentInfo componentInfo) {
        String categoryKey = defaultCategory;
        // Load category
        if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY))
                && categoryKey == null) {
                Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "
            Log.w(LOG_TAG, "Found " + componentInfo.name + " for intent "
                    + intent + " missing metadata "
                    + (metaData == null ? "" : EXTRA_CATEGORY_KEY));
                continue;
            return;
        } else {
            categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
        }

            Pair<String, String> key = new Pair<>(activityInfo.packageName, activityInfo.name);
        final Pair<String, String> key = new Pair<>(componentInfo.packageName, componentInfo.name);
        Tile tile = addedCache.get(key);
        if (tile == null) {
                tile = new Tile(activityInfo, categoryKey);
            tile = new ActivityTile(componentInfo, categoryKey);
            addedCache.put(key, tile);
        } else {
            tile.setMetaData(metaData);
@@ -314,6 +329,21 @@ public class TileUtils {
            outTiles.add(tile);
        }
    }

    /**
     * Returns the complete uri from the meta data key of the tile.

     * @param tile          Tile which contains meta data
     * @param metaDataKey   Key mapping to the uri in meta data
     * @return Uri associated with the key
     */
    public static Uri getCompleteUri(Tile tile, String metaDataKey) {
        final String uriString = tile.getMetaData().getString(metaDataKey);
        if (TextUtils.isEmpty(uriString)) {
            return null;
        }

        return Uri.parse(uriString);
    }

    /**
@@ -321,17 +351,17 @@ public class TileUtils {
     *
     * @param context     context
     * @param packageName package name of the target activity
     * @param uriString   URI for the content provider
     * @param uri         URI for the content provider
     * @param providerMap Maps URI authorities to providers
     * @return package name and resource id of the icon specified
     */
    public static Pair<String, Integer> getIconFromUri(Context context, String packageName,
            String uriString, Map<String, IContentProvider> providerMap) {
        Bundle bundle = getBundleFromUri(context, uriString, providerMap);
            Uri uri, Map<String, IContentProvider> providerMap) {
        final Bundle bundle = getBundleFromUri(context, uri, providerMap, null);
        if (bundle == null) {
            return null;
        }
        String iconPackageName = bundle.getString(EXTRA_PREFERENCE_ICON_PACKAGE);
        final String iconPackageName = bundle.getString(EXTRA_PREFERENCE_ICON_PACKAGE);
        if (TextUtils.isEmpty(iconPackageName)) {
            return null;
        }
@@ -342,7 +372,7 @@ public class TileUtils {
        // Icon can either come from the target package or from the Settings app.
        if (iconPackageName.equals(packageName)
                || iconPackageName.equals(context.getPackageName())) {
            return Pair.create(iconPackageName, bundle.getInt(META_DATA_PREFERENCE_ICON, 0));
            return Pair.create(iconPackageName, resId);
        }
        return null;
    }
@@ -351,34 +381,33 @@ public class TileUtils {
     * Gets text associated with the input key from the content provider.
     *
     * @param context     context
     * @param uriString   URI for the content provider
     * @param uri         URI for the content provider
     * @param providerMap Maps URI authorities to providers
     * @param key         Key mapping to the text in bundle returned by the content provider
     * @return Text associated with the key, if returned by the content provider
     */
    public static String getTextFromUri(Context context, String uriString,
    public static String getTextFromUri(Context context, Uri uri,
            Map<String, IContentProvider> providerMap, String key) {
        Bundle bundle = getBundleFromUri(context, uriString, providerMap);
        final Bundle bundle = getBundleFromUri(context, uri, providerMap, null);
        return (bundle != null) ? bundle.getString(key) : null;
    }

    private static Bundle getBundleFromUri(Context context, String uriString,
            Map<String, IContentProvider> providerMap) {
        if (TextUtils.isEmpty(uriString)) {
    private static Bundle getBundleFromUri(Context context, Uri uri,
            Map<String, IContentProvider> providerMap, Bundle bundle) {
        if (uri == null) {
            return null;
        }
        Uri uri = Uri.parse(uriString);
        String method = getMethodFromUri(uri);
        final String method = getMethodFromUri(uri);
        if (TextUtils.isEmpty(method)) {
            return null;
        }
        IContentProvider provider = getProviderFromUri(context, uri, providerMap);
        final IContentProvider provider = getProviderFromUri(context, uri, providerMap);
        if (provider == null) {
            return null;
        }
        try {
            return provider.call(context.getPackageName(), uri.getAuthority(),
                    method, uriString, null);
                    method, uri.toString(), bundle);
        } catch (RemoteException e) {
            return null;
        }
@@ -389,7 +418,7 @@ public class TileUtils {
        if (uri == null) {
            return null;
        }
        String authority = uri.getAuthority();
        final String authority = uri.getAuthority();
        if (TextUtils.isEmpty(authority)) {
            return null;
        }
@@ -400,7 +429,7 @@ public class TileUtils {
    }

    /** Returns the first path segment of the uri if it exists as the method, otherwise null. */
    static String getMethodFromUri(Uri uri) {
    private static String getMethodFromUri(Uri uri) {
        if (uri == null) {
            return null;
        }
+11 −12

File changed.

Preview size limit exceeded, changes collapsed.

+4 −3
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.graphics.drawable.ShapeDrawable;
import android.os.Bundle;

import com.android.settingslib.R;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.Tile;

@@ -84,7 +85,7 @@ public class AdaptiveIconTest {

    @Test
    public void setBackgroundColor_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() {
        final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
        final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
        mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000);
        doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
                .when(tile).getIcon(mContext);
@@ -97,7 +98,7 @@ public class AdaptiveIconTest {

    @Test
    public void setBackgroundColor_tileWithoutBackgroundColor_shouldSetDefaultBackgroundColor() {
        final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
        final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
        doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
            .when(tile).getIcon(mContext);
        final AdaptiveIcon icon = new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
@@ -110,7 +111,7 @@ public class AdaptiveIconTest {

    @Test
    public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() {
        final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
        final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
        mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
                R.color.bt_outline_color);
        doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))