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.impl.binding;
18
19import com.android.ide.common.rendering.api.AdapterBinding;
20import com.android.ide.common.rendering.api.DataBindingItem;
21import com.android.ide.common.rendering.api.IProjectCallback;
22import com.android.ide.common.rendering.api.LayoutLog;
23import com.android.ide.common.rendering.api.ResourceReference;
24import com.android.ide.common.rendering.api.IProjectCallback.ViewAttribute;
25import com.android.layoutlib.bridge.Bridge;
26import com.android.layoutlib.bridge.android.BridgeContext;
27import com.android.layoutlib.bridge.impl.RenderAction;
28import com.android.util.Pair;
29
30import android.database.DataSetObserver;
31import android.view.View;
32import android.view.ViewGroup;
33import android.widget.AdapterView;
34import android.widget.Checkable;
35import android.widget.ImageView;
36import android.widget.TextView;
37
38import java.util.ArrayList;
39import java.util.Collections;
40import java.util.List;
41
42/**
43 * Base adapter to do fake data binding in {@link AdapterView} objects.
44 */
45public class BaseAdapter {
46
47    /**
48     * This is the items provided by the adapter. They are dynamically generated.
49     */
50    protected final static class AdapterItem {
51        private final DataBindingItem mItem;
52        private final int mType;
53        private final int mFullPosition;
54        private final int mPositionPerType;
55        private List<AdapterItem> mChildren;
56
57        protected AdapterItem(DataBindingItem item, int type, int fullPosition,
58                int positionPerType) {
59            mItem = item;
60            mType = type;
61            mFullPosition = fullPosition;
62            mPositionPerType = positionPerType;
63        }
64
65        void addChild(AdapterItem child) {
66            if (mChildren == null) {
67                mChildren = new ArrayList<AdapterItem>();
68            }
69
70            mChildren.add(child);
71        }
72
73        List<AdapterItem> getChildren() {
74            if (mChildren != null) {
75                return mChildren;
76            }
77
78            return Collections.emptyList();
79        }
80
81        int getType() {
82            return mType;
83        }
84
85        int getFullPosition() {
86            return mFullPosition;
87        }
88
89        int getPositionPerType() {
90            return mPositionPerType;
91        }
92
93        DataBindingItem getDataBindingItem() {
94            return mItem;
95        }
96    }
97
98    private final AdapterBinding mBinding;
99    private final IProjectCallback mCallback;
100    private final ResourceReference mAdapterRef;
101    private boolean mSkipCallbackParser = false;
102
103    protected final List<AdapterItem> mItems = new ArrayList<AdapterItem>();
104
105    protected BaseAdapter(ResourceReference adapterRef, AdapterBinding binding,
106            IProjectCallback callback) {
107        mAdapterRef = adapterRef;
108        mBinding = binding;
109        mCallback = callback;
110    }
111
112    // ------- Some Adapter method used by all children classes.
113
114    public boolean areAllItemsEnabled() {
115        return true;
116    }
117
118    public boolean hasStableIds() {
119        return true;
120    }
121
122    public boolean isEmpty() {
123        return mItems.size() == 0;
124    }
125
126    public void registerDataSetObserver(DataSetObserver observer) {
127        // pass
128    }
129
130    public void unregisterDataSetObserver(DataSetObserver observer) {
131        // pass
132    }
133
134    // -------
135
136
137    protected AdapterBinding getBinding() {
138        return mBinding;
139    }
140
141    protected View getView(AdapterItem item, AdapterItem parentItem, View convertView,
142            ViewGroup parent) {
143        // we don't care about recycling here because we never scroll.
144        DataBindingItem dataBindingItem = item.getDataBindingItem();
145
146        BridgeContext context = RenderAction.getCurrentContext();
147
148        Pair<View, Boolean> pair = context.inflateView(dataBindingItem.getViewReference(),
149                parent, false /*attachToRoot*/, mSkipCallbackParser);
150
151        View view = pair.getFirst();
152        mSkipCallbackParser |= pair.getSecond();
153
154        if (view != null) {
155            fillView(context, view, item, parentItem);
156        } else {
157            // create a text view to display an error.
158            TextView tv = new TextView(context);
159            tv.setText("Unable to find layout: " + dataBindingItem.getViewReference().getName());
160            view = tv;
161        }
162
163        return view;
164    }
165
166    private void fillView(BridgeContext context, View view, AdapterItem item,
167            AdapterItem parentItem) {
168        if (view instanceof ViewGroup) {
169            ViewGroup group = (ViewGroup) view;
170            final int count = group.getChildCount();
171            for (int i = 0 ; i < count ; i++) {
172                fillView(context, group.getChildAt(i), item, parentItem);
173            }
174        } else {
175            int id = view.getId();
176            if (id != 0) {
177                ResourceReference resolvedRef = context.resolveId(id);
178                if (resolvedRef != null) {
179                    int fullPosition = item.getFullPosition();
180                    int positionPerType = item.getPositionPerType();
181                    int fullParentPosition = parentItem != null ? parentItem.getFullPosition() : 0;
182                    int parentPositionPerType = parentItem != null ?
183                            parentItem.getPositionPerType() : 0;
184
185                    if (view instanceof TextView) {
186                        TextView tv = (TextView) view;
187                        Object value = mCallback.getAdapterItemValue(
188                                mAdapterRef, context.getViewKey(view),
189                                item.getDataBindingItem().getViewReference(),
190                                fullPosition, positionPerType,
191                                fullParentPosition, parentPositionPerType,
192                                resolvedRef, ViewAttribute.TEXT, tv.getText().toString());
193                        if (value != null) {
194                            if (value.getClass() != ViewAttribute.TEXT.getAttributeClass()) {
195                                Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
196                                        "Wrong Adapter Item value class for TEXT. Expected String, got %s",
197                                        value.getClass().getName()), null);
198                            } else {
199                                tv.setText((String) value);
200                            }
201                        }
202                    }
203
204                    if (view instanceof Checkable) {
205                        Checkable cb = (Checkable) view;
206
207                        Object value = mCallback.getAdapterItemValue(
208                                mAdapterRef, context.getViewKey(view),
209                                item.getDataBindingItem().getViewReference(),
210                                fullPosition, positionPerType,
211                                fullParentPosition, parentPositionPerType,
212                                resolvedRef, ViewAttribute.IS_CHECKED, cb.isChecked());
213                        if (value != null) {
214                            if (value.getClass() != ViewAttribute.IS_CHECKED.getAttributeClass()) {
215                                Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
216                                        "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s",
217                                        value.getClass().getName()), null);
218                            } else {
219                                cb.setChecked((Boolean) value);
220                            }
221                        }
222                    }
223
224                    if (view instanceof ImageView) {
225                        ImageView iv = (ImageView) view;
226
227                        Object value = mCallback.getAdapterItemValue(
228                                mAdapterRef, context.getViewKey(view),
229                                item.getDataBindingItem().getViewReference(),
230                                fullPosition, positionPerType,
231                                fullParentPosition, parentPositionPerType,
232                                resolvedRef, ViewAttribute.SRC, iv.getDrawable());
233                        if (value != null) {
234                            if (value.getClass() != ViewAttribute.SRC.getAttributeClass()) {
235                                Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
236                                        "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s",
237                                        value.getClass().getName()), null);
238                            } else {
239                                // FIXME
240                            }
241                        }
242                    }
243                }
244            }
245        }
246    }
247}
248