CustomBar.java revision 1076be31f1c14f39295cc2ce7a747ee9ad96ee73
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
17package com.android.layoutlib.bridge.bars;
18
19import com.android.ide.common.rendering.api.RenderResources;
20import com.android.ide.common.rendering.api.ResourceValue;
21import com.android.ide.common.rendering.api.StyleResourceValue;
22import com.android.layoutlib.bridge.Bridge;
23import com.android.layoutlib.bridge.android.BridgeContext;
24import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
25import com.android.layoutlib.bridge.impl.ParserFactory;
26import com.android.layoutlib.bridge.impl.ResourceHelper;
27import com.android.resources.Density;
28import com.android.resources.LayoutDirection;
29
30import org.xmlpull.v1.XmlPullParser;
31import org.xmlpull.v1.XmlPullParserException;
32
33import android.content.Context;
34import android.content.res.ColorStateList;
35import android.graphics.Bitmap;
36import android.graphics.Bitmap_Delegate;
37import android.graphics.drawable.BitmapDrawable;
38import android.graphics.drawable.Drawable;
39import android.util.TypedValue;
40import android.view.Gravity;
41import android.view.LayoutInflater;
42import android.view.View;
43import android.widget.ImageView;
44import android.widget.LinearLayout;
45import android.widget.TextView;
46
47import java.io.IOException;
48import java.io.InputStream;
49
50import static com.android.layoutlib.bridge.bars.Config.DEFAULT_RESOURCE_DIR;
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 *
57 * The given layout should be a merge layout so that all the children belong to this class directly.
58 *
59 * It also provides a few utility methods to configure the content of the layout.
60 */
61abstract class CustomBar extends LinearLayout {
62
63    // An upper-bound on the length of the path to the directory to find the icon in.
64    // This assumes that resource directory name for different api levels have same length.
65    private static final int ICON_PATH_LENGTH = DEFAULT_RESOURCE_DIR.length()
66            + LayoutDirection.RTL.getResourceValue().length() + 9;  // 9 = "-xxxhdpi/".length
67
68
69    private final int mSimulatedPlatformVersion;
70
71    protected abstract TextView getStyleableTextView();
72
73    protected CustomBar(Context context, int orientation, String layoutPath,
74            String name, int simulatedPlatformVersion) throws XmlPullParserException {
75        super(context);
76        mSimulatedPlatformVersion = simulatedPlatformVersion;
77        setOrientation(orientation);
78        if (orientation == LinearLayout.HORIZONTAL) {
79            setGravity(Gravity.CENTER_VERTICAL);
80        } else {
81            setGravity(Gravity.CENTER_HORIZONTAL);
82        }
83
84        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
85                Context.LAYOUT_INFLATER_SERVICE);
86
87        XmlPullParser parser = ParserFactory.create(getClass().getResourceAsStream(layoutPath),
88                name);
89
90        BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
91                parser, (BridgeContext) context, false /*platformFile*/);
92
93        try {
94            inflater.inflate(bridgeParser, this, true);
95        } finally {
96            bridgeParser.ensurePopped();
97        }
98    }
99
100    private InputStream getIcon(String iconName, Density[] densityInOut, LayoutDirection direction,
101            StringBuilder[] pathOut, boolean tryOtherDensities) {
102        pathOut[0] = new StringBuilder(ICON_PATH_LENGTH + iconName.length());
103
104        if (Config.usesCustomResourceDir(mSimulatedPlatformVersion)) {
105            // current density.
106            Density density = densityInOut[0];
107            InputStream stream = getIcon(iconName, Config.getResourceDir(mSimulatedPlatformVersion),
108                    densityInOut, direction, pathOut, tryOtherDensities);
109            if (stream != null) {
110                return stream;
111            }
112            // reset the density.
113            densityInOut[0] = density;
114        }
115        return getIcon(iconName, DEFAULT_RESOURCE_DIR, densityInOut, direction, pathOut,
116                tryOtherDensities);
117
118    }
119
120    private InputStream getIcon(String iconName, String dir, Density[] densityInOut,
121            LayoutDirection direction, StringBuilder[] pathOut, boolean tryOtherDensities) {
122        // current density
123        Density density = densityInOut[0];
124
125        pathOut[0].setLength(0);
126
127        // bitmap url relative to this class
128        if (direction == LayoutDirection.RTL) {
129            pathOut[0].append(dir)
130                    .append(direction.getResourceValue())
131                    .append('-')
132                    .append(density.getResourceValue())
133                    .append('/')
134                    .append(iconName);
135        } else {
136            // Since we do not have any ldltr resource, skip the check.
137            pathOut[0].append(dir)
138                    .append(density.getResourceValue())
139                    .append('/')
140                    .append(iconName);
141        }
142
143        InputStream stream = getClass().getResourceAsStream(pathOut[0].toString());
144        if (stream == null && tryOtherDensities) {
145            for (Density d : Density.values()) {
146                if (d != density) {
147                    densityInOut[0] = d;
148                    stream = getIcon(iconName, dir, densityInOut, direction, pathOut,
149                            false /*tryOtherDensities*/);
150                    if (stream != null) {
151                        return stream;
152                    }
153                }
154            }
155            // couldn't find resource with direction qualifier, try without.
156            if (direction == LayoutDirection.RTL) {
157                densityInOut[0] = density;
158                stream = getIcon(iconName, dir, densityInOut, null, pathOut,
159                        true /*tryOtherDensities*/);
160            }
161        }
162
163        return stream;
164    }
165
166    protected void loadIcon(int index, String iconName, Density density) {
167        loadIcon(index, iconName, density, false);
168    }
169
170    protected void loadIcon(int index, String iconName, Density density, boolean isRtl) {
171        View child = getChildAt(index);
172        if (child instanceof ImageView) {
173            ImageView imageView = (ImageView) child;
174
175            StringBuilder[] pathOut = new StringBuilder[1];
176            Density[] densityInOut = new Density[]{density};
177            LayoutDirection dir = isRtl ? LayoutDirection.RTL : null;
178            InputStream stream = getIcon(iconName, densityInOut, dir, pathOut,
179                    true /*tryOtherDensities*/);
180            density = densityInOut[0];
181            String path = pathOut[0].toString();
182
183            if (stream != null) {
184                // look for a cached bitmap
185                Bitmap bitmap = Bridge.getCachedBitmap(path, true /*isFramework*/);
186                if (bitmap == null) {
187                    try {
188                        bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
189                        Bridge.setCachedBitmap(path, bitmap, true /*isFramework*/);
190                    } catch (IOException e) {
191                        return;
192                    }
193                }
194
195                if (bitmap != null) {
196                    BitmapDrawable drawable = new BitmapDrawable(getContext().getResources(),
197                            bitmap);
198                    imageView.setImageDrawable(drawable);
199                }
200            }
201        }
202    }
203
204    protected TextView setText(int index, String stringReference) {
205        View child = getChildAt(index);
206        if (child instanceof TextView) {
207            TextView textView = (TextView) child;
208            setText(textView, stringReference);
209            return textView;
210        }
211
212        return null;
213    }
214
215    private void setText(TextView textView, String stringReference) {
216        ResourceValue value = getResourceValue(stringReference);
217        if (value != null) {
218            textView.setText(value.getValue());
219        } else {
220            textView.setText(stringReference);
221        }
222    }
223
224    protected void setStyle(String themeEntryName) {
225
226        BridgeContext bridgeContext = (BridgeContext) mContext;
227        RenderResources res = bridgeContext.getRenderResources();
228
229        ResourceValue value = res.findItemInTheme(themeEntryName, true /*isFrameworkAttr*/);
230        value = res.resolveResValue(value);
231
232        if (!(value instanceof StyleResourceValue)) {
233            return;
234        }
235
236        StyleResourceValue style = (StyleResourceValue) value;
237
238        // get the background
239        ResourceValue backgroundValue = res.findItemInStyle(style, "background",
240                true /*isFrameworkAttr*/);
241        backgroundValue = res.resolveResValue(backgroundValue);
242        if (backgroundValue != null) {
243            Drawable d = ResourceHelper.getDrawable(backgroundValue, bridgeContext);
244            if (d != null) {
245                setBackground(d);
246            }
247        }
248
249        TextView textView = getStyleableTextView();
250        if (textView != null) {
251            // get the text style
252            ResourceValue textStyleValue = res.findItemInStyle(style, "titleTextStyle",
253                    true /*isFrameworkAttr*/);
254            textStyleValue = res.resolveResValue(textStyleValue);
255            if (textStyleValue instanceof StyleResourceValue) {
256                StyleResourceValue textStyle = (StyleResourceValue) textStyleValue;
257
258                ResourceValue textSize = res.findItemInStyle(textStyle, "textSize",
259                        true /*isFrameworkAttr*/);
260                textSize = res.resolveResValue(textSize);
261
262                if (textSize != null) {
263                    TypedValue out = new TypedValue();
264                    if (ResourceHelper.parseFloatAttribute("textSize", textSize.getValue(), out,
265                            true /*requireUnit*/)) {
266                        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
267                                out.getDimension(bridgeContext.getResources().getDisplayMetrics()));
268                    }
269                }
270
271
272                ResourceValue textColor = res.findItemInStyle(textStyle, "textColor",
273                        true /*isFrameworkAttr*/);
274                textColor = res.resolveResValue(textColor);
275                if (textColor != null) {
276                    ColorStateList stateList = ResourceHelper.getColorStateList(
277                            textColor, bridgeContext);
278                    if (stateList != null) {
279                        textView.setTextColor(stateList);
280                    }
281                }
282            }
283        }
284    }
285
286    private ResourceValue getResourceValue(String reference) {
287        BridgeContext bridgeContext = (BridgeContext) mContext;
288        RenderResources res = bridgeContext.getRenderResources();
289
290        // find the resource
291        ResourceValue value = res.findResValue(reference, false /*isFramework*/);
292
293        // resolve it if needed
294        return res.resolveResValue(value);
295    }
296}
297