Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 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 android.view; |
| 18 | |
| 19 | import com.android.tools.layoutlib.annotations.LayoutlibDelegate; |
| 20 | |
| 21 | import org.xmlpull.v1.XmlPullParser; |
| 22 | import org.xmlpull.v1.XmlPullParserException; |
| 23 | |
| 24 | import android.content.res.TypedArray; |
| 25 | import android.content.res.XmlResourceParser; |
| 26 | import android.util.AttributeSet; |
| 27 | import android.util.Xml; |
| 28 | |
| 29 | import java.io.IOException; |
| 30 | |
| 31 | /** |
| 32 | * Delegate used to provide new implementation of a select few methods of {@link LayoutInflater} |
| 33 | * |
| 34 | * Through the layoutlib_create tool, the original methods of LayoutInflater have been replaced |
| 35 | * by calls to methods of the same name in this delegate class. |
| 36 | * |
| 37 | */ |
| 38 | public class LayoutInflater_Delegate { |
| 39 | |
| 40 | private static final String TAG_MERGE = "merge"; |
| 41 | |
| 42 | public static boolean sIsInInclude = false; |
| 43 | |
| 44 | /** |
| 45 | * Recursive method used to descend down the xml hierarchy and instantiate |
| 46 | * views, instantiate their children, and then call onFinishInflate(). |
| 47 | * |
| 48 | * This implementation just records the merge status before calling the default implementation. |
| 49 | */ |
| 50 | @LayoutlibDelegate |
Alan Viverette | ebcef6b | 2014-01-08 15:15:01 -0800 | [diff] [blame] | 51 | /* package */ static void rInflate(LayoutInflater thisInflater, XmlPullParser parser, |
| 52 | View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext) |
| 53 | throws XmlPullParserException, IOException { |
Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 54 | |
| 55 | if (finishInflate == false) { |
| 56 | // this is a merge rInflate! |
| 57 | if (thisInflater instanceof BridgeInflater) { |
| 58 | ((BridgeInflater) thisInflater).setIsInMerge(true); |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | // ---- START DEFAULT IMPLEMENTATION. |
| 63 | |
Alan Viverette | ebcef6b | 2014-01-08 15:15:01 -0800 | [diff] [blame] | 64 | thisInflater.rInflate_Original(parser, parent, attrs, finishInflate, inheritContext); |
Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 65 | |
| 66 | // ---- END DEFAULT IMPLEMENTATION. |
| 67 | |
| 68 | if (finishInflate == false) { |
| 69 | // this is a merge rInflate! |
| 70 | if (thisInflater instanceof BridgeInflater) { |
| 71 | ((BridgeInflater) thisInflater).setIsInMerge(false); |
| 72 | } |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | @LayoutlibDelegate |
Alan Viverette | ebcef6b | 2014-01-08 15:15:01 -0800 | [diff] [blame] | 77 | public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, View parent, |
| 78 | AttributeSet attrs, boolean inheritContext) throws XmlPullParserException, IOException { |
Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 79 | |
| 80 | int type; |
| 81 | |
| 82 | if (parent instanceof ViewGroup) { |
| 83 | final int layout = attrs.getAttributeResourceValue(null, "layout", 0); |
| 84 | if (layout == 0) { |
| 85 | final String value = attrs.getAttributeValue(null, "layout"); |
| 86 | if (value == null) { |
| 87 | throw new InflateException("You must specifiy a layout in the" |
| 88 | + " include tag: <include layout=\"@layout/layoutID\" />"); |
| 89 | } else { |
| 90 | throw new InflateException("You must specifiy a valid layout " |
| 91 | + "reference. The layout ID " + value + " is not valid."); |
| 92 | } |
| 93 | } else { |
| 94 | final XmlResourceParser childParser = |
| 95 | thisInflater.getContext().getResources().getLayout(layout); |
| 96 | |
| 97 | try { |
| 98 | final AttributeSet childAttrs = Xml.asAttributeSet(childParser); |
| 99 | |
| 100 | while ((type = childParser.next()) != XmlPullParser.START_TAG && |
| 101 | type != XmlPullParser.END_DOCUMENT) { |
| 102 | // Empty. |
| 103 | } |
| 104 | |
| 105 | if (type != XmlPullParser.START_TAG) { |
| 106 | throw new InflateException(childParser.getPositionDescription() + |
| 107 | ": No start tag found!"); |
| 108 | } |
| 109 | |
| 110 | final String childName = childParser.getName(); |
| 111 | |
| 112 | if (TAG_MERGE.equals(childName)) { |
| 113 | // Inflate all children. |
Alan Viverette | ebcef6b | 2014-01-08 15:15:01 -0800 | [diff] [blame] | 114 | thisInflater.rInflate(childParser, parent, childAttrs, false, |
| 115 | inheritContext); |
Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 116 | } else { |
Alan Viverette | ebcef6b | 2014-01-08 15:15:01 -0800 | [diff] [blame] | 117 | final View view = thisInflater.createViewFromTag(parent, childName, |
| 118 | childAttrs, inheritContext); |
Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 119 | final ViewGroup group = (ViewGroup) parent; |
| 120 | |
| 121 | // We try to load the layout params set in the <include /> tag. If |
| 122 | // they don't exist, we will rely on the layout params set in the |
| 123 | // included XML file. |
| 124 | // During a layoutparams generation, a runtime exception is thrown |
| 125 | // if either layout_width or layout_height is missing. We catch |
| 126 | // this exception and set localParams accordingly: true means we |
| 127 | // successfully loaded layout params from the <include /> tag, |
| 128 | // false means we need to rely on the included layout params. |
| 129 | ViewGroup.LayoutParams params = null; |
| 130 | try { |
| 131 | // ---- START CHANGES |
| 132 | sIsInInclude = true; |
| 133 | // ---- END CHANGES |
| 134 | |
| 135 | params = group.generateLayoutParams(attrs); |
| 136 | |
| 137 | } catch (RuntimeException e) { |
| 138 | // ---- START CHANGES |
| 139 | sIsInInclude = false; |
| 140 | // ---- END CHANGES |
| 141 | |
| 142 | params = group.generateLayoutParams(childAttrs); |
| 143 | } finally { |
| 144 | // ---- START CHANGES |
| 145 | sIsInInclude = false; |
| 146 | // ---- END CHANGES |
| 147 | |
| 148 | if (params != null) { |
| 149 | view.setLayoutParams(params); |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | // Inflate all children. |
Alan Viverette | ebcef6b | 2014-01-08 15:15:01 -0800 | [diff] [blame] | 154 | thisInflater.rInflate(childParser, view, childAttrs, true, true); |
Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 155 | |
| 156 | // Attempt to override the included layout's android:id with the |
| 157 | // one set on the <include /> tag itself. |
| 158 | TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs, |
| 159 | com.android.internal.R.styleable.View, 0, 0); |
| 160 | int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID); |
| 161 | // While we're at it, let's try to override android:visibility. |
| 162 | int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1); |
| 163 | a.recycle(); |
| 164 | |
| 165 | if (id != View.NO_ID) { |
| 166 | view.setId(id); |
| 167 | } |
| 168 | |
| 169 | switch (visibility) { |
| 170 | case 0: |
| 171 | view.setVisibility(View.VISIBLE); |
| 172 | break; |
| 173 | case 1: |
| 174 | view.setVisibility(View.INVISIBLE); |
| 175 | break; |
| 176 | case 2: |
| 177 | view.setVisibility(View.GONE); |
| 178 | break; |
| 179 | } |
| 180 | |
| 181 | group.addView(view); |
| 182 | } |
| 183 | } finally { |
| 184 | childParser.close(); |
| 185 | } |
| 186 | } |
| 187 | } else { |
| 188 | throw new InflateException("<include /> can only be used inside of a ViewGroup"); |
| 189 | } |
| 190 | |
| 191 | final int currentDepth = parser.getDepth(); |
| 192 | while (((type = parser.next()) != XmlPullParser.END_TAG || |
| 193 | parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { |
| 194 | // Empty |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | |
| 199 | } |