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