• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.Dialog;
22 import android.app.Fragment;
23 import android.app.FragmentBreadCrumbs;
24 import android.app.LoaderManager.LoaderCallbacks;
25 import android.content.ClipboardManager;
26 import android.content.ContentResolver;
27 import android.content.Context;
28 import android.content.CursorLoader;
29 import android.content.DialogInterface;
30 import android.content.Intent;
31 import android.content.Loader;
32 import android.content.pm.PackageManager;
33 import android.content.pm.ResolveInfo;
34 import android.database.Cursor;
35 import android.database.DataSetObserver;
36 import android.graphics.BitmapFactory;
37 import android.graphics.drawable.Drawable;
38 import android.net.Uri;
39 import android.os.Bundle;
40 import android.provider.Browser;
41 import android.provider.BrowserContract;
42 import android.provider.BrowserContract.Combined;
43 import android.view.ContextMenu;
44 import android.view.ContextMenu.ContextMenuInfo;
45 import android.view.LayoutInflater;
46 import android.view.Menu;
47 import android.view.MenuInflater;
48 import android.view.MenuItem;
49 import android.view.View;
50 import android.view.ViewGroup;
51 import android.view.ViewStub;
52 import android.widget.AbsListView;
53 import android.widget.AdapterView;
54 import android.widget.AdapterView.AdapterContextMenuInfo;
55 import android.widget.AdapterView.OnItemClickListener;
56 import android.widget.BaseAdapter;
57 import android.widget.ExpandableListView;
58 import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
59 import android.widget.ExpandableListView.OnChildClickListener;
60 import android.widget.ListView;
61 import android.widget.TextView;
62 import android.widget.Toast;
63 
64 /**
65  * Activity for displaying the browser's history, divided into
66  * days of viewing.
67  */
68 public class BrowserHistoryPage extends Fragment
69         implements LoaderCallbacks<Cursor>, OnChildClickListener {
70 
71     static final int LOADER_HISTORY = 1;
72     static final int LOADER_MOST_VISITED = 2;
73 
74     CombinedBookmarksCallbacks mCallback;
75     HistoryAdapter mAdapter;
76     HistoryChildWrapper mChildWrapper;
77     boolean mDisableNewWindow;
78     HistoryItem mContextHeader;
79     String mMostVisitsLimit;
80     ListView mGroupList, mChildList;
81     private ViewGroup mPrefsContainer;
82     private FragmentBreadCrumbs mFragmentBreadCrumbs;
83     private ExpandableListView mHistoryList;
84 
85     private View mRoot;
86 
87     static interface HistoryQuery {
88         static final String[] PROJECTION = new String[] {
89                 Combined._ID, // 0
90                 Combined.DATE_LAST_VISITED, // 1
91                 Combined.TITLE, // 2
92                 Combined.URL, // 3
93                 Combined.FAVICON, // 4
94                 Combined.VISITS, // 5
95                 Combined.IS_BOOKMARK, // 6
96         };
97 
98         static final int INDEX_ID = 0;
99         static final int INDEX_DATE_LAST_VISITED = 1;
100         static final int INDEX_TITE = 2;
101         static final int INDEX_URL = 3;
102         static final int INDEX_FAVICON = 4;
103         static final int INDEX_VISITS = 5;
104         static final int INDEX_IS_BOOKMARK = 6;
105     }
106 
copy(CharSequence text)107     private void copy(CharSequence text) {
108         ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
109                 Context.CLIPBOARD_SERVICE);
110         cm.setText(text);
111     }
112 
113     @Override
onCreateLoader(int id, Bundle args)114     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
115         Uri.Builder combinedBuilder = Combined.CONTENT_URI.buildUpon();
116 
117         switch (id) {
118             case LOADER_HISTORY: {
119                 String sort = Combined.DATE_LAST_VISITED + " DESC";
120                 String where = Combined.VISITS + " > 0";
121                 CursorLoader loader = new CursorLoader(getActivity(), combinedBuilder.build(),
122                         HistoryQuery.PROJECTION, where, null, sort);
123                 return loader;
124             }
125 
126             case LOADER_MOST_VISITED: {
127                 Uri uri = combinedBuilder
128                         .appendQueryParameter(BrowserContract.PARAM_LIMIT, mMostVisitsLimit)
129                         .build();
130                 String where = Combined.VISITS + " > 0";
131                 CursorLoader loader = new CursorLoader(getActivity(), uri,
132                         HistoryQuery.PROJECTION, where, null, Combined.VISITS + " DESC");
133                 return loader;
134             }
135 
136             default: {
137                 throw new IllegalArgumentException();
138             }
139         }
140     }
141 
selectGroup(int position)142     void selectGroup(int position) {
143         mGroupItemClickListener.onItemClick(null,
144                 mAdapter.getGroupView(position, false, null, null),
145                 position, position);
146     }
147 
checkIfEmpty()148     void checkIfEmpty() {
149         if (mAdapter.mMostVisited != null && mAdapter.mHistoryCursor != null) {
150             // Both cursors have loaded - check to see if we have data
151             if (mAdapter.isEmpty()) {
152                 mRoot.findViewById(R.id.history).setVisibility(View.GONE);
153                 mRoot.findViewById(android.R.id.empty).setVisibility(View.VISIBLE);
154             } else {
155                 mRoot.findViewById(R.id.history).setVisibility(View.VISIBLE);
156                 mRoot.findViewById(android.R.id.empty).setVisibility(View.GONE);
157             }
158         }
159     }
160 
161     @Override
onLoadFinished(Loader<Cursor> loader, Cursor data)162     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
163         switch (loader.getId()) {
164             case LOADER_HISTORY: {
165                 mAdapter.changeCursor(data);
166                 if (!mAdapter.isEmpty() && mGroupList != null
167                         && mGroupList.getCheckedItemPosition() == ListView.INVALID_POSITION) {
168                     selectGroup(0);
169                 }
170 
171                 checkIfEmpty();
172                 break;
173             }
174 
175             case LOADER_MOST_VISITED: {
176                 mAdapter.changeMostVisitedCursor(data);
177 
178                 checkIfEmpty();
179                 break;
180             }
181 
182             default: {
183                 throw new IllegalArgumentException();
184             }
185         }
186     }
187 
188     @Override
onLoaderReset(Loader<Cursor> loader)189     public void onLoaderReset(Loader<Cursor> loader) {
190     }
191 
192     @Override
onCreate(Bundle icicle)193     public void onCreate(Bundle icicle) {
194         super.onCreate(icicle);
195 
196         setHasOptionsMenu(true);
197 
198         Bundle args = getArguments();
199         mDisableNewWindow = args.getBoolean(BrowserBookmarksPage.EXTRA_DISABLE_WINDOW, false);
200         int mvlimit = getResources().getInteger(R.integer.most_visits_limit);
201         mMostVisitsLimit = Integer.toString(mvlimit);
202         mCallback = (CombinedBookmarksCallbacks) getActivity();
203     }
204 
205     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)206     public View onCreateView(LayoutInflater inflater, ViewGroup container,
207             Bundle savedInstanceState) {
208         mRoot = inflater.inflate(R.layout.history, container, false);
209         mAdapter = new HistoryAdapter(getActivity());
210         ViewStub stub = (ViewStub) mRoot.findViewById(R.id.pref_stub);
211         if (stub != null) {
212             inflateTwoPane(stub);
213         } else {
214             inflateSinglePane();
215         }
216 
217         // Start the loaders
218         getLoaderManager().restartLoader(LOADER_HISTORY, null, this);
219         getLoaderManager().restartLoader(LOADER_MOST_VISITED, null, this);
220 
221         return mRoot;
222     }
223 
inflateSinglePane()224     private void inflateSinglePane() {
225         mHistoryList = (ExpandableListView) mRoot.findViewById(R.id.history);
226         mHistoryList.setAdapter(mAdapter);
227         mHistoryList.setOnChildClickListener(this);
228         registerForContextMenu(mHistoryList);
229     }
230 
inflateTwoPane(ViewStub stub)231     private void inflateTwoPane(ViewStub stub) {
232         stub.setLayoutResource(R.layout.preference_list_content);
233         stub.inflate();
234         mGroupList = (ListView) mRoot.findViewById(android.R.id.list);
235         mPrefsContainer = (ViewGroup) mRoot.findViewById(R.id.prefs_frame);
236         mFragmentBreadCrumbs = (FragmentBreadCrumbs) mRoot.findViewById(android.R.id.title);
237         mFragmentBreadCrumbs.setMaxVisible(1);
238         mFragmentBreadCrumbs.setActivity(getActivity());
239         mPrefsContainer.setVisibility(View.VISIBLE);
240         mGroupList.setAdapter(new HistoryGroupWrapper(mAdapter));
241         mGroupList.setOnItemClickListener(mGroupItemClickListener);
242         mGroupList.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
243         mChildWrapper = new HistoryChildWrapper(mAdapter);
244         mChildList = new ListView(getActivity());
245         mChildList.setAdapter(mChildWrapper);
246         mChildList.setOnItemClickListener(mChildItemClickListener);
247         registerForContextMenu(mChildList);
248         ViewGroup prefs = (ViewGroup) mRoot.findViewById(R.id.prefs);
249         prefs.addView(mChildList);
250     }
251 
252     private OnItemClickListener mGroupItemClickListener = new OnItemClickListener() {
253         @Override
254         public void onItemClick(
255                 AdapterView<?> parent, View view, int position, long id) {
256             CharSequence title = ((TextView) view).getText();
257             mFragmentBreadCrumbs.setTitle(title, title);
258             mChildWrapper.setSelectedGroup(position);
259             mGroupList.setItemChecked(position, true);
260         }
261     };
262 
263     private OnItemClickListener mChildItemClickListener = new OnItemClickListener() {
264         @Override
265         public void onItemClick(
266                 AdapterView<?> parent, View view, int position, long id) {
267             mCallback.openUrl(((HistoryItem) view).getUrl());
268         }
269     };
270 
271     @Override
onChildClick(ExpandableListView parent, View view, int groupPosition, int childPosition, long id)272     public boolean onChildClick(ExpandableListView parent, View view,
273             int groupPosition, int childPosition, long id) {
274         mCallback.openUrl(((HistoryItem) view).getUrl());
275         return true;
276     }
277 
278     @Override
onDestroy()279     public void onDestroy() {
280         super.onDestroy();
281         getLoaderManager().destroyLoader(LOADER_HISTORY);
282         getLoaderManager().destroyLoader(LOADER_MOST_VISITED);
283     }
284 
285     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)286     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
287         super.onCreateOptionsMenu(menu, inflater);
288         inflater.inflate(R.menu.history, menu);
289     }
290 
promptToClearHistory()291     void promptToClearHistory() {
292         final ContentResolver resolver = getActivity().getContentResolver();
293         final ClearHistoryTask clear = new ClearHistoryTask(resolver);
294         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
295                 .setMessage(R.string.pref_privacy_clear_history_dlg)
296                 .setIconAttribute(android.R.attr.alertDialogIcon)
297                 .setNegativeButton(R.string.cancel, null)
298                 .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
299                      @Override
300                      public void onClick(DialogInterface dialog, int which) {
301                          if (which == DialogInterface.BUTTON_POSITIVE) {
302                              clear.start();
303                          }
304                      }
305                 });
306         final Dialog dialog = builder.create();
307         dialog.show();
308     }
309 
310     @Override
onOptionsItemSelected(MenuItem item)311     public boolean onOptionsItemSelected(MenuItem item) {
312         if (item.getItemId() == R.id.clear_history_menu_id) {
313             promptToClearHistory();
314             return true;
315         }
316         return super.onOptionsItemSelected(item);
317     }
318 
319     static class ClearHistoryTask extends Thread {
320         ContentResolver mResolver;
321 
ClearHistoryTask(ContentResolver resolver)322         public ClearHistoryTask(ContentResolver resolver) {
323             mResolver = resolver;
324         }
325 
326         @Override
run()327         public void run() {
328             Browser.clearHistory(mResolver);
329         }
330     }
331 
getTargetView(ContextMenuInfo menuInfo)332     View getTargetView(ContextMenuInfo menuInfo) {
333         if (menuInfo instanceof AdapterContextMenuInfo) {
334             return ((AdapterContextMenuInfo) menuInfo).targetView;
335         }
336         if (menuInfo instanceof ExpandableListContextMenuInfo) {
337             return ((ExpandableListContextMenuInfo) menuInfo).targetView;
338         }
339         return null;
340     }
341 
342     @Override
onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)343     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
344 
345         View targetView = getTargetView(menuInfo);
346         if (!(targetView instanceof HistoryItem)) {
347             return;
348         }
349         HistoryItem historyItem = (HistoryItem) targetView;
350 
351         // Inflate the menu
352         Activity parent = getActivity();
353         MenuInflater inflater = parent.getMenuInflater();
354         inflater.inflate(R.menu.historycontext, menu);
355 
356         // Setup the header
357         if (mContextHeader == null) {
358             mContextHeader = new HistoryItem(parent, false);
359             mContextHeader.setEnableScrolling(true);
360         } else if (mContextHeader.getParent() != null) {
361             ((ViewGroup) mContextHeader.getParent()).removeView(mContextHeader);
362         }
363         historyItem.copyTo(mContextHeader);
364         menu.setHeaderView(mContextHeader);
365 
366         // Only show open in new tab if it was not explicitly disabled
367         if (mDisableNewWindow) {
368             menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
369         }
370         // For a bookmark, provide the option to remove it from bookmarks
371         if (historyItem.isBookmark()) {
372             MenuItem item = menu.findItem(R.id.save_to_bookmarks_menu_id);
373             item.setTitle(R.string.remove_from_bookmarks);
374         }
375         // decide whether to show the share link option
376         PackageManager pm = parent.getPackageManager();
377         Intent send = new Intent(Intent.ACTION_SEND);
378         send.setType("text/plain");
379         ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
380         menu.findItem(R.id.share_link_context_menu_id).setVisible(ri != null);
381 
382         super.onCreateContextMenu(menu, v, menuInfo);
383     }
384 
385     @Override
onContextItemSelected(MenuItem item)386     public boolean onContextItemSelected(MenuItem item) {
387         ContextMenuInfo menuInfo = item.getMenuInfo();
388         if (menuInfo == null) {
389             return false;
390         }
391         View targetView = getTargetView(menuInfo);
392         if (!(targetView instanceof HistoryItem)) {
393             return false;
394         }
395         HistoryItem historyItem = (HistoryItem) targetView;
396         String url = historyItem.getUrl();
397         String title = historyItem.getName();
398         Activity activity = getActivity();
399         switch (item.getItemId()) {
400             case R.id.open_context_menu_id:
401                 mCallback.openUrl(url);
402                 return true;
403             case R.id.new_window_context_menu_id:
404                 mCallback.openInNewTab(url);
405                 return true;
406             case R.id.save_to_bookmarks_menu_id:
407                 if (historyItem.isBookmark()) {
408                     Bookmarks.removeFromBookmarks(activity, activity.getContentResolver(),
409                             url, title);
410                 } else {
411                     Browser.saveBookmark(activity, title, url);
412                 }
413                 return true;
414             case R.id.share_link_context_menu_id:
415                 Browser.sendString(activity, url,
416                         activity.getText(R.string.choosertitle_sharevia).toString());
417                 return true;
418             case R.id.copy_url_context_menu_id:
419                 copy(url);
420                 return true;
421             case R.id.delete_context_menu_id:
422                 Browser.deleteFromHistory(activity.getContentResolver(), url);
423                 return true;
424             case R.id.homepage_context_menu_id:
425                 BrowserSettings.getInstance().setHomePage(url);
426                 Toast.makeText(activity, R.string.homepage_set, Toast.LENGTH_LONG).show();
427                 return true;
428             default:
429                 break;
430         }
431         return super.onContextItemSelected(item);
432     }
433 
434     private static abstract class HistoryWrapper extends BaseAdapter {
435 
436         protected HistoryAdapter mAdapter;
437         private DataSetObserver mObserver = new DataSetObserver() {
438             @Override
439             public void onChanged() {
440                 super.onChanged();
441                 notifyDataSetChanged();
442             }
443 
444             @Override
445             public void onInvalidated() {
446                 super.onInvalidated();
447                 notifyDataSetInvalidated();
448             }
449         };
450 
HistoryWrapper(HistoryAdapter adapter)451         public HistoryWrapper(HistoryAdapter adapter) {
452             mAdapter = adapter;
453             mAdapter.registerDataSetObserver(mObserver);
454         }
455 
456     }
457     private static class HistoryGroupWrapper extends HistoryWrapper {
458 
HistoryGroupWrapper(HistoryAdapter adapter)459         public HistoryGroupWrapper(HistoryAdapter adapter) {
460             super(adapter);
461         }
462 
463         @Override
getCount()464         public int getCount() {
465             return mAdapter.getGroupCount();
466         }
467 
468         @Override
getItem(int position)469         public Object getItem(int position) {
470             return null;
471         }
472 
473         @Override
getItemId(int position)474         public long getItemId(int position) {
475             return position;
476         }
477 
478         @Override
getView(int position, View convertView, ViewGroup parent)479         public View getView(int position, View convertView, ViewGroup parent) {
480             return mAdapter.getGroupView(position, false, convertView, parent);
481         }
482 
483     }
484 
485     private static class HistoryChildWrapper extends HistoryWrapper {
486 
487         private int mSelectedGroup;
488 
HistoryChildWrapper(HistoryAdapter adapter)489         public HistoryChildWrapper(HistoryAdapter adapter) {
490             super(adapter);
491         }
492 
setSelectedGroup(int groupPosition)493         void setSelectedGroup(int groupPosition) {
494             mSelectedGroup = groupPosition;
495             notifyDataSetChanged();
496         }
497 
498         @Override
getCount()499         public int getCount() {
500             return mAdapter.getChildrenCount(mSelectedGroup);
501         }
502 
503         @Override
getItem(int position)504         public Object getItem(int position) {
505             return null;
506         }
507 
508         @Override
getItemId(int position)509         public long getItemId(int position) {
510             return position;
511         }
512 
513         @Override
getView(int position, View convertView, ViewGroup parent)514         public View getView(int position, View convertView, ViewGroup parent) {
515             return mAdapter.getChildView(mSelectedGroup, position,
516                     false, convertView, parent);
517         }
518 
519     }
520 
521     private class HistoryAdapter extends DateSortedExpandableListAdapter {
522 
523         private Cursor mMostVisited, mHistoryCursor;
524         Drawable mFaviconBackground;
525 
HistoryAdapter(Context context)526         HistoryAdapter(Context context) {
527             super(context, HistoryQuery.INDEX_DATE_LAST_VISITED);
528             mFaviconBackground = BookmarkUtils.createListFaviconBackground(context);
529         }
530 
531         @Override
changeCursor(Cursor cursor)532         public void changeCursor(Cursor cursor) {
533             mHistoryCursor = cursor;
534             super.changeCursor(cursor);
535         }
536 
changeMostVisitedCursor(Cursor cursor)537         void changeMostVisitedCursor(Cursor cursor) {
538             if (mMostVisited == cursor) {
539                 return;
540             }
541             if (mMostVisited != null) {
542                 mMostVisited.unregisterDataSetObserver(mDataSetObserver);
543                 mMostVisited.close();
544             }
545             mMostVisited = cursor;
546             if (mMostVisited != null) {
547                 mMostVisited.registerDataSetObserver(mDataSetObserver);
548             }
549             notifyDataSetChanged();
550         }
551 
552         @Override
getChildId(int groupPosition, int childPosition)553         public long getChildId(int groupPosition, int childPosition) {
554             if (moveCursorToChildPosition(groupPosition, childPosition)) {
555                 Cursor cursor = getCursor(groupPosition);
556                 return cursor.getLong(HistoryQuery.INDEX_ID);
557             }
558             return 0;
559         }
560 
561         @Override
getGroupCount()562         public int getGroupCount() {
563             return super.getGroupCount() + (!isMostVisitedEmpty() ? 1 : 0);
564         }
565 
566         @Override
getChildrenCount(int groupPosition)567         public int getChildrenCount(int groupPosition) {
568             if (groupPosition >= super.getGroupCount()) {
569                 if (isMostVisitedEmpty()) {
570                     return 0;
571                 }
572                 return mMostVisited.getCount();
573             }
574             return super.getChildrenCount(groupPosition);
575         }
576 
577         @Override
isEmpty()578         public boolean isEmpty() {
579             if (!super.isEmpty()) {
580                 return false;
581             }
582             return isMostVisitedEmpty();
583         }
584 
isMostVisitedEmpty()585         private boolean isMostVisitedEmpty() {
586             return mMostVisited == null
587                     || mMostVisited.isClosed()
588                     || mMostVisited.getCount() == 0;
589         }
590 
getCursor(int groupPosition)591         Cursor getCursor(int groupPosition) {
592             if (groupPosition >= super.getGroupCount()) {
593                 return mMostVisited;
594             }
595             return mHistoryCursor;
596         }
597 
598         @Override
getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)599         public View getGroupView(int groupPosition, boolean isExpanded,
600                 View convertView, ViewGroup parent) {
601             if (groupPosition >= super.getGroupCount()) {
602                 if (mMostVisited == null || mMostVisited.isClosed()) {
603                     throw new IllegalStateException("Data is not valid");
604                 }
605                 TextView item;
606                 if (null == convertView || !(convertView instanceof TextView)) {
607                     LayoutInflater factory = LayoutInflater.from(getContext());
608                     item = (TextView) factory.inflate(R.layout.history_header, null);
609                 } else {
610                     item = (TextView) convertView;
611                 }
612                 item.setText(R.string.tab_most_visited);
613                 return item;
614             }
615             return super.getGroupView(groupPosition, isExpanded, convertView, parent);
616         }
617 
618         @Override
moveCursorToChildPosition( int groupPosition, int childPosition)619         boolean moveCursorToChildPosition(
620                 int groupPosition, int childPosition) {
621             if (groupPosition >= super.getGroupCount()) {
622                 if (mMostVisited != null && !mMostVisited.isClosed()) {
623                     mMostVisited.moveToPosition(childPosition);
624                     return true;
625                 }
626                 return false;
627             }
628             return super.moveCursorToChildPosition(groupPosition, childPosition);
629         }
630 
631         @Override
getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent)632         public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
633                 View convertView, ViewGroup parent) {
634             HistoryItem item;
635             if (null == convertView || !(convertView instanceof HistoryItem)) {
636                 item = new HistoryItem(getContext());
637                 // Add padding on the left so it will be indented from the
638                 // arrows on the group views.
639                 item.setPadding(item.getPaddingLeft() + 10,
640                         item.getPaddingTop(),
641                         item.getPaddingRight(),
642                         item.getPaddingBottom());
643                 item.setFaviconBackground(mFaviconBackground);
644             } else {
645                 item = (HistoryItem) convertView;
646             }
647 
648             // Bail early if the Cursor is closed.
649             if (!moveCursorToChildPosition(groupPosition, childPosition)) {
650                 return item;
651             }
652 
653             Cursor cursor = getCursor(groupPosition);
654             item.setName(cursor.getString(HistoryQuery.INDEX_TITE));
655             String url = cursor.getString(HistoryQuery.INDEX_URL);
656             item.setUrl(url);
657             byte[] data = cursor.getBlob(HistoryQuery.INDEX_FAVICON);
658             if (data != null) {
659                 item.setFavicon(BitmapFactory.decodeByteArray(data, 0,
660                         data.length));
661             } else {
662                 item.setFavicon(null);
663             }
664             item.setIsBookmark(cursor.getInt(HistoryQuery.INDEX_IS_BOOKMARK) == 1);
665             return item;
666         }
667     }
668 }
669