|
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/. */ |
|
5 |
|
6 package org.mozilla.gecko.home; |
|
7 |
|
8 import java.util.List; |
|
9 |
|
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; |
|
17 |
|
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; |
|
31 |
|
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"; |
|
37 |
|
38 // Cursor loader ID for list of bookmarks. |
|
39 private static final int LOADER_ID_BOOKMARKS_LIST = 0; |
|
40 |
|
41 // Information about the target bookmarks folder. |
|
42 private static final String BOOKMARKS_FOLDER_INFO = "folder_info"; |
|
43 |
|
44 // Refresh type for folder refreshing loader. |
|
45 private static final String BOOKMARKS_REFRESH_TYPE = "refresh_type"; |
|
46 |
|
47 // List of bookmarks. |
|
48 private BookmarksListView mList; |
|
49 |
|
50 // Adapter for list of bookmarks. |
|
51 private BookmarksListAdapter mListAdapter; |
|
52 |
|
53 // Adapter's parent stack. |
|
54 private List<FolderInfo> mSavedParentStack; |
|
55 |
|
56 // Reference to the View to display when there are no results. |
|
57 private View mEmptyView; |
|
58 |
|
59 // Callback for cursor loaders. |
|
60 private CursorLoaderCallbacks mLoaderCallbacks; |
|
61 |
|
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); |
|
65 |
|
66 mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list); |
|
67 |
|
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 }); |
|
83 |
|
84 return view; |
|
85 } |
|
86 |
|
87 @Override |
|
88 public void onViewCreated(View view, Bundle savedInstanceState) { |
|
89 super.onViewCreated(view, savedInstanceState); |
|
90 |
|
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 } |
|
98 |
|
99 mList.setTag(HomePager.LIST_TAG_BOOKMARKS); |
|
100 mList.setOnUrlOpenListener(listener); |
|
101 |
|
102 registerForContextMenu(mList); |
|
103 } |
|
104 |
|
105 @Override |
|
106 public void onActivityCreated(Bundle savedInstanceState) { |
|
107 super.onActivityCreated(savedInstanceState); |
|
108 |
|
109 final Activity activity = getActivity(); |
|
110 |
|
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); |
|
124 |
|
125 // Create callbacks before the initial loader is started. |
|
126 mLoaderCallbacks = new CursorLoaderCallbacks(); |
|
127 loadIfVisible(); |
|
128 } |
|
129 |
|
130 @Override |
|
131 public void onDestroyView() { |
|
132 mList = null; |
|
133 mListAdapter = null; |
|
134 mEmptyView = null; |
|
135 super.onDestroyView(); |
|
136 } |
|
137 |
|
138 @Override |
|
139 public void onConfigurationChanged(Configuration newConfig) { |
|
140 super.onConfigurationChanged(newConfig); |
|
141 |
|
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(); |
|
154 |
|
155 getFragmentManager().beginTransaction() |
|
156 .detach(this) |
|
157 .attach(this) |
|
158 .commitAllowingStateLoss(); |
|
159 } |
|
160 } |
|
161 |
|
162 @Override |
|
163 protected void load() { |
|
164 getLoaderManager().initLoader(LOADER_ID_BOOKMARKS_LIST, null, mLoaderCallbacks); |
|
165 } |
|
166 |
|
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(); |
|
172 |
|
173 final ImageView emptyIcon = (ImageView) mEmptyView.findViewById(R.id.home_empty_image); |
|
174 emptyIcon.setImageResource(R.drawable.icon_bookmarks_empty); |
|
175 |
|
176 final TextView emptyText = (TextView) mEmptyView.findViewById(R.id.home_empty_text); |
|
177 emptyText.setText(R.string.home_bookmarks_empty); |
|
178 |
|
179 mList.setEmptyView(mEmptyView); |
|
180 } |
|
181 } |
|
182 |
|
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; |
|
189 |
|
190 public BookmarksLoader(Context context) { |
|
191 this(context, new FolderInfo(Bookmarks.FIXED_ROOT_ID), RefreshType.CHILD); |
|
192 } |
|
193 |
|
194 public BookmarksLoader(Context context, FolderInfo folderInfo, RefreshType refreshType) { |
|
195 super(context); |
|
196 mFolderInfo = folderInfo; |
|
197 mRefreshType = refreshType; |
|
198 } |
|
199 |
|
200 @Override |
|
201 public Cursor loadCursor() { |
|
202 return BrowserDB.getBookmarksInFolder(getContext().getContentResolver(), mFolderInfo.id); |
|
203 } |
|
204 |
|
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 } |
|
212 |
|
213 public FolderInfo getFolderInfo() { |
|
214 return mFolderInfo; |
|
215 } |
|
216 |
|
217 public RefreshType getRefreshType() { |
|
218 return mRefreshType; |
|
219 } |
|
220 } |
|
221 |
|
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 } |
|
236 |
|
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 } |
|
243 |
|
244 @Override |
|
245 public void onLoaderReset(Loader<Cursor> loader) { |
|
246 if (mList != null) { |
|
247 mListAdapter.swapCursor(null); |
|
248 } |
|
249 } |
|
250 } |
|
251 } |