1 /* 2 * Copyright (C) 2011 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.layoutlib.bridge.bars; 18 19 import com.android.ide.common.rendering.api.LayoutLog; 20 import com.android.ide.common.rendering.api.RenderResources; 21 import com.android.ide.common.rendering.api.ResourceValue; 22 import com.android.ide.common.rendering.api.StyleResourceValue; 23 import com.android.layoutlib.bridge.Bridge; 24 import com.android.layoutlib.bridge.android.BridgeContext; 25 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; 26 import com.android.layoutlib.bridge.impl.ResourceHelper; 27 import com.android.layoutlib.bridge.resources.IconLoader; 28 import com.android.layoutlib.bridge.resources.SysUiResources; 29 import com.android.resources.Density; 30 import com.android.resources.LayoutDirection; 31 import com.android.resources.ResourceType; 32 33 import android.annotation.NonNull; 34 import android.content.res.ColorStateList; 35 import android.graphics.Bitmap; 36 import android.graphics.Bitmap_Delegate; 37 import android.graphics.drawable.BitmapDrawable; 38 import android.graphics.drawable.Drawable; 39 import android.util.TypedValue; 40 import android.view.Gravity; 41 import android.view.LayoutInflater; 42 import android.view.View; 43 import android.widget.ImageView; 44 import android.widget.LinearLayout; 45 import android.widget.TextView; 46 47 import java.io.IOException; 48 import java.io.InputStream; 49 50 import static android.os._Original_Build.VERSION_CODES.LOLLIPOP; 51 52 /** 53 * Base "bar" class for the window decor around the the edited layout. 54 * This is basically an horizontal layout that loads a given layout on creation (it is read 55 * through {@link Class#getResourceAsStream(String)}). 56 * <p> 57 * The given layout should be a merge layout so that all the children belong to this class directly. 58 * <p> 59 * It also provides a few utility methods to configure the content of the layout. 60 */ 61 abstract class CustomBar extends LinearLayout { 62 private final int mSimulatedPlatformVersion; 63 CustomBar(BridgeContext context, int orientation, String layoutName, int simulatedPlatformVersion)64 protected CustomBar(BridgeContext context, int orientation, String layoutName, 65 int simulatedPlatformVersion) { 66 super(context); 67 mSimulatedPlatformVersion = simulatedPlatformVersion; 68 setOrientation(orientation); 69 if (orientation == LinearLayout.HORIZONTAL) { 70 setGravity(Gravity.CENTER_VERTICAL); 71 } else { 72 setGravity(Gravity.CENTER_HORIZONTAL); 73 } 74 75 LayoutInflater inflater = LayoutInflater.from(mContext); 76 BridgeXmlBlockParser bridgeParser = loadXml(layoutName); 77 try { 78 inflater.inflate(bridgeParser, this, true); 79 } finally { 80 bridgeParser.ensurePopped(); 81 } 82 } 83 getStyleableTextView()84 protected abstract TextView getStyleableTextView(); 85 loadXml(String layoutName)86 protected BridgeXmlBlockParser loadXml(String layoutName) { 87 return SysUiResources.loadXml((BridgeContext) mContext, mSimulatedPlatformVersion, 88 layoutName); 89 } 90 loadIcon(ImageView imageView, String iconName, Density density)91 protected ImageView loadIcon(ImageView imageView, String iconName, Density density) { 92 return SysUiResources.loadIcon(mContext, mSimulatedPlatformVersion, imageView, iconName, 93 density, false); 94 } 95 loadIcon(int index, String iconName, Density density, boolean isRtl)96 protected ImageView loadIcon(int index, String iconName, Density density, boolean isRtl) { 97 View child = getChildAt(index); 98 if (child instanceof ImageView) { 99 ImageView imageView = (ImageView) child; 100 return SysUiResources.loadIcon(mContext, mSimulatedPlatformVersion, imageView, iconName, 101 density, isRtl); 102 } 103 104 return null; 105 } 106 loadIcon(ImageView imageView, String iconName, Density density, boolean isRtl)107 protected ImageView loadIcon(ImageView imageView, String iconName, Density density, 108 boolean isRtl) { 109 LayoutDirection dir = isRtl ? LayoutDirection.RTL : null; 110 IconLoader iconLoader = new IconLoader(iconName, density, mSimulatedPlatformVersion, dir); 111 InputStream stream = iconLoader.getIcon(); 112 113 if (stream != null) { 114 density = iconLoader.getDensity(); 115 String path = iconLoader.getPath(); 116 // look for a cached bitmap 117 Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/); 118 if (bitmap == null) { 119 try { 120 bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density); 121 Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/); 122 } catch (IOException e) { 123 return imageView; 124 } 125 } 126 127 if (bitmap != null) { 128 BitmapDrawable drawable = new BitmapDrawable(getContext().getResources(), bitmap); 129 imageView.setImageDrawable(drawable); 130 } 131 } 132 133 return imageView; 134 } 135 setText(int index, String string)136 protected TextView setText(int index, String string) { 137 View child = getChildAt(index); 138 if (child instanceof TextView) { 139 TextView textView = (TextView) child; 140 textView.setText(string); 141 return textView; 142 } 143 144 return null; 145 } 146 setStyle(String themeEntryName)147 protected void setStyle(String themeEntryName) { 148 BridgeContext bridgeContext = getContext(); 149 RenderResources res = bridgeContext.getRenderResources(); 150 151 ResourceValue value = 152 res.findItemInTheme(BridgeContext.createFrameworkAttrReference(themeEntryName)); 153 value = res.resolveResValue(value); 154 155 if (!(value instanceof StyleResourceValue)) { 156 return; 157 } 158 159 StyleResourceValue style = (StyleResourceValue) value; 160 161 // get the background 162 ResourceValue backgroundValue = res.findItemInStyle(style, 163 BridgeContext.createFrameworkAttrReference("background")); 164 backgroundValue = res.resolveResValue(backgroundValue); 165 if (backgroundValue != null) { 166 Drawable d = ResourceHelper.getDrawable(backgroundValue, bridgeContext); 167 if (d != null) { 168 setBackground(d); 169 } 170 } 171 172 TextView textView = getStyleableTextView(); 173 if (textView != null) { 174 // get the text style 175 ResourceValue textStyleValue = res.findItemInStyle(style, 176 BridgeContext.createFrameworkAttrReference("titleTextStyle")); 177 textStyleValue = res.resolveResValue(textStyleValue); 178 if (textStyleValue instanceof StyleResourceValue) { 179 StyleResourceValue textStyle = (StyleResourceValue) textStyleValue; 180 181 ResourceValue textSize = res.findItemInStyle(textStyle, 182 BridgeContext.createFrameworkAttrReference("textSize")); 183 textSize = res.resolveResValue(textSize); 184 185 if (textSize != null) { 186 TypedValue out = new TypedValue(); 187 if (ResourceHelper.parseFloatAttribute("textSize", textSize.getValue(), out, 188 true /*requireUnit*/)) { 189 textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 190 out.getDimension(bridgeContext.getResources().getDisplayMetrics())); 191 } 192 } 193 194 ResourceValue textColor = res.findItemInStyle(textStyle, 195 BridgeContext.createFrameworkAttrReference("textColor")); 196 textColor = res.resolveResValue(textColor); 197 if (textColor != null) { 198 ColorStateList stateList = 199 ResourceHelper.getColorStateList(textColor, bridgeContext, null); 200 if (stateList != null) { 201 textView.setTextColor(stateList); 202 } 203 } 204 } 205 } 206 } 207 208 @Override getContext()209 public BridgeContext getContext() { 210 return (BridgeContext) mContext; 211 } 212 213 /** 214 * Find the background color for this bar from the theme attributes. Only relevant to StatusBar 215 * and NavigationBar. 216 * <p/> 217 * Returns 0 if not found. 218 * 219 * @param colorAttrName the attribute name for the background color 220 * @param translucentAttrName the attribute name for the translucency property of the bar. 221 * 222 * @throws NumberFormatException if color resolved to an invalid string. 223 */ getBarColor(@onNull String colorAttrName, @NonNull String translucentAttrName)224 protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) { 225 if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) { 226 return 0; 227 } 228 RenderResources renderResources = getContext().getRenderResources(); 229 // First check if the bar is translucent. 230 boolean translucent = ResourceHelper.getBooleanThemeFrameworkAttrValue(renderResources, 231 translucentAttrName, false); 232 if (translucent) { 233 // Keep in sync with R.color.system_bar_background_semi_transparent from system ui. 234 return 0x66000000; // 40% black. 235 } 236 boolean transparent = ResourceHelper.getBooleanThemeFrameworkAttrValue(renderResources, 237 "windowDrawsSystemBarBackgrounds", false); 238 if (transparent) { 239 return getColor(renderResources, colorAttrName); 240 } 241 return 0; 242 } 243 getColor(RenderResources renderResources, String attr)244 private static int getColor(RenderResources renderResources, String attr) { 245 // From ?attr/foo to @color/bar. This is most likely an StyleItemResourceValue. 246 ResourceValue resource = 247 renderResources.findItemInTheme(BridgeContext.createFrameworkAttrReference(attr)); 248 // Form @color/bar to the #AARRGGBB 249 resource = renderResources.resolveResValue(resource); 250 if (resource != null) { 251 ResourceType type = resource.getResourceType(); 252 if (type == null || type == ResourceType.COLOR) { 253 // if no type is specified, the value may have been specified directly in the style 254 // file, rather than referencing a color resource value. 255 try { 256 return ResourceHelper.getColor(resource.getValue()); 257 } catch (NumberFormatException e) { 258 // Conversion failed. 259 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, 260 "Theme attribute @android:" + attr + 261 " does not reference a color, instead is '" + 262 resource.getValue() + "'.", null, resource); 263 } 264 } 265 } 266 return 0; 267 } 268 } 269