• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.browser.view;
18 
19 import android.content.Context;
20 import android.database.DataSetObserver;
21 import android.provider.BrowserContract;
22 import android.util.AttributeSet;
23 import android.view.ContextMenu;
24 import android.view.ContextMenu.ContextMenuInfo;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.widget.BaseExpandableListAdapter;
29 import android.widget.ExpandableListAdapter;
30 import android.widget.ExpandableListView;
31 import android.widget.FrameLayout;
32 import android.widget.LinearLayout;
33 import android.widget.TextView;
34 
35 import com.android.browser.BreadCrumbView;
36 import com.android.browser.BrowserBookmarksAdapter;
37 import com.android.browser.R;
38 import com.android.internal.view.menu.MenuBuilder;
39 
40 import org.json.JSONException;
41 import org.json.JSONObject;
42 
43 import java.util.ArrayList;
44 import java.util.HashMap;
45 
46 public class BookmarkExpandableView extends ExpandableListView
47         implements BreadCrumbView.Controller {
48 
49     public static final String LOCAL_ACCOUNT_NAME = "local";
50 
51     private BookmarkAccountAdapter mAdapter;
52     private int mColumnWidth;
53     private Context mContext;
54     private OnChildClickListener mOnChildClickListener;
55     private ContextMenuInfo mContextMenuInfo = null;
56     private OnCreateContextMenuListener mOnCreateContextMenuListener;
57     private boolean mLongClickable;
58     private BreadCrumbView.Controller mBreadcrumbController;
59     private int mMaxColumnCount;
60 
BookmarkExpandableView(Context context)61     public BookmarkExpandableView(Context context) {
62         super(context);
63         init(context);
64     }
65 
BookmarkExpandableView(Context context, AttributeSet attrs)66     public BookmarkExpandableView(Context context, AttributeSet attrs) {
67         super(context, attrs);
68         init(context);
69     }
70 
BookmarkExpandableView( Context context, AttributeSet attrs, int defStyle)71     public BookmarkExpandableView(
72             Context context, AttributeSet attrs, int defStyle) {
73         super(context, attrs, defStyle);
74         init(context);
75     }
76 
init(Context context)77     void init(Context context) {
78         mContext = context;
79         setItemsCanFocus(true);
80         setLongClickable(false);
81         mMaxColumnCount = mContext.getResources()
82                 .getInteger(R.integer.max_bookmark_columns);
83         setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY);
84         mAdapter = new BookmarkAccountAdapter(mContext);
85         super.setAdapter(mAdapter);
86     }
87 
88     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)89     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
90         int width = MeasureSpec.getSize(widthMeasureSpec);
91         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
92         if (width > 0) {
93             mAdapter.measureChildren(width);
94             setPadding(mAdapter.mRowPadding, 0, mAdapter.mRowPadding, 0);
95             widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, widthMode);
96         }
97         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
98         if (width != getMeasuredWidth()) {
99             mAdapter.measureChildren(getMeasuredWidth());
100         }
101     }
102 
103     @Override
setAdapter(ExpandableListAdapter adapter)104     public void setAdapter(ExpandableListAdapter adapter) {
105         throw new RuntimeException("Not supported");
106     }
107 
setColumnWidthFromLayout(int layout)108     public void setColumnWidthFromLayout(int layout) {
109         LayoutInflater infalter = LayoutInflater.from(mContext);
110         View v = infalter.inflate(layout, this, false);
111         v.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
112         mColumnWidth = v.getMeasuredWidth();
113     }
114 
clearAccounts()115     public void clearAccounts() {
116         mAdapter.clear();
117     }
118 
addAccount(String accountName, BrowserBookmarksAdapter adapter, boolean expandGroup)119     public void addAccount(String accountName, BrowserBookmarksAdapter adapter,
120             boolean expandGroup) {
121         // First, check if it already exists
122         int indexOf = mAdapter.mGroups.indexOf(accountName);
123         if (indexOf >= 0) {
124             BrowserBookmarksAdapter existing = mAdapter.mChildren.get(indexOf);
125             if (existing != adapter) {
126                 existing.unregisterDataSetObserver(mAdapter.mObserver);
127                 // Replace the existing one
128                 mAdapter.mChildren.remove(indexOf);
129                 mAdapter.mChildren.add(indexOf, adapter);
130                 adapter.registerDataSetObserver(mAdapter.mObserver);
131             }
132         } else {
133             mAdapter.mGroups.add(accountName);
134             mAdapter.mChildren.add(adapter);
135             adapter.registerDataSetObserver(mAdapter.mObserver);
136         }
137         mAdapter.notifyDataSetChanged();
138         if (expandGroup) {
139             expandGroup(mAdapter.getGroupCount() - 1);
140         }
141     }
142 
143     @Override
setOnChildClickListener(OnChildClickListener onChildClickListener)144     public void setOnChildClickListener(OnChildClickListener onChildClickListener) {
145         mOnChildClickListener = onChildClickListener;
146     }
147 
148     @Override
setOnCreateContextMenuListener(OnCreateContextMenuListener l)149     public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) {
150         mOnCreateContextMenuListener = l;
151         if (!mLongClickable) {
152             mLongClickable = true;
153             if (mAdapter != null) {
154                 mAdapter.notifyDataSetChanged();
155             }
156         }
157     }
158 
159     @Override
createContextMenu(ContextMenu menu)160     public void createContextMenu(ContextMenu menu) {
161         // The below is copied from View - we want to bypass the override
162         // in AbsListView
163 
164         ContextMenuInfo menuInfo = getContextMenuInfo();
165 
166         // Sets the current menu info so all items added to menu will have
167         // my extra info set.
168         ((MenuBuilder)menu).setCurrentMenuInfo(menuInfo);
169 
170         onCreateContextMenu(menu);
171         if (mOnCreateContextMenuListener != null) {
172             mOnCreateContextMenuListener.onCreateContextMenu(menu, this, menuInfo);
173         }
174 
175         // Clear the extra information so subsequent items that aren't mine don't
176         // have my extra info.
177         ((MenuBuilder)menu).setCurrentMenuInfo(null);
178 
179         if (mParent != null) {
180             mParent.createContextMenu(menu);
181         }
182     }
183 
184     @Override
showContextMenuForChild(View originalView)185     public boolean showContextMenuForChild(View originalView) {
186         int groupPosition = (Integer) originalView.getTag(R.id.group_position);
187         int childPosition = (Integer) originalView.getTag(R.id.child_position);
188 
189         mContextMenuInfo = new BookmarkContextMenuInfo(childPosition,
190                 groupPosition);
191         if (getParent() != null) {
192             getParent().showContextMenuForChild(this);
193         }
194 
195         return true;
196     }
197 
198     @Override
onTop(BreadCrumbView view, int level, Object data)199     public void onTop(BreadCrumbView view, int level, Object data) {
200         if (mBreadcrumbController != null) {
201             mBreadcrumbController.onTop(view, level, data);
202         }
203     }
204 
setBreadcrumbController(BreadCrumbView.Controller controller)205     public void setBreadcrumbController(BreadCrumbView.Controller controller) {
206         mBreadcrumbController = controller;
207     }
208 
209     @Override
getContextMenuInfo()210     protected ContextMenuInfo getContextMenuInfo() {
211         return mContextMenuInfo;
212     }
213 
getChildAdapter(int groupPosition)214     public BrowserBookmarksAdapter getChildAdapter(int groupPosition) {
215         return mAdapter.mChildren.get(groupPosition);
216     }
217 
218     private OnClickListener mChildClickListener = new OnClickListener() {
219 
220         @Override
221         public void onClick(View v) {
222             if (v.getVisibility() != View.VISIBLE) {
223                 return;
224             }
225             int groupPosition = (Integer) v.getTag(R.id.group_position);
226             int childPosition = (Integer) v.getTag(R.id.child_position);
227             if (mAdapter.getGroupCount() <= groupPosition
228                     || mAdapter.mChildren.get(groupPosition).getCount() <= childPosition) {
229                 return;
230             }
231             long id = mAdapter.mChildren.get(groupPosition).getItemId(childPosition);
232             if (mOnChildClickListener != null) {
233                 mOnChildClickListener.onChildClick(BookmarkExpandableView.this,
234                         v, groupPosition, childPosition, id);
235             }
236         }
237     };
238 
239     private OnClickListener mGroupOnClickListener = new OnClickListener() {
240 
241         @Override
242         public void onClick(View v) {
243             int groupPosition = (Integer) v.getTag(R.id.group_position);
244             if (isGroupExpanded(groupPosition)) {
245                 collapseGroup(groupPosition);
246             } else {
247                 expandGroup(groupPosition, true);
248             }
249         }
250     };
251 
getBreadCrumbs(int groupPosition)252     public BreadCrumbView getBreadCrumbs(int groupPosition) {
253         return mAdapter.getBreadCrumbView(groupPosition);
254     }
255 
saveGroupState()256     public JSONObject saveGroupState() throws JSONException {
257         JSONObject obj = new JSONObject();
258         int count = mAdapter.getGroupCount();
259         for (int i = 0; i < count; i++) {
260             String acctName = mAdapter.mGroups.get(i);
261             if (!isGroupExpanded(i)) {
262                 obj.put(acctName != null ? acctName : LOCAL_ACCOUNT_NAME, false);
263             }
264         }
265         return obj;
266     }
267 
268     class BookmarkAccountAdapter extends BaseExpandableListAdapter {
269         ArrayList<BrowserBookmarksAdapter> mChildren;
270         ArrayList<String> mGroups;
271         HashMap<Integer, BreadCrumbView> mBreadcrumbs =
272                 new HashMap<Integer, BreadCrumbView>();
273         LayoutInflater mInflater;
274         int mRowCount = 1; // assume at least 1 child fits in a row
275         int mLastViewWidth = -1;
276         int mRowPadding = -1;
277         DataSetObserver mObserver = new DataSetObserver() {
278             @Override
279             public void onChanged() {
280                 notifyDataSetChanged();
281             }
282 
283             @Override
284             public void onInvalidated() {
285                 notifyDataSetInvalidated();
286             }
287         };
288 
BookmarkAccountAdapter(Context context)289         public BookmarkAccountAdapter(Context context) {
290             mContext = context;
291             mInflater = LayoutInflater.from(mContext);
292             mChildren = new ArrayList<BrowserBookmarksAdapter>();
293             mGroups = new ArrayList<String>();
294         }
295 
clear()296         public void clear() {
297             mGroups.clear();
298             mChildren.clear();
299             notifyDataSetChanged();
300         }
301 
302         @Override
getChild(int groupPosition, int childPosition)303         public Object getChild(int groupPosition, int childPosition) {
304             return mChildren.get(groupPosition).getItem(childPosition);
305         }
306 
307         @Override
getChildId(int groupPosition, int childPosition)308         public long getChildId(int groupPosition, int childPosition) {
309             return childPosition;
310         }
311 
312         @Override
getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent)313         public View getChildView(int groupPosition, int childPosition,
314                 boolean isLastChild, View convertView, ViewGroup parent) {
315             if (convertView == null) {
316                 convertView = mInflater.inflate(R.layout.bookmark_grid_row, parent, false);
317             }
318             BrowserBookmarksAdapter childAdapter = mChildren.get(groupPosition);
319             int rowCount = mRowCount;
320             LinearLayout row = (LinearLayout) convertView;
321             if (row.getChildCount() > rowCount) {
322                 row.removeViews(rowCount, row.getChildCount() - rowCount);
323             }
324             for (int i = 0; i < rowCount; i++) {
325                 View cv = null;
326                 if (row.getChildCount() > i) {
327                     cv = row.getChildAt(i);
328                 }
329                 int realChildPosition = (childPosition * rowCount) + i;
330                 if (realChildPosition < childAdapter.getCount()) {
331                     View v = childAdapter.getView(realChildPosition, cv, row);
332                     v.setTag(R.id.group_position, groupPosition);
333                     v.setTag(R.id.child_position, realChildPosition);
334                     v.setOnClickListener(mChildClickListener);
335                     v.setLongClickable(mLongClickable);
336                     if (cv == null) {
337                         row.addView(v);
338                     } else if (cv != v) {
339                         row.removeViewAt(i);
340                         row.addView(v, i);
341                     } else {
342                         cv.setVisibility(View.VISIBLE);
343                     }
344                 } else if (cv != null) {
345                     cv.setVisibility(View.GONE);
346                 }
347             }
348             return row;
349         }
350 
351         @Override
getChildrenCount(int groupPosition)352         public int getChildrenCount(int groupPosition) {
353             BrowserBookmarksAdapter adapter = mChildren.get(groupPosition);
354             return (int) Math.ceil(adapter.getCount() / (float)mRowCount);
355         }
356 
357         @Override
getGroup(int groupPosition)358         public Object getGroup(int groupPosition) {
359             return mChildren.get(groupPosition);
360         }
361 
362         @Override
getGroupCount()363         public int getGroupCount() {
364             return mGroups.size();
365         }
366 
measureChildren(int viewWidth)367         public void measureChildren(int viewWidth) {
368             if (mLastViewWidth == viewWidth) return;
369 
370             int rowCount = viewWidth / mColumnWidth;
371             if (mMaxColumnCount > 0) {
372                 rowCount = Math.min(rowCount, mMaxColumnCount);
373             }
374             int rowPadding = (viewWidth - (rowCount * mColumnWidth)) / 2;
375             boolean notify = rowCount != mRowCount || rowPadding != mRowPadding;
376             mRowCount = rowCount;
377             mRowPadding = rowPadding;
378             mLastViewWidth = viewWidth;
379             if (notify) {
380                 notifyDataSetChanged();
381             }
382         }
383 
384         @Override
getGroupId(int groupPosition)385         public long getGroupId(int groupPosition) {
386             return groupPosition;
387         }
388 
389         @Override
getGroupView(int groupPosition, boolean isExpanded, View view, ViewGroup parent)390         public View getGroupView(int groupPosition, boolean isExpanded,
391                 View view, ViewGroup parent) {
392             if (view == null) {
393                 view = mInflater.inflate(R.layout.bookmark_group_view, parent, false);
394                 view.setOnClickListener(mGroupOnClickListener);
395             }
396             view.setTag(R.id.group_position, groupPosition);
397             FrameLayout crumbHolder = (FrameLayout) view.findViewById(R.id.crumb_holder);
398             crumbHolder.removeAllViews();
399             BreadCrumbView crumbs = getBreadCrumbView(groupPosition);
400             if (crumbs.getParent() != null) {
401                 ((ViewGroup)crumbs.getParent()).removeView(crumbs);
402             }
403             crumbHolder.addView(crumbs);
404             TextView name = (TextView) view.findViewById(R.id.group_name);
405             String groupName = mGroups.get(groupPosition);
406             if (groupName == null) {
407                 groupName = mContext.getString(R.string.local_bookmarks);
408             }
409             name.setText(groupName);
410             return view;
411         }
412 
getBreadCrumbView(int groupPosition)413         public BreadCrumbView getBreadCrumbView(int groupPosition) {
414             BreadCrumbView crumbs = mBreadcrumbs.get(groupPosition);
415             if (crumbs == null) {
416                 crumbs = (BreadCrumbView)
417                         mInflater.inflate(R.layout.bookmarks_header, null);
418                 crumbs.setController(BookmarkExpandableView.this);
419                 crumbs.setUseBackButton(true);
420                 crumbs.setMaxVisible(2);
421                 String bookmarks = mContext.getString(R.string.bookmarks);
422                 crumbs.pushView(bookmarks, false,
423                         BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER);
424                 crumbs.setTag(R.id.group_position, groupPosition);
425                 crumbs.setVisibility(View.GONE);
426                 mBreadcrumbs.put(groupPosition, crumbs);
427             }
428             return crumbs;
429         }
430 
431         @Override
hasStableIds()432         public boolean hasStableIds() {
433             return false;
434         }
435 
436         @Override
isChildSelectable(int groupPosition, int childPosition)437         public boolean isChildSelectable(int groupPosition, int childPosition) {
438             return true;
439         }
440     }
441 
442     public static class BookmarkContextMenuInfo implements ContextMenuInfo {
443 
BookmarkContextMenuInfo(int childPosition, int groupPosition)444         private BookmarkContextMenuInfo(int childPosition, int groupPosition) {
445             this.childPosition = childPosition;
446             this.groupPosition = groupPosition;
447         }
448 
449         public int childPosition;
450         public int groupPosition;
451     }
452 
453 }
454