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