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;
71         if (resId != 0) {
72             icon = loadDrawableFromPackage(context, packageName, resId);
73         } else {
74             icon = context.getDrawable(R.drawable.ic_settings_gear);
75             LOG.d("Icon not provided for " + packageName + "; using default icon");
76         }
77         if (icon == null) {
78             return null;
79         }
80         if (metaData.getBoolean(META_DATA_PREFERENCE_IS_TOP_LEVEL, /* defaultValue= */ false)) {
81             icon.mutate().setTintList(
82                     context.getColorStateList(R.color.top_level_injected_icon_default));
83             icon = new TopLevelIcon(context, icon, R.dimen.top_level_foreground_icon_inset);
84             ((TopLevelIcon) icon).setBackgroundColor(context, metaData, packageName);
85         } else if (isIconTintable(metaData)) {
86             // If the icon is tintable, tint it with the default icon color
87             icon.mutate().setTintList(context.getColorStateList(R.color.icon_color_default));
88         }
89         return icon;
90     }
91 
92     /**
93      * Returns whether or not an icon is tintable given the injected setting metadata.
94      */
isIconTintable(Bundle metaData)95     public static boolean isIconTintable(Bundle metaData) {
96         if (metaData.containsKey(META_DATA_PREFERENCE_ICON_TINTABLE)) {
97             return metaData.getBoolean(META_DATA_PREFERENCE_ICON_TINTABLE);
98         }
99         return false;
100     }
101 
102     /**
103      * Returns a drawable from an resource's package context.
104      */
loadDrawableFromPackage(Context context, String resPackage, int resId)105     public static Drawable loadDrawableFromPackage(Context context, String resPackage, int resId) {
106         try {
107             return context.createPackageContext(resPackage, /* flags= */ 0)
108                     .getDrawable(resId);
109         } catch (PackageManager.NameNotFoundException ex) {
110             LOG.e("loadDrawableFromPackage: package name not found: ", ex);
111         } catch (Resources.NotFoundException ex) {
112             LOG.w("loadDrawableFromPackage: resource not found with id " + resId, ex);
113         }
114         return null;
115     }
116 
117     /**
118      * Returns the complete uri from the meta data key of the injected setting metadata.
119      *
120      * A complete uri should contain at least one path segment and be one of the following types:
121      *      content://authority/method
122      *      content://authority/method/key
123      *
124      * If the uri from the tile is not complete, build a uri by the default method and the
125      * preference key.
126      */
getCompleteUri(Bundle metaData, String metaDataKey, String defaultMethod)127     public static Uri getCompleteUri(Bundle metaData, String metaDataKey, String defaultMethod) {
128         String uriString = metaData.getString(metaDataKey);
129         if (TextUtils.isEmpty(uriString)) {
130             return null;
131         }
132 
133         Uri uri = Uri.parse(uriString);
134         List<String> pathSegments = uri.getPathSegments();
135         if (pathSegments != null && !pathSegments.isEmpty()) {
136             return uri;
137         }
138 
139         String key = metaData.getString(META_DATA_PREFERENCE_KEYHINT);
140         if (TextUtils.isEmpty(key)) {
141             LOG.w("Please specify the meta-data " + META_DATA_PREFERENCE_KEYHINT
142                     + " in AndroidManifest.xml for " + uriString);
143             return buildUri(uri.getAuthority(), defaultMethod);
144         }
145         return buildUri(uri.getAuthority(), defaultMethod, key);
146     }
147 
buildUri(String authority, String method, String key)148     private static Uri buildUri(String authority, String method, String key) {
149         return new Uri.Builder()
150                 .scheme(ContentResolver.SCHEME_CONTENT)
151                 .authority(authority)
152                 .appendPath(method)
153                 .appendPath(key)
154                 .build();
155     }
156 
buildUri(String authority, String method)157     private static Uri buildUri(String authority, String method) {
158         return new Uri.Builder()
159                 .scheme(ContentResolver.SCHEME_CONTENT)
160                 .authority(authority)
161                 .appendPath(method)
162                 .build();
163     }
164 }
165