1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/home/BookmarksPanel.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,251 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.home; 1.10 + 1.11 +import java.util.List; 1.12 + 1.13 +import org.mozilla.gecko.R; 1.14 +import org.mozilla.gecko.db.BrowserContract.Bookmarks; 1.15 +import org.mozilla.gecko.db.BrowserDB; 1.16 +import org.mozilla.gecko.home.BookmarksListAdapter.FolderInfo; 1.17 +import org.mozilla.gecko.home.BookmarksListAdapter.OnRefreshFolderListener; 1.18 +import org.mozilla.gecko.home.BookmarksListAdapter.RefreshType; 1.19 +import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; 1.20 + 1.21 +import android.app.Activity; 1.22 +import android.content.Context; 1.23 +import android.content.res.Configuration; 1.24 +import android.database.Cursor; 1.25 +import android.os.Bundle; 1.26 +import android.support.v4.app.LoaderManager.LoaderCallbacks; 1.27 +import android.support.v4.content.Loader; 1.28 +import android.view.LayoutInflater; 1.29 +import android.view.View; 1.30 +import android.view.ViewGroup; 1.31 +import android.view.ViewStub; 1.32 +import android.widget.ImageView; 1.33 +import android.widget.TextView; 1.34 + 1.35 +/** 1.36 + * A page in about:home that displays a ListView of bookmarks. 1.37 + */ 1.38 +public class BookmarksPanel extends HomeFragment { 1.39 + public static final String LOGTAG = "GeckoBookmarksPanel"; 1.40 + 1.41 + // Cursor loader ID for list of bookmarks. 1.42 + private static final int LOADER_ID_BOOKMARKS_LIST = 0; 1.43 + 1.44 + // Information about the target bookmarks folder. 1.45 + private static final String BOOKMARKS_FOLDER_INFO = "folder_info"; 1.46 + 1.47 + // Refresh type for folder refreshing loader. 1.48 + private static final String BOOKMARKS_REFRESH_TYPE = "refresh_type"; 1.49 + 1.50 + // List of bookmarks. 1.51 + private BookmarksListView mList; 1.52 + 1.53 + // Adapter for list of bookmarks. 1.54 + private BookmarksListAdapter mListAdapter; 1.55 + 1.56 + // Adapter's parent stack. 1.57 + private List<FolderInfo> mSavedParentStack; 1.58 + 1.59 + // Reference to the View to display when there are no results. 1.60 + private View mEmptyView; 1.61 + 1.62 + // Callback for cursor loaders. 1.63 + private CursorLoaderCallbacks mLoaderCallbacks; 1.64 + 1.65 + @Override 1.66 + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 1.67 + final View view = inflater.inflate(R.layout.home_bookmarks_panel, container, false); 1.68 + 1.69 + mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list); 1.70 + 1.71 + mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() { 1.72 + @Override 1.73 + public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) { 1.74 + final int type = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE)); 1.75 + if (type == Bookmarks.TYPE_FOLDER) { 1.76 + // We don't show a context menu for folders 1.77 + return null; 1.78 + } 1.79 + final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id); 1.80 + info.url = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.URL)); 1.81 + info.title = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.TITLE)); 1.82 + info.bookmarkId = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID)); 1.83 + return info; 1.84 + } 1.85 + }); 1.86 + 1.87 + return view; 1.88 + } 1.89 + 1.90 + @Override 1.91 + public void onViewCreated(View view, Bundle savedInstanceState) { 1.92 + super.onViewCreated(view, savedInstanceState); 1.93 + 1.94 + OnUrlOpenListener listener = null; 1.95 + try { 1.96 + listener = (OnUrlOpenListener) getActivity(); 1.97 + } catch (ClassCastException e) { 1.98 + throw new ClassCastException(getActivity().toString() 1.99 + + " must implement HomePager.OnUrlOpenListener"); 1.100 + } 1.101 + 1.102 + mList.setTag(HomePager.LIST_TAG_BOOKMARKS); 1.103 + mList.setOnUrlOpenListener(listener); 1.104 + 1.105 + registerForContextMenu(mList); 1.106 + } 1.107 + 1.108 + @Override 1.109 + public void onActivityCreated(Bundle savedInstanceState) { 1.110 + super.onActivityCreated(savedInstanceState); 1.111 + 1.112 + final Activity activity = getActivity(); 1.113 + 1.114 + // Setup the list adapter. 1.115 + mListAdapter = new BookmarksListAdapter(activity, null, mSavedParentStack); 1.116 + mListAdapter.setOnRefreshFolderListener(new OnRefreshFolderListener() { 1.117 + @Override 1.118 + public void onRefreshFolder(FolderInfo folderInfo, RefreshType refreshType) { 1.119 + // Restart the loader with folder as the argument. 1.120 + Bundle bundle = new Bundle(); 1.121 + bundle.putParcelable(BOOKMARKS_FOLDER_INFO, folderInfo); 1.122 + bundle.putParcelable(BOOKMARKS_REFRESH_TYPE, refreshType); 1.123 + getLoaderManager().restartLoader(LOADER_ID_BOOKMARKS_LIST, bundle, mLoaderCallbacks); 1.124 + } 1.125 + }); 1.126 + mList.setAdapter(mListAdapter); 1.127 + 1.128 + // Create callbacks before the initial loader is started. 1.129 + mLoaderCallbacks = new CursorLoaderCallbacks(); 1.130 + loadIfVisible(); 1.131 + } 1.132 + 1.133 + @Override 1.134 + public void onDestroyView() { 1.135 + mList = null; 1.136 + mListAdapter = null; 1.137 + mEmptyView = null; 1.138 + super.onDestroyView(); 1.139 + } 1.140 + 1.141 + @Override 1.142 + public void onConfigurationChanged(Configuration newConfig) { 1.143 + super.onConfigurationChanged(newConfig); 1.144 + 1.145 + // Reattach the fragment, forcing a reinflation of its view. 1.146 + // We use commitAllowingStateLoss() instead of commit() here to avoid 1.147 + // an IllegalStateException. If the phone is rotated while Fennec 1.148 + // is in the background, onConfigurationChanged() is fired. 1.149 + // onConfigurationChanged() is called before onResume(), so 1.150 + // using commit() would throw an IllegalStateException since it can't 1.151 + // be used between the Activity's onSaveInstanceState() and 1.152 + // onResume(). 1.153 + if (isVisible()) { 1.154 + // The parent stack is saved just so that the folder state can be 1.155 + // restored on rotation. 1.156 + mSavedParentStack = mListAdapter.getParentStack(); 1.157 + 1.158 + getFragmentManager().beginTransaction() 1.159 + .detach(this) 1.160 + .attach(this) 1.161 + .commitAllowingStateLoss(); 1.162 + } 1.163 + } 1.164 + 1.165 + @Override 1.166 + protected void load() { 1.167 + getLoaderManager().initLoader(LOADER_ID_BOOKMARKS_LIST, null, mLoaderCallbacks); 1.168 + } 1.169 + 1.170 + private void updateUiFromCursor(Cursor c) { 1.171 + if ((c == null || c.getCount() == 0) && mEmptyView == null) { 1.172 + // Set empty page view. We delay this so that the empty view won't flash. 1.173 + final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub); 1.174 + mEmptyView = emptyViewStub.inflate(); 1.175 + 1.176 + final ImageView emptyIcon = (ImageView) mEmptyView.findViewById(R.id.home_empty_image); 1.177 + emptyIcon.setImageResource(R.drawable.icon_bookmarks_empty); 1.178 + 1.179 + final TextView emptyText = (TextView) mEmptyView.findViewById(R.id.home_empty_text); 1.180 + emptyText.setText(R.string.home_bookmarks_empty); 1.181 + 1.182 + mList.setEmptyView(mEmptyView); 1.183 + } 1.184 + } 1.185 + 1.186 + /** 1.187 + * Loader for the list for bookmarks. 1.188 + */ 1.189 + private static class BookmarksLoader extends SimpleCursorLoader { 1.190 + private final FolderInfo mFolderInfo; 1.191 + private final RefreshType mRefreshType; 1.192 + 1.193 + public BookmarksLoader(Context context) { 1.194 + this(context, new FolderInfo(Bookmarks.FIXED_ROOT_ID), RefreshType.CHILD); 1.195 + } 1.196 + 1.197 + public BookmarksLoader(Context context, FolderInfo folderInfo, RefreshType refreshType) { 1.198 + super(context); 1.199 + mFolderInfo = folderInfo; 1.200 + mRefreshType = refreshType; 1.201 + } 1.202 + 1.203 + @Override 1.204 + public Cursor loadCursor() { 1.205 + return BrowserDB.getBookmarksInFolder(getContext().getContentResolver(), mFolderInfo.id); 1.206 + } 1.207 + 1.208 + @Override 1.209 + public void onContentChanged() { 1.210 + // Invalidate the cached value that keeps track of whether or 1.211 + // not desktop bookmarks exist. 1.212 + BrowserDB.invalidateCachedState(); 1.213 + super.onContentChanged(); 1.214 + } 1.215 + 1.216 + public FolderInfo getFolderInfo() { 1.217 + return mFolderInfo; 1.218 + } 1.219 + 1.220 + public RefreshType getRefreshType() { 1.221 + return mRefreshType; 1.222 + } 1.223 + } 1.224 + 1.225 + /** 1.226 + * Loader callbacks for the LoaderManager of this fragment. 1.227 + */ 1.228 + private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> { 1.229 + @Override 1.230 + public Loader<Cursor> onCreateLoader(int id, Bundle args) { 1.231 + if (args == null) { 1.232 + return new BookmarksLoader(getActivity()); 1.233 + } else { 1.234 + FolderInfo folderInfo = (FolderInfo) args.getParcelable(BOOKMARKS_FOLDER_INFO); 1.235 + RefreshType refreshType = (RefreshType) args.getParcelable(BOOKMARKS_REFRESH_TYPE); 1.236 + return new BookmarksLoader(getActivity(), folderInfo, refreshType); 1.237 + } 1.238 + } 1.239 + 1.240 + @Override 1.241 + public void onLoadFinished(Loader<Cursor> loader, Cursor c) { 1.242 + BookmarksLoader bl = (BookmarksLoader) loader; 1.243 + mListAdapter.swapCursor(c, bl.getFolderInfo(), bl.getRefreshType()); 1.244 + updateUiFromCursor(c); 1.245 + } 1.246 + 1.247 + @Override 1.248 + public void onLoaderReset(Loader<Cursor> loader) { 1.249 + if (mList != null) { 1.250 + mListAdapter.swapCursor(null); 1.251 + } 1.252 + } 1.253 + } 1.254 +}