mobile/android/base/home/HomeFragment.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial