1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.settings.common;
18 
19 import static com.android.car.settings.common.ExtraSettingsLoader.META_DATA_PREFERENCE_IS_TOP_LEVEL;
20 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
21 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE;
22 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
23 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
24 
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.content.pm.PackageManager;
28 import android.content.res.Resources;
29 import android.graphics.drawable.Drawable;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.text.TextUtils;
33 
34 import androidx.annotation.DrawableRes;
35 import androidx.annotation.Nullable;
36 
37 import com.android.car.settings.R;
38 
39 import java.util.List;
40 
41 /** Contains utility functions for injected settings. */
42 public class ExtraSettingsUtil {
43     private static final Logger LOG = new Logger(ExtraSettingsUtil.class);
44 
45     /**
46      * See {@link #createIcon(Context, Bundle, String, int)
47      * <p>
48      * Will return null if the provided metadata does not specify
49      * {@link com.android.settingslib.drawer.TileUtils#META_DATA_PREFERENCE_ICON} but contains
50      * {@link com.android.settingslib.drawer.TileUtils#META_DATA_PREFERENCE_ICON_URI}.
51      */
52     @Nullable
createIcon(Context context, Bundle metaData, String packageName)53     public static Drawable createIcon(Context context, Bundle metaData, String packageName) {
54         if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
55             int iconRes = metaData.getInt(META_DATA_PREFERENCE_ICON);
56             return createIcon(context, metaData, packageName, iconRes);
57         } else if (metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
58             return null;
59         }
60         return createIcon(context, metaData, packageName, /* resId= */ 0);
61     }
62 
63     /**
64      * Returns an icon for an injected preference with the necessary styling, or null if the
65      * provided {@code resId} could not be loaded.
66      */
67     @Nullable
createIcon(Context context, Bundle metaData, String packageName, @DrawableRes int resId)68     public static Drawable createIcon(Context context, Bundle metaData, String packageName,
69             @DrawableRes int resId) {
70         Drawable icon = null;
71         if (resId != 0) {
72             icon = loadDrawableFromPackage(context, packageName, resId);
73         }
74         if (icon == null) {
75             return null;
76         }
77         if (metaData.getBoolean(META_DATA_PREFERENCE_IS_TOP_LEVEL, /* defaultValue= */ false)) {
78             icon.mutate().setTintList(
79                     context.getColorStateList(R.color.top_level_injected_icon_default));
80             icon = new TopLevelIcon(context, icon, R.dimen.top_level_foreground_icon_inset);
81             ((TopLevelIcon) icon).setBackgroundColor(context, metaData, packageName);
82         } else if (isIconTintable(metaData)) {
83             // If the icon is tintable, tint it with the default icon color
84             icon.mutate().setTintList(context.getColorStateList(R.color.icon_color_default));
85         }
86         return icon;
87     }
88 
89     /**
90      * Returns whether or not an icon is tintable given the injected setting metadata.
91      */
isIconTintable(Bundle metaData)92     public static boolean isIconTintable(Bundle metaData) {
93         if (metaData.containsKey(META_DATA_PREFERENCE_ICON_TINTABLE)) {
94             return metaData.getBoolean(META_DATA_PREFERENCE_ICON_TINTABLE);
95         }
96         return false;
97     }
98 
99     /**
100      * Returns a drawable from an resource's package context.
101      */
loadDrawableFromPackage(Context context, String resPackage, int resId)102     public static Drawable loadDrawableFromPackage(Context context, String resPackage, int resId) {
103         try {
104             return context.createPackageContext(resPackage, /* flags= */ 0)
105                     .getDrawable(resId);
106         } catch (PackageManager.NameNotFoundException ex) {
107             LOG.e("loadDrawableFromPackage: package name not found: ", ex);
108         } catch (Resources.NotFoundException ex) {
109             LOG.w("loadDrawableFromPackage: resource not found with id " + resId, ex);
110         }
111         return null;
112     }
113 
114     /**
115      * Returns the complete uri from the meta data key of the injected setting metadata.
116      *
117      * A complete uri should contain at least one path segment and be one of the following types:
118      *      content://authority/method
119      *      content://authority/method/key
120      *
121      * If the uri from the tile is not complete, build a uri by the default method and the
122      * preference key.
123      */
getCompleteUri(Bundle metaData, String metaDataKey, String defaultMethod)124     public static Uri getCompleteUri(Bundle metaData, String metaDataKey, String defaultMethod) {
125         String uriString = metaData.getString(metaDataKey);
126         if (TextUtils.isEmpty(uriString)) {
127             return null;
128         }
129 
130         Uri uri = Uri.parse(uriString);
131         List<String> pathSegments = uri.getPathSegments();
132         if (pathSegments != null && !pathSegments.isEmpty()) {
133             return uri;
134         }
135 
136         String key = metaData.getString(META_DATA_PREFERENCE_KEYHINT);
137         if (TextUtils.isEmpty(key)) {
138             LOG.w("Please specify the meta-data " + META_DATA_PREFERENCE_KEYHINT
139                     + " in AndroidManifest.xml for " + uriString);
140             return buildUri(uri.getAuthority(), defaultMethod);
141         }
142         return buildUri(uri.getAuthority(), defaultMethod, key);
143     }
144 
buildUri(String authority, String method, String key)145     private static Uri buildUri(String authority, String method, String key) {
146         return new Uri.Builder()
147                 .scheme(ContentResolver.SCHEME_CONTENT)
148                 .authority(authority)
149                 .appendPath(method)
150                 .appendPath(key)
151                 .build();
152     }
153 
buildUri(String authority, String method)154     private static Uri buildUri(String authority, String method) {
155         return new Uri.Builder()
156                 .scheme(ContentResolver.SCHEME_CONTENT)
157                 .authority(authority)
158                 .appendPath(method)
159                 .build();
160     }
161 }
162