| |
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 org.mozilla.gecko.AboutPages; |
| |
9 import org.mozilla.gecko.GeckoProfile; |
| |
10 import org.mozilla.gecko.R; |
| |
11 import org.mozilla.gecko.SessionParser; |
| |
12 import org.mozilla.gecko.Telemetry; |
| |
13 import org.mozilla.gecko.TelemetryContract; |
| |
14 import org.mozilla.gecko.db.BrowserContract.Combined; |
| |
15 import org.mozilla.gecko.home.HomePager.OnNewTabsListener; |
| |
16 |
| |
17 import android.app.Activity; |
| |
18 import android.content.Context; |
| |
19 import android.database.Cursor; |
| |
20 import android.database.MatrixCursor; |
| |
21 import android.database.MatrixCursor.RowBuilder; |
| |
22 import android.os.Bundle; |
| |
23 import android.support.v4.app.LoaderManager.LoaderCallbacks; |
| |
24 import android.support.v4.content.Loader; |
| |
25 import android.support.v4.widget.CursorAdapter; |
| |
26 import android.view.LayoutInflater; |
| |
27 import android.view.View; |
| |
28 import android.view.ViewGroup; |
| |
29 import android.view.ViewStub; |
| |
30 import android.widget.AdapterView; |
| |
31 import android.widget.ImageView; |
| |
32 import android.widget.TextView; |
| |
33 |
| |
34 /** |
| |
35 * Fragment that displays tabs from last session in a ListView. |
| |
36 */ |
| |
37 public class LastTabsPanel extends HomeFragment { |
| |
38 // Logging tag name |
| |
39 private static final String LOGTAG = "GeckoLastTabsPanel"; |
| |
40 |
| |
41 // Cursor loader ID for the session parser |
| |
42 private static final int LOADER_ID_LAST_TABS = 0; |
| |
43 |
| |
44 // Adapter for the list of search results |
| |
45 private LastTabsAdapter mAdapter; |
| |
46 |
| |
47 // The view shown by the fragment. |
| |
48 private HomeListView mList; |
| |
49 |
| |
50 // The title for this HomeFragment panel. |
| |
51 private TextView mTitle; |
| |
52 |
| |
53 // The button view for restoring tabs from last session. |
| |
54 private View mRestoreButton; |
| |
55 |
| |
56 // Reference to the View to display when there are no results. |
| |
57 private View mEmptyView; |
| |
58 |
| |
59 // Callbacks used for the search and favicon cursor loaders |
| |
60 private CursorLoaderCallbacks mCursorLoaderCallbacks; |
| |
61 |
| |
62 // On new tabs listener |
| |
63 private OnNewTabsListener mNewTabsListener; |
| |
64 |
| |
65 public static LastTabsPanel newInstance() { |
| |
66 return new LastTabsPanel(); |
| |
67 } |
| |
68 |
| |
69 public LastTabsPanel() { |
| |
70 mNewTabsListener = null; |
| |
71 } |
| |
72 |
| |
73 @Override |
| |
74 public void onAttach(Activity activity) { |
| |
75 super.onAttach(activity); |
| |
76 |
| |
77 try { |
| |
78 mNewTabsListener = (OnNewTabsListener) activity; |
| |
79 } catch (ClassCastException e) { |
| |
80 throw new ClassCastException(activity.toString() |
| |
81 + " must implement HomePager.OnNewTabsListener"); |
| |
82 } |
| |
83 } |
| |
84 |
| |
85 @Override |
| |
86 public void onDetach() { |
| |
87 super.onDetach(); |
| |
88 |
| |
89 mNewTabsListener = null; |
| |
90 } |
| |
91 |
| |
92 @Override |
| |
93 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { |
| |
94 return inflater.inflate(R.layout.home_last_tabs_panel, container, false); |
| |
95 } |
| |
96 |
| |
97 @Override |
| |
98 public void onViewCreated(View view, Bundle savedInstanceState) { |
| |
99 mTitle = (TextView) view.findViewById(R.id.title); |
| |
100 if (mTitle != null) { |
| |
101 mTitle.setText(R.string.home_last_tabs_title); |
| |
102 } |
| |
103 |
| |
104 mList = (HomeListView) view.findViewById(R.id.list); |
| |
105 mList.setTag(HomePager.LIST_TAG_LAST_TABS); |
| |
106 |
| |
107 mList.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
| |
108 @Override |
| |
109 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
| |
110 final Cursor c = mAdapter.getCursor(); |
| |
111 if (c == null || !c.moveToPosition(position)) { |
| |
112 return; |
| |
113 } |
| |
114 |
| |
115 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL); |
| |
116 |
| |
117 final String url = c.getString(c.getColumnIndexOrThrow(Combined.URL)); |
| |
118 mNewTabsListener.onNewTabs(new String[] { url }); |
| |
119 } |
| |
120 }); |
| |
121 |
| |
122 mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() { |
| |
123 @Override |
| |
124 public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) { |
| |
125 final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id); |
| |
126 info.url = cursor.getString(cursor.getColumnIndexOrThrow(Combined.URL)); |
| |
127 info.title = cursor.getString(cursor.getColumnIndexOrThrow(Combined.TITLE)); |
| |
128 return info; |
| |
129 } |
| |
130 }); |
| |
131 |
| |
132 registerForContextMenu(mList); |
| |
133 |
| |
134 mRestoreButton = view.findViewById(R.id.open_all_tabs_button); |
| |
135 mRestoreButton.setOnClickListener(new View.OnClickListener() { |
| |
136 @Override |
| |
137 public void onClick(View v) { |
| |
138 openAllTabs(); |
| |
139 } |
| |
140 }); |
| |
141 } |
| |
142 |
| |
143 @Override |
| |
144 public void onDestroyView() { |
| |
145 super.onDestroyView(); |
| |
146 mList = null; |
| |
147 mTitle = null; |
| |
148 mEmptyView = null; |
| |
149 mRestoreButton = null; |
| |
150 } |
| |
151 |
| |
152 @Override |
| |
153 public void onActivityCreated(Bundle savedInstanceState) { |
| |
154 super.onActivityCreated(savedInstanceState); |
| |
155 |
| |
156 // Intialize adapter |
| |
157 mAdapter = new LastTabsAdapter(getActivity()); |
| |
158 mList.setAdapter(mAdapter); |
| |
159 |
| |
160 // Create callbacks before the initial loader is started |
| |
161 mCursorLoaderCallbacks = new CursorLoaderCallbacks(); |
| |
162 loadIfVisible(); |
| |
163 } |
| |
164 |
| |
165 private void updateUiFromCursor(Cursor c) { |
| |
166 if (c != null && c.getCount() > 0) { |
| |
167 if (mTitle != null) { |
| |
168 mTitle.setVisibility(View.VISIBLE); |
| |
169 } |
| |
170 mRestoreButton.setVisibility(View.VISIBLE); |
| |
171 return; |
| |
172 } |
| |
173 |
| |
174 // Cursor is empty, so hide the title and set the |
| |
175 // empty view if it hasn't been set already. |
| |
176 if (mTitle != null) { |
| |
177 mTitle.setVisibility(View.GONE); |
| |
178 } |
| |
179 mRestoreButton.setVisibility(View.GONE); |
| |
180 |
| |
181 if (mEmptyView == null) { |
| |
182 // Set empty panel view. We delay this so that the empty view won't flash. |
| |
183 final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub); |
| |
184 mEmptyView = emptyViewStub.inflate(); |
| |
185 |
| |
186 final ImageView emptyIcon = (ImageView) mEmptyView.findViewById(R.id.home_empty_image); |
| |
187 emptyIcon.setImageResource(R.drawable.icon_last_tabs_empty); |
| |
188 |
| |
189 final TextView emptyText = (TextView) mEmptyView.findViewById(R.id.home_empty_text); |
| |
190 emptyText.setText(R.string.home_last_tabs_empty); |
| |
191 |
| |
192 mList.setEmptyView(mEmptyView); |
| |
193 } |
| |
194 } |
| |
195 |
| |
196 @Override |
| |
197 protected void load() { |
| |
198 getLoaderManager().initLoader(LOADER_ID_LAST_TABS, null, mCursorLoaderCallbacks); |
| |
199 } |
| |
200 |
| |
201 private void openAllTabs() { |
| |
202 final Cursor c = mAdapter.getCursor(); |
| |
203 if (c == null || !c.moveToFirst()) { |
| |
204 return; |
| |
205 } |
| |
206 |
| |
207 final String[] urls = new String[c.getCount()]; |
| |
208 |
| |
209 do { |
| |
210 urls[c.getPosition()] = c.getString(c.getColumnIndexOrThrow(Combined.URL)); |
| |
211 } while (c.moveToNext()); |
| |
212 |
| |
213 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.BUTTON); |
| |
214 |
| |
215 mNewTabsListener.onNewTabs(urls); |
| |
216 } |
| |
217 |
| |
218 private static class LastTabsCursorLoader extends SimpleCursorLoader { |
| |
219 public LastTabsCursorLoader(Context context) { |
| |
220 super(context); |
| |
221 } |
| |
222 |
| |
223 @Override |
| |
224 public Cursor loadCursor() { |
| |
225 final Context context = getContext(); |
| |
226 |
| |
227 final String jsonString = GeckoProfile.get(context).readSessionFile(true); |
| |
228 if (jsonString == null) { |
| |
229 // No previous session data |
| |
230 return null; |
| |
231 } |
| |
232 |
| |
233 final MatrixCursor c = new MatrixCursor(new String[] { Combined._ID, |
| |
234 Combined.URL, |
| |
235 Combined.TITLE }); |
| |
236 |
| |
237 new SessionParser() { |
| |
238 @Override |
| |
239 public void onTabRead(SessionTab tab) { |
| |
240 final String url = tab.getUrl(); |
| |
241 |
| |
242 // Don't show last tabs for about:home |
| |
243 if (AboutPages.isAboutHome(url)) { |
| |
244 return; |
| |
245 } |
| |
246 |
| |
247 final RowBuilder row = c.newRow(); |
| |
248 row.add(-1); |
| |
249 row.add(url); |
| |
250 |
| |
251 final String title = tab.getTitle(); |
| |
252 row.add(title); |
| |
253 } |
| |
254 }.parse(jsonString); |
| |
255 |
| |
256 return c; |
| |
257 } |
| |
258 } |
| |
259 |
| |
260 private static class LastTabsAdapter extends CursorAdapter { |
| |
261 public LastTabsAdapter(Context context) { |
| |
262 super(context, null, 0); |
| |
263 } |
| |
264 |
| |
265 @Override |
| |
266 public void bindView(View view, Context context, Cursor cursor) { |
| |
267 ((TwoLinePageRow) view).updateFromCursor(cursor); |
| |
268 } |
| |
269 |
| |
270 @Override |
| |
271 public View newView(Context context, Cursor cursor, ViewGroup parent) { |
| |
272 return LayoutInflater.from(context).inflate(R.layout.home_item_row, parent, false); |
| |
273 } |
| |
274 } |
| |
275 |
| |
276 private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> { |
| |
277 @Override |
| |
278 public Loader<Cursor> onCreateLoader(int id, Bundle args) { |
| |
279 return new LastTabsCursorLoader(getActivity()); |
| |
280 } |
| |
281 |
| |
282 @Override |
| |
283 public void onLoadFinished(Loader<Cursor> loader, Cursor c) { |
| |
284 mAdapter.swapCursor(c); |
| |
285 updateUiFromCursor(c); |
| |
286 } |
| |
287 |
| |
288 @Override |
| |
289 public void onLoaderReset(Loader<Cursor> loader) { |
| |
290 mAdapter.swapCursor(null); |
| |
291 } |
| |
292 } |
| |
293 } |