mobile/android/base/home/HomeFragment.java

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

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 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;
    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;
    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";
    46     // Share MIME type.
    47     protected static final String SHARE_MIME_TYPE = "text/plain";
    49     // Default value for "can load" hint
    50     static final boolean DEFAULT_CAN_LOAD_HINT = false;
    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;
    57     // Whether the fragment has loaded its content
    58     private boolean mIsLoaded;
    60     @Override
    61     public void onCreate(Bundle savedInstanceState) {
    62         super.onCreate(savedInstanceState);
    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         }
    71         mIsLoaded = false;
    72     }
    74     @Override
    75     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
    76         if (menuInfo == null || !(menuInfo instanceof HomeContextMenuInfo)) {
    77             return;
    78         }
    80         HomeContextMenuInfo info = (HomeContextMenuInfo) menuInfo;
    82         // Don't show the context menu for folders.
    83         if (info.isFolder) {
    84             return;
    85         }
    87         MenuInflater inflater = new MenuInflater(view.getContext());
    88         inflater.inflate(R.menu.home_contextmenu, menu);
    90         menu.setHeaderTitle(info.getDisplayTitle());
    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);
    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         }
   103         // Hide the "Remove" menuitem if this item not removable.
   104         if (!info.canRemove()) {
   105             menu.findItem(R.id.home_remove).setVisible(false);
   106         }
   108         menu.findItem(R.id.home_share).setVisible(!GeckoProfile.get(getActivity()).inGuestMode());
   110         final boolean canOpenInReader = (info.display == Combined.DISPLAY_READER);
   111         menu.findItem(R.id.home_open_in_reader).setVisible(canOpenInReader);
   112     }
   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.
   121         ContextMenuInfo menuInfo = item.getMenuInfo();
   122         if (menuInfo == null || !(menuInfo instanceof HomeContextMenuInfo)) {
   123             return false;
   124         }
   126         final HomeContextMenuInfo info = (HomeContextMenuInfo) menuInfo;
   127         final Context context = getActivity();
   129         final int itemId = item.getItemId();
   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));
   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());
   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         }
   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             }
   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         }
   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             }
   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;
   170             Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.CONTEXT_MENU);
   172             final String url = (info.isInReadingList() ? ReaderModeUtils.getAboutReaderForUrl(info.url) : info.url);
   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         }
   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         }
   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         }
   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             }
   200             if (info.hasBookmarkId()) {
   201                 new RemoveBookmarkTask(context, info.bookmarkId).execute();
   202                 return true;
   203             }
   205             if (info.isInReadingList()) {
   206                 (new RemoveReadingListItemTask(context, info.readingListItemId, info.url)).execute();
   207                 return true;
   208             }
   209         }
   211         return false;
   212     }
   214     @Override
   215     public void setUserVisibleHint (boolean isVisibleToUser) {
   216         if (isVisibleToUser == getUserVisibleHint()) {
   217             return;
   218         }
   220         super.setUserVisibleHint(isVisibleToUser);
   221         loadIfVisible();
   222     }
   224     @Override
   225     public void onConfigurationChanged(Configuration newConfig) {
   226         super.onConfigurationChanged(newConfig);
   227     }
   229     void setCanLoadHint(boolean canLoadHint) {
   230         if (mCanLoadHint == canLoadHint) {
   231             return;
   232         }
   234         mCanLoadHint = canLoadHint;
   235         loadIfVisible();
   236     }
   238     boolean getCanLoadHint() {
   239         return mCanLoadHint;
   240     }
   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     }
   259     protected abstract void load();
   261     protected boolean canLoad() {
   262         return (mCanLoadHint && isVisible() && getUserVisibleHint());
   263     }
   265     protected void loadIfVisible() {
   266         if (!canLoad() || mIsLoaded) {
   267             return;
   268         }
   270         load();
   271         mIsLoaded = true;
   272     }
   274     private static class RemoveBookmarkTask extends UiAsyncTask<Void, Void, Void> {
   275         private final Context mContext;
   276         private final int mId;
   278         public RemoveBookmarkTask(Context context, int id) {
   279             super(ThreadUtils.getBackgroundHandler());
   281             mContext = context;
   282             mId = id;
   283         }
   285         @Override
   286         public Void doInBackground(Void... params) {
   287             ContentResolver cr = mContext.getContentResolver();
   288             BrowserDB.removeBookmark(cr, mId);
   289             return null;
   290         }
   292         @Override
   293         public void onPostExecute(Void result) {
   294             Toast.makeText(mContext, R.string.bookmark_removed, Toast.LENGTH_SHORT).show();
   295         }
   296     }
   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;
   304         public RemoveReadingListItemTask(Context context, int id, String url) {
   305             super(ThreadUtils.getBackgroundHandler());
   306             mId = id;
   307             mUrl = url;
   308             mContext = context;
   309         }
   311         @Override
   312         public Void doInBackground(Void... params) {
   313             ContentResolver cr = mContext.getContentResolver();
   314             BrowserDB.removeReadingListItem(cr, mId);
   316             GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Remove", mUrl);
   317             GeckoAppShell.sendEventToGecko(e);
   319             return null;
   320         }
   321     }
   323     private static class RemoveHistoryTask extends UiAsyncTask<Void, Void, Void> {
   324         private final Context mContext;
   325         private final int mId;
   327         public RemoveHistoryTask(Context context, int id) {
   328             super(ThreadUtils.getBackgroundHandler());
   330             mContext = context;
   331             mId = id;
   332         }
   334         @Override
   335         public Void doInBackground(Void... params) {
   336             BrowserDB.removeHistoryEntry(mContext.getContentResolver(), mId);
   337             return null;
   338         }
   340         @Override
   341         public void onPostExecute(Void result) {
   342             Toast.makeText(mContext, R.string.history_removed, Toast.LENGTH_SHORT).show();
   343         }
   344     }
   345 }

mercurial