1 /*
2  * Copyright (C) 2021 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.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
20 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
21 
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.content.res.ColorStateList;
25 import android.graphics.drawable.Drawable;
26 import android.graphics.drawable.LayerDrawable;
27 import android.os.Bundle;
28 
29 import androidx.annotation.ColorInt;
30 import androidx.annotation.VisibleForTesting;
31 
32 import com.android.car.settings.R;
33 import com.android.internal.graphics.ColorUtils;
34 import com.android.settingslib.utils.ColorUtil;
35 
36 /**
37  * Top-level icon that can set background color.
38  *
39  * Adapted from {@link com.android.settingslib.widget.AdaptiveIcon}
40  */
41 public class TopLevelIcon extends LayerDrawable {
42     private static final Logger LOG = new Logger("TopLevelIcon");
43 
44     private static final int BACKGROUND_INDEX = 0;
45     private static final int FOREGROUND_INDEX = 1;
46 
47     private Context mContext;
48     private AdaptiveConstantState mAdaptiveConstantState;
49     private boolean mAlwaysDefaultColor;
50     @VisibleForTesting
51     ColorStateList mBackgroundColorStateList;
52 
TopLevelIcon(Context context, Drawable foreground)53     public TopLevelIcon(Context context, Drawable foreground) {
54         this(context, foreground, R.dimen.top_level_foreground_icon_inset);
55     }
56 
TopLevelIcon(Context context, Drawable foreground, int insetResId)57     public TopLevelIcon(Context context, Drawable foreground, int insetResId) {
58         super(new Drawable[]{
59                 new TopLevelIconShapeDrawable(context.getResources()),
60                 foreground
61         });
62         mContext = context;
63         int insetPx = context.getResources().getDimensionPixelSize(insetResId);
64         setLayerInset(FOREGROUND_INDEX , insetPx, insetPx, insetPx, insetPx);
65         mAdaptiveConstantState = new AdaptiveConstantState(context, foreground);
66         mAlwaysDefaultColor = context.getResources().getBoolean(
67                 R.bool.config_top_level_injection_background_always_use_default);
68     }
69 
70     /**
71      *  Sets background color based on injected metaData.
72      */
setBackgroundColor(Context context, Bundle metaData, String packageName)73     public void setBackgroundColor(Context context, Bundle metaData, String packageName) {
74         try {
75             if (metaData != null && packageName != null && !mAlwaysDefaultColor) {
76                 // Load from bg.argb first
77                 int bgColor = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB,
78                         /* defaultValue= */ 0);
79                 // If not found, load from bg.hint
80                 if (bgColor == 0) {
81                     int colorRes = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
82                             /* defaultValue= */ 0);
83                     if (colorRes != 0) {
84                         bgColor = context.getPackageManager()
85                                 .getResourcesForApplication(packageName)
86                                 .getColor(colorRes, /* theme= */ null);
87                     }
88                 }
89                 // If a color was found, use it.
90                 if (bgColor != 0) {
91                     setBackgroundColor(bgColor);
92                     return;
93                 }
94             }
95         } catch (PackageManager.NameNotFoundException e) {
96             LOG.e("Failed to set background color for " + packageName);
97         }
98         setBackgroundColor(context.getColor(R.color.top_level_injected_default_background));
99     }
100 
101     /**
102      * Sets background color by {@code color}.
103      */
setBackgroundColor(@olorInt int color)104     public void setBackgroundColor(@ColorInt int color) {
105         mBackgroundColorStateList = createBackgroundColorStateList(color);
106         getDrawable(BACKGROUND_INDEX).setTintList(mBackgroundColorStateList);
107         mAdaptiveConstantState.mColor = color;
108     }
109 
110     @Override
getConstantState()111     public ConstantState getConstantState() {
112         return mAdaptiveConstantState;
113     }
114 
createBackgroundColorStateList(@olorInt int color)115     private ColorStateList createBackgroundColorStateList(@ColorInt int color) {
116         return new ColorStateList(
117                 new int[][]{
118                         new int[]{-android.R.attr.state_enabled}, // disabled state
119                         new int[]{R.attr.state_ux_restricted}, // ux restricted state
120                         new int[]{} // default state
121                 },
122                 new int[]{
123                         getDisabledAlphaColor(color),
124                         getDisabledAlphaColor(color),
125                         color
126                 }
127         );
128     }
129 
130     @ColorInt
getDisabledAlphaColor(@olorInt int color)131     private int getDisabledAlphaColor(@ColorInt int color) {
132         return ColorUtils.setAlphaComponent(color,
133                 (int) (ColorUtil.getDisabledAlpha(mContext) * 255));
134     }
135 
136     @VisibleForTesting
137     static class AdaptiveConstantState extends ConstantState {
138         Context mContext;
139         Drawable mDrawable;
140         int mColor;
141 
AdaptiveConstantState(Context context, Drawable drawable)142         AdaptiveConstantState(Context context, Drawable drawable) {
143             this.mContext = context;
144             this.mDrawable = drawable;
145         }
146 
147         @Override
newDrawable()148         public Drawable newDrawable() {
149             TopLevelIcon icon = new TopLevelIcon(mContext, mDrawable);
150             icon.setBackgroundColor(mColor);
151 
152             return icon;
153         }
154 
155         @Override
getChangingConfigurations()156         public int getChangingConfigurations() {
157             return 0;
158         }
159     }
160 }
161