mobile/android/base/home/HomeFragment.java

branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
equal deleted inserted replaced
-1:000000000000 0:6fd5576d4eab
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.EditBookmarkDialog;
9 import org.mozilla.gecko.GeckoAppShell;
10 import org.mozilla.gecko.GeckoEvent;
11 import org.mozilla.gecko.GeckoProfile;
12 import org.mozilla.gecko.R;
13 import org.mozilla.gecko.ReaderModeUtils;
14 import org.mozilla.gecko.Tabs;
15 import org.mozilla.gecko.Telemetry;
16 import org.mozilla.gecko.TelemetryContract;
17 import org.mozilla.gecko.db.BrowserContract.Combined;
18 import org.mozilla.gecko.db.BrowserDB;
19 import org.mozilla.gecko.favicons.Favicons;
20 import org.mozilla.gecko.util.ThreadUtils;
21 import org.mozilla.gecko.util.UiAsyncTask;
22
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.res.Configuration;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.support.v4.app.Fragment;
30 import android.util.Log;
31 import android.view.ContextMenu;
32 import android.view.ContextMenu.ContextMenuInfo;
33 import android.view.MenuInflater;
34 import android.view.MenuItem;
35 import android.view.View;
36 import android.widget.Toast;
37
38 /**
39 * HomeFragment is an empty fragment that can be added to the HomePager.
40 * Subclasses can add their own views.
41 */
42 abstract class HomeFragment extends Fragment {
43 // Log Tag.
44 private static final String LOGTAG="GeckoHomeFragment";
45
46 // Share MIME type.
47 protected static final String SHARE_MIME_TYPE = "text/plain";
48
49 // Default value for "can load" hint
50 static final boolean DEFAULT_CAN_LOAD_HINT = false;
51
52 // Whether the fragment can load its content or not
53 // This is used to defer data loading until the editing
54 // mode animation ends.
55 private boolean mCanLoadHint;
56
57 // Whether the fragment has loaded its content
58 private boolean mIsLoaded;
59
60 @Override
61 public void onCreate(Bundle savedInstanceState) {
62 super.onCreate(savedInstanceState);
63
64 final Bundle args = getArguments();
65 if (args != null) {
66 mCanLoadHint = args.getBoolean(HomePager.CAN_LOAD_ARG, DEFAULT_CAN_LOAD_HINT);
67 } else {
68 mCanLoadHint = DEFAULT_CAN_LOAD_HINT;
69 }
70
71 mIsLoaded = false;
72 }
73
74 @Override
75 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
76 if (menuInfo == null || !(menuInfo instanceof HomeContextMenuInfo)) {
77 return;
78 }
79
80 HomeContextMenuInfo info = (HomeContextMenuInfo) menuInfo;
81
82 // Don't show the context menu for folders.
83 if (info.isFolder) {
84 return;
85 }
86
87 MenuInflater inflater = new MenuInflater(view.getContext());
88 inflater.inflate(R.menu.home_contextmenu, menu);
89
90 menu.setHeaderTitle(info.getDisplayTitle());
91
92 // Hide ununsed menu items.
93 menu.findItem(R.id.top_sites_edit).setVisible(false);
94 menu.findItem(R.id.top_sites_pin).setVisible(false);
95 menu.findItem(R.id.top_sites_unpin).setVisible(false);
96
97 // Hide the "Edit" menuitem if this item isn't a bookmark,
98 // or if this is a reading list item.
99 if (!info.hasBookmarkId() || info.isInReadingList()) {
100 menu.findItem(R.id.home_edit_bookmark).setVisible(false);
101 }
102
103 // Hide the "Remove" menuitem if this item not removable.
104 if (!info.canRemove()) {
105 menu.findItem(R.id.home_remove).setVisible(false);
106 }
107
108 menu.findItem(R.id.home_share).setVisible(!GeckoProfile.get(getActivity()).inGuestMode());
109
110 final boolean canOpenInReader = (info.display == Combined.DISPLAY_READER);
111 menu.findItem(R.id.home_open_in_reader).setVisible(canOpenInReader);
112 }
113
114 @Override
115 public boolean onContextItemSelected(MenuItem item) {
116 // onContextItemSelected() is first dispatched to the activity and
117 // then dispatched to its fragments. Since fragments cannot "override"
118 // menu item selection handling, it's better to avoid menu id collisions
119 // between the activity and its fragments.
120
121 ContextMenuInfo menuInfo = item.getMenuInfo();
122 if (menuInfo == null || !(menuInfo instanceof HomeContextMenuInfo)) {
123 return false;
124 }
125
126 final HomeContextMenuInfo info = (HomeContextMenuInfo) menuInfo;
127 final Context context = getActivity();
128
129 final int itemId = item.getItemId();
130
131 // Track the menu action. We don't know much about the context, but we can use this to determine
132 // the frequency of use for various actions.
133 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.CONTEXT_MENU, getResources().getResourceEntryName(itemId));
134
135 if (itemId == R.id.home_share) {
136 if (info.url == null) {
137 Log.e(LOGTAG, "Can't share because URL is null");
138 return false;
139 } else {
140 GeckoAppShell.openUriExternal(info.url, SHARE_MIME_TYPE, "", "",
141 Intent.ACTION_SEND, info.getDisplayTitle());
142
143 // Context: Sharing via chrome homepage contextmenu list (home session should be active)
144 Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
145 return true;
146 }
147 }
148
149 if (itemId == R.id.home_add_to_launcher) {
150 if (info.url == null) {
151 Log.e(LOGTAG, "Can't add to home screen because URL is null");
152 return false;
153 }
154
155 // Fetch an icon big enough for use as a home screen icon.
156 Favicons.getPreferredSizeFaviconForPage(info.url, new GeckoAppShell.CreateShortcutFaviconLoadedListener(info.url, info.getDisplayTitle()));
157 return true;
158 }
159
160 if (itemId == R.id.home_open_private_tab || itemId == R.id.home_open_new_tab) {
161 if (info.url == null) {
162 Log.e(LOGTAG, "Can't open in new tab because URL is null");
163 return false;
164 }
165
166 int flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_BACKGROUND;
167 if (item.getItemId() == R.id.home_open_private_tab)
168 flags |= Tabs.LOADURL_PRIVATE;
169
170 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.CONTEXT_MENU);
171
172 final String url = (info.isInReadingList() ? ReaderModeUtils.getAboutReaderForUrl(info.url) : info.url);
173
174 // Some pinned site items have "user-entered" urls. URLs entered in the PinSiteDialog are wrapped in
175 // a special URI until we can get a valid URL. If the url is a user-entered url, decode the URL before loading it.
176 Tabs.getInstance().loadUrl(decodeUserEnteredUrl(url), flags);
177 Toast.makeText(context, R.string.new_tab_opened, Toast.LENGTH_SHORT).show();
178 return true;
179 }
180
181 if (itemId == R.id.home_edit_bookmark) {
182 // UI Dialog associates to the activity context, not the applications'.
183 new EditBookmarkDialog(context).show(info.url);
184 return true;
185 }
186
187 if (itemId == R.id.home_open_in_reader) {
188 final String url = ReaderModeUtils.getAboutReaderForUrl(info.url);
189 Tabs.getInstance().loadUrl(url, Tabs.LOADURL_NONE);
190 return true;
191 }
192
193 if (itemId == R.id.home_remove) {
194 // Prioritize removing a history entry over a bookmark in the case of a combined item.
195 if (info.hasHistoryId()) {
196 new RemoveHistoryTask(context, info.historyId).execute();
197 return true;
198 }
199
200 if (info.hasBookmarkId()) {
201 new RemoveBookmarkTask(context, info.bookmarkId).execute();
202 return true;
203 }
204
205 if (info.isInReadingList()) {
206 (new RemoveReadingListItemTask(context, info.readingListItemId, info.url)).execute();
207 return true;
208 }
209 }
210
211 return false;
212 }
213
214 @Override
215 public void setUserVisibleHint (boolean isVisibleToUser) {
216 if (isVisibleToUser == getUserVisibleHint()) {
217 return;
218 }
219
220 super.setUserVisibleHint(isVisibleToUser);
221 loadIfVisible();
222 }
223
224 @Override
225 public void onConfigurationChanged(Configuration newConfig) {
226 super.onConfigurationChanged(newConfig);
227 }
228
229 void setCanLoadHint(boolean canLoadHint) {
230 if (mCanLoadHint == canLoadHint) {
231 return;
232 }
233
234 mCanLoadHint = canLoadHint;
235 loadIfVisible();
236 }
237
238 boolean getCanLoadHint() {
239 return mCanLoadHint;
240 }
241
242 /**
243 * Given a url with a user-entered scheme, extract the
244 * scheme-specific component. For e.g, given "user-entered://www.google.com",
245 * this method returns "//www.google.com". If the passed url
246 * does not have a user-entered scheme, the same url will be returned.
247 *
248 * @param url to be decoded
249 * @return url component entered by user
250 */
251 public static String decodeUserEnteredUrl(String url) {
252 Uri uri = Uri.parse(url);
253 if ("user-entered".equals(uri.getScheme())) {
254 return uri.getSchemeSpecificPart();
255 }
256 return url;
257 }
258
259 protected abstract void load();
260
261 protected boolean canLoad() {
262 return (mCanLoadHint && isVisible() && getUserVisibleHint());
263 }
264
265 protected void loadIfVisible() {
266 if (!canLoad() || mIsLoaded) {
267 return;
268 }
269
270 load();
271 mIsLoaded = true;
272 }
273
274 private static class RemoveBookmarkTask extends UiAsyncTask<Void, Void, Void> {
275 private final Context mContext;
276 private final int mId;
277
278 public RemoveBookmarkTask(Context context, int id) {
279 super(ThreadUtils.getBackgroundHandler());
280
281 mContext = context;
282 mId = id;
283 }
284
285 @Override
286 public Void doInBackground(Void... params) {
287 ContentResolver cr = mContext.getContentResolver();
288 BrowserDB.removeBookmark(cr, mId);
289 return null;
290 }
291
292 @Override
293 public void onPostExecute(Void result) {
294 Toast.makeText(mContext, R.string.bookmark_removed, Toast.LENGTH_SHORT).show();
295 }
296 }
297
298
299 private static class RemoveReadingListItemTask extends UiAsyncTask<Void, Void, Void> {
300 private final int mId;
301 private final String mUrl;
302 private final Context mContext;
303
304 public RemoveReadingListItemTask(Context context, int id, String url) {
305 super(ThreadUtils.getBackgroundHandler());
306 mId = id;
307 mUrl = url;
308 mContext = context;
309 }
310
311 @Override
312 public Void doInBackground(Void... params) {
313 ContentResolver cr = mContext.getContentResolver();
314 BrowserDB.removeReadingListItem(cr, mId);
315
316 GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Remove", mUrl);
317 GeckoAppShell.sendEventToGecko(e);
318
319 return null;
320 }
321 }
322
323 private static class RemoveHistoryTask extends UiAsyncTask<Void, Void, Void> {
324 private final Context mContext;
325 private final int mId;
326
327 public RemoveHistoryTask(Context context, int id) {
328 super(ThreadUtils.getBackgroundHandler());
329
330 mContext = context;
331 mId = id;
332 }
333
334 @Override
335 public Void doInBackground(Void... params) {
336 BrowserDB.removeHistoryEntry(mContext.getContentResolver(), mId);
337 return null;
338 }
339
340 @Override
341 public void onPostExecute(Void result) {
342 Toast.makeText(mContext, R.string.history_removed, Toast.LENGTH_SHORT).show();
343 }
344 }
345 }

mercurial