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