Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 package org.mozilla.gecko.home;
8 import java.util.List;
10 import org.mozilla.gecko.R;
11 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
12 import org.mozilla.gecko.db.BrowserDB;
13 import org.mozilla.gecko.home.BookmarksListAdapter.FolderInfo;
14 import org.mozilla.gecko.home.BookmarksListAdapter.OnRefreshFolderListener;
15 import org.mozilla.gecko.home.BookmarksListAdapter.RefreshType;
16 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
18 import android.app.Activity;
19 import android.content.Context;
20 import android.content.res.Configuration;
21 import android.database.Cursor;
22 import android.os.Bundle;
23 import android.support.v4.app.LoaderManager.LoaderCallbacks;
24 import android.support.v4.content.Loader;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.view.ViewStub;
29 import android.widget.ImageView;
30 import android.widget.TextView;
32 /**
33 * A page in about:home that displays a ListView of bookmarks.
34 */
35 public class BookmarksPanel extends HomeFragment {
36 public static final String LOGTAG = "GeckoBookmarksPanel";
38 // Cursor loader ID for list of bookmarks.
39 private static final int LOADER_ID_BOOKMARKS_LIST = 0;
41 // Information about the target bookmarks folder.
42 private static final String BOOKMARKS_FOLDER_INFO = "folder_info";
44 // Refresh type for folder refreshing loader.
45 private static final String BOOKMARKS_REFRESH_TYPE = "refresh_type";
47 // List of bookmarks.
48 private BookmarksListView mList;
50 // Adapter for list of bookmarks.
51 private BookmarksListAdapter mListAdapter;
53 // Adapter's parent stack.
54 private List<FolderInfo> mSavedParentStack;
56 // Reference to the View to display when there are no results.
57 private View mEmptyView;
59 // Callback for cursor loaders.
60 private CursorLoaderCallbacks mLoaderCallbacks;
62 @Override
63 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
64 final View view = inflater.inflate(R.layout.home_bookmarks_panel, container, false);
66 mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list);
68 mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
69 @Override
70 public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
71 final int type = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE));
72 if (type == Bookmarks.TYPE_FOLDER) {
73 // We don't show a context menu for folders
74 return null;
75 }
76 final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
77 info.url = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.URL));
78 info.title = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.TITLE));
79 info.bookmarkId = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID));
80 return info;
81 }
82 });
84 return view;
85 }
87 @Override
88 public void onViewCreated(View view, Bundle savedInstanceState) {
89 super.onViewCreated(view, savedInstanceState);
91 OnUrlOpenListener listener = null;
92 try {
93 listener = (OnUrlOpenListener) getActivity();
94 } catch (ClassCastException e) {
95 throw new ClassCastException(getActivity().toString()
96 + " must implement HomePager.OnUrlOpenListener");
97 }
99 mList.setTag(HomePager.LIST_TAG_BOOKMARKS);
100 mList.setOnUrlOpenListener(listener);
102 registerForContextMenu(mList);
103 }
105 @Override
106 public void onActivityCreated(Bundle savedInstanceState) {
107 super.onActivityCreated(savedInstanceState);
109 final Activity activity = getActivity();
111 // Setup the list adapter.
112 mListAdapter = new BookmarksListAdapter(activity, null, mSavedParentStack);
113 mListAdapter.setOnRefreshFolderListener(new OnRefreshFolderListener() {
114 @Override
115 public void onRefreshFolder(FolderInfo folderInfo, RefreshType refreshType) {
116 // Restart the loader with folder as the argument.
117 Bundle bundle = new Bundle();
118 bundle.putParcelable(BOOKMARKS_FOLDER_INFO, folderInfo);
119 bundle.putParcelable(BOOKMARKS_REFRESH_TYPE, refreshType);
120 getLoaderManager().restartLoader(LOADER_ID_BOOKMARKS_LIST, bundle, mLoaderCallbacks);
121 }
122 });
123 mList.setAdapter(mListAdapter);
125 // Create callbacks before the initial loader is started.
126 mLoaderCallbacks = new CursorLoaderCallbacks();
127 loadIfVisible();
128 }
130 @Override
131 public void onDestroyView() {
132 mList = null;
133 mListAdapter = null;
134 mEmptyView = null;
135 super.onDestroyView();
136 }
138 @Override
139 public void onConfigurationChanged(Configuration newConfig) {
140 super.onConfigurationChanged(newConfig);
142 // Reattach the fragment, forcing a reinflation of its view.
143 // We use commitAllowingStateLoss() instead of commit() here to avoid
144 // an IllegalStateException. If the phone is rotated while Fennec
145 // is in the background, onConfigurationChanged() is fired.
146 // onConfigurationChanged() is called before onResume(), so
147 // using commit() would throw an IllegalStateException since it can't
148 // be used between the Activity's onSaveInstanceState() and
149 // onResume().
150 if (isVisible()) {
151 // The parent stack is saved just so that the folder state can be
152 // restored on rotation.
153 mSavedParentStack = mListAdapter.getParentStack();
155 getFragmentManager().beginTransaction()
156 .detach(this)
157 .attach(this)
158 .commitAllowingStateLoss();
159 }
160 }
162 @Override
163 protected void load() {
164 getLoaderManager().initLoader(LOADER_ID_BOOKMARKS_LIST, null, mLoaderCallbacks);
165 }
167 private void updateUiFromCursor(Cursor c) {
168 if ((c == null || c.getCount() == 0) && mEmptyView == null) {
169 // Set empty page view. We delay this so that the empty view won't flash.
170 final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub);
171 mEmptyView = emptyViewStub.inflate();
173 final ImageView emptyIcon = (ImageView) mEmptyView.findViewById(R.id.home_empty_image);
174 emptyIcon.setImageResource(R.drawable.icon_bookmarks_empty);
176 final TextView emptyText = (TextView) mEmptyView.findViewById(R.id.home_empty_text);
177 emptyText.setText(R.string.home_bookmarks_empty);
179 mList.setEmptyView(mEmptyView);
180 }
181 }
183 /**
184 * Loader for the list for bookmarks.
185 */
186 private static class BookmarksLoader extends SimpleCursorLoader {
187 private final FolderInfo mFolderInfo;
188 private final RefreshType mRefreshType;
190 public BookmarksLoader(Context context) {
191 this(context, new FolderInfo(Bookmarks.FIXED_ROOT_ID), RefreshType.CHILD);
192 }
194 public BookmarksLoader(Context context, FolderInfo folderInfo, RefreshType refreshType) {
195 super(context);
196 mFolderInfo = folderInfo;
197 mRefreshType = refreshType;
198 }
200 @Override
201 public Cursor loadCursor() {
202 return BrowserDB.getBookmarksInFolder(getContext().getContentResolver(), mFolderInfo.id);
203 }
205 @Override
206 public void onContentChanged() {
207 // Invalidate the cached value that keeps track of whether or
208 // not desktop bookmarks exist.
209 BrowserDB.invalidateCachedState();
210 super.onContentChanged();
211 }
213 public FolderInfo getFolderInfo() {
214 return mFolderInfo;
215 }
217 public RefreshType getRefreshType() {
218 return mRefreshType;
219 }
220 }
222 /**
223 * Loader callbacks for the LoaderManager of this fragment.
224 */
225 private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
226 @Override
227 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
228 if (args == null) {
229 return new BookmarksLoader(getActivity());
230 } else {
231 FolderInfo folderInfo = (FolderInfo) args.getParcelable(BOOKMARKS_FOLDER_INFO);
232 RefreshType refreshType = (RefreshType) args.getParcelable(BOOKMARKS_REFRESH_TYPE);
233 return new BookmarksLoader(getActivity(), folderInfo, refreshType);
234 }
235 }
237 @Override
238 public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
239 BookmarksLoader bl = (BookmarksLoader) loader;
240 mListAdapter.swapCursor(c, bl.getFolderInfo(), bl.getRefreshType());
241 updateUiFromCursor(c);
242 }
244 @Override
245 public void onLoaderReset(Loader<Cursor> loader) {
246 if (mList != null) {
247 mListAdapter.swapCursor(null);
248 }
249 }
250 }
251 }