Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: mobile/android/base/BrowserApp.java

Issue 29322687: Issue 2807 - MOBILE3901_2015070622_RELBRANCH merge conflicts (Closed)
Patch Set: Created July 20, 2015, 4:53 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: mobile/android/base/BrowserApp.java
diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java
index fbdc52ce3f56bc07c406a6bd0a2ef0f0a4f5300f..3c729296fdedd10ae2496f13ef9d6164770880a1 100644
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -5,22 +5,6 @@
package org.mozilla.gecko;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.lang.Override;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.net.URLEncoder;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Vector;
-
-import android.support.v4.app.Fragment;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.DynamicToolbar.PinReason;
import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
@@ -30,7 +14,6 @@ import org.mozilla.gecko.animation.PropertyAnimator;
import org.mozilla.gecko.animation.TransitionsTracker;
import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.db.BrowserContract.Combined;
-import org.mozilla.gecko.db.BrowserContract.SearchHistory;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.SuggestedSites;
import org.mozilla.gecko.distribution.Distribution;
@@ -38,6 +21,7 @@ import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.favicons.LoadFaviconTask;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
import org.mozilla.gecko.favicons.decoders.IconDirectoryEntry;
+import org.mozilla.gecko.firstrun.FirstrunPane;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
import org.mozilla.gecko.gfx.BitmapUtils;
@@ -54,22 +38,24 @@ import org.mozilla.gecko.home.HomePager;
import org.mozilla.gecko.home.HomePager.OnUrlOpenInBackgroundListener;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.home.HomePanelsManager;
-import org.mozilla.gecko.home.TopSitesPanel;
import org.mozilla.gecko.home.SearchEngine;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.GeckoMenuItem;
import org.mozilla.gecko.mozglue.ContextUtils;
+import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
+import org.mozilla.gecko.mozglue.RobocopTarget;
+import org.mozilla.gecko.overlays.ui.ShareDialog;
import org.mozilla.gecko.preferences.ClearOnShutdownPref;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.prompts.Prompt;
import org.mozilla.gecko.prompts.PromptListItem;
import org.mozilla.gecko.sync.setup.SyncAccounts;
+import org.mozilla.gecko.tabqueue.TabQueueHelper;
import org.mozilla.gecko.tabs.TabHistoryController;
+import org.mozilla.gecko.tabs.TabHistoryController.OnShowTabHistory;
import org.mozilla.gecko.tabs.TabHistoryFragment;
import org.mozilla.gecko.tabs.TabHistoryPage;
import org.mozilla.gecko.tabs.TabsPanel;
-import org.mozilla.gecko.tabs.TabHistoryController.OnShowTabHistory;
-import org.mozilla.gecko.tiles.TilesRecorder;
import org.mozilla.gecko.toolbar.AutocompleteHandler;
import org.mozilla.gecko.toolbar.BrowserToolbar;
import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
@@ -93,8 +79,7 @@ import org.mozilla.gecko.widget.GeckoActionProvider;
import android.app.Activity;
import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
-import android.content.ContentValues;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -116,11 +101,12 @@ import android.nfc.NfcEvent;
import android.os.Build;
import android.os.Bundle;
import android.os.StrictMode;
-import android.support.v4.app.DialogFragment;
+import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
-import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Base64;
+import android.util.Base64OutputStream;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -140,6 +126,20 @@ import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import android.widget.ViewFlipper;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URLEncoder;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Vector;
public class BrowserApp extends GeckoApp
implements TabsPanel.TabsLayoutChangeListener,
@@ -154,6 +154,8 @@ public class BrowserApp extends GeckoApp
LayoutInflater.Factory {
private static final String LOGTAG = "GeckoBrowserApp";
+ private static final boolean ZOOMED_VIEW_ENABLED = AppConstants.NIGHTLY_BUILD;
+
private static final int TABS_ANIMATION_DURATION = 450;
private static final String ADD_SHORTCUT_TOAST = "add_shortcut_toast";
@@ -162,12 +164,12 @@ public class BrowserApp extends GeckoApp
private static final String STATE_ABOUT_HOME_TOP_PADDING = "abouthome_top_padding";
private static final String BROWSER_SEARCH_TAG = "browser_search";
- private static final String ONBOARD_STARTPANE_TAG = "startpane_dialog";
// Request ID for startActivityForResult.
private static final int ACTIVITY_REQUEST_PREFERENCES = 1001;
- public static final String PREF_STARTPANE_ENABLED = "startpane_enabled";
+ @RobocopTarget
+ public static final String EXTRA_SKIP_STARTPANE = "skipstartpane";
private BrowserSearch mBrowserSearch;
private View mBrowserSearchContainer;
@@ -179,17 +181,19 @@ public class BrowserApp extends GeckoApp
// We can't name the TabStrip class because it's not included on API 9.
private Refreshable mTabStrip;
private ToolbarProgressView mProgressView;
+ private FirstrunPane mFirstrunPane;
private HomePager mHomePager;
private TabsPanel mTabsPanel;
private ViewGroup mHomePagerContainer;
- protected Telemetry.Timer mAboutHomeStartupTimer;
private ActionModeCompat mActionMode;
private boolean mHideDynamicToolbarOnActionModeEnd;
private TabHistoryController tabHistoryController;
+ private ZoomedView mZoomedView;
private static final int GECKO_TOOLS_MENU = -1;
private static final int ADDON_MENU_OFFSET = 1000;
public static final String TAB_HISTORY_FRAGMENT_TAG = "tabHistoryFragment";
+
private static class MenuItemInfo {
public int id;
public String label;
@@ -237,8 +241,6 @@ public class BrowserApp extends GeckoApp
private OrderedBroadcastHelper mOrderedBroadcastHelper;
- private BroadcastReceiver mOnboardingReceiver;
-
private BrowserHealthReporter mBrowserHealthReporter;
private ReadingListHelper mReadingListHelper;
@@ -256,6 +258,175 @@ public class BrowserApp extends GeckoApp
private final DynamicToolbar mDynamicToolbar = new DynamicToolbar();
+ private DragHelper mDragHelper;
+
+ private class DragHelper implements OuterLayout.DragCallback {
+ private int[] mToolbarLocation = new int[2]; // to avoid creation every time we need to check for toolbar location.
+ // When dragging horizontally, the area of mainlayout between left drag bound and right drag bound can
+ // be dragged. A touch on the right of that area will automatically close the view.
+ private int mStatusBarHeight;
+
+ public DragHelper() {
+ // If a layout round happens from the root, the offset placed by viewdraghelper gets forgotten and
+ // main layout gets replaced to offset 0.
+ ((MainLayout) mMainLayout).setLayoutInterceptor(new LayoutInterceptor() {
+ @Override
+ public void onLayout() {
+ if (mRootLayout.isMoving()) {
+ mRootLayout.restoreTargetViewPosition();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onDragProgress(float progress) {
+ mBrowserToolbar.setToolBarButtonsAlpha(1.0f - progress);
+ mTabsPanel.translateInRange(progress);
+ }
+
+ @Override
+ public View getViewToDrag() {
+ return mMainLayout;
+ }
+
+ /**
+ * Since pressing the tabs button slides the main layout, whereas draghelper changes its offset, here we
+ * restore the position of mainlayout as if it was opened by pressing the button. This allows the closing
+ * mechanism to work.
+ */
+ @Override
+ public void startDrag(boolean wasOpen) {
+ if (wasOpen) {
+ mTabsPanel.setHWLayerEnabled(true);
+ mMainLayout.offsetTopAndBottom(getDragRange());
+ mMainLayout.scrollTo(0, 0);
+ } else {
+ prepareTabsToShow();
+ mBrowserToolbar.hideVirtualKeyboard();
+ }
+ mBrowserToolbar.setContextMenuEnabled(false);
+ }
+
+ @Override
+ public void stopDrag(boolean stoppingToOpen) {
+ if (stoppingToOpen) {
+ mTabsPanel.setHWLayerEnabled(false);
+ mMainLayout.offsetTopAndBottom(-getDragRange());
+ mMainLayout.scrollTo(0, -getDragRange());
+ } else {
+ mTabsPanel.hideImmediately();
+ mTabsPanel.setHWLayerEnabled(false);
+ }
+ // Re-enabling context menu only while stopping to close.
+ if (stoppingToOpen) {
+ mBrowserToolbar.setContextMenuEnabled(false);
+ } else {
+ mBrowserToolbar.setContextMenuEnabled(true);
+ }
+ }
+
+ @Override
+ public int getDragRange() {
+ return mTabsPanel.getVerticalPanelHeight();
+ }
+
+ @Override
+ public int getOrderedChildIndex(int index) {
+ // See ViewDragHelper's findTopChildUnder method. ViewDragHelper looks for the topmost view in z order
+ // to understand what needs to be dragged. Here we are tampering Toast's index in case it's hidden,
+ // otherwise draghelper would try to drag it.
+ int mainLayoutIndex = mRootLayout.indexOfChild(mMainLayout);
+ if (index > mainLayoutIndex && (mToast == null || !mToast.isVisible())) {
+ return mainLayoutIndex;
+ } else {
+ return index;
+ }
+ }
+
+ @Override
+ public boolean canDrag(MotionEvent event) {
+ if (!AppConstants.MOZ_DRAGGABLE_URLBAR) {
+ return false;
+ }
+
+ // if no current tab is active.
+ if (Tabs.getInstance().getSelectedTab() == null) {
+ return false;
+ }
+
+ // currently disabled for tablets.
+ if (HardwareUtils.isTablet()) {
+ return false;
+ }
+
+ // not enabled in editing mode.
+ if (mBrowserToolbar.isEditing()) {
+ return false;
+ }
+
+ return isInToolbarBounds((int) event.getRawY());
+ }
+
+ @Override
+ public boolean canInterceptEventWhileOpen(MotionEvent event) {
+ if (event.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ return false;
+ }
+
+ // Need to check if are intercepting a touch on main layout since we might hit a visible toast.
+ if (mRootLayout.findTopChildUnder(event) == mMainLayout &&
+ isInToolbarBounds((int) event.getRawY())) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isInToolbarBounds(int y) {
+ mBrowserToolbar.getLocationOnScreen(mToolbarLocation);
+ final int upperLimit = mToolbarLocation[1] + mBrowserToolbar.getMeasuredHeight();
+ final int lowerLimit = mToolbarLocation[1];
+ return (y > lowerLimit && y < upperLimit);
+ }
+
+ public void prepareTabsToShow() {
+ if (ensureTabsPanelExists()) {
+ // If we've just inflated the tabs panel, only show it once the current
+ // layout pass is done to avoid displayed temporary UI states during
+ // relayout.
+ final ViewTreeObserver vto = mTabsPanel.getViewTreeObserver();
+ if (vto.isAlive()) {
+ vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ mTabsPanel.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+ prepareTabsToShow();
+ }
+ });
+ }
+ } else {
+ mTabsPanel.prepareToDrag();
+ }
+ }
+
+ public int getLowerLimit() {
+ return getStatusBarHeight();
+ }
+
+ private int getStatusBarHeight() {
+ if (mStatusBarHeight != 0) {
+ return mStatusBarHeight;
+ }
+ final int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
+ if (resourceId > 0) {
+ mStatusBarHeight = getResources().getDimensionPixelSize(resourceId);
+ return mStatusBarHeight;
+ }
+ Log.e(LOGTAG, "Unable to find statusbar height");
+ return 0;
+ }
+ }
+
@Override
public View onCreateView(final String name, final Context context, final AttributeSet attrs) {
final View view;
@@ -330,7 +501,7 @@ public class BrowserApp extends GeckoApp
break;
}
- if (NewTabletUI.isEnabled(this) && msg == TabEvents.SELECTED) {
+ if (HardwareUtils.isTablet() && msg == TabEvents.SELECTED) {
updateEditingModeForTab(tab);
}
@@ -493,10 +664,14 @@ public class BrowserApp extends GeckoApp
@Override
public void onCreate(Bundle savedInstanceState) {
- mAboutHomeStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_ABOUTHOME");
-
final Intent intent = getIntent();
+
+ // Note that we're calling GeckoProfile.get *before GeckoApp.onCreate*.
+ // This means we're reliant on the logic in GeckoProfile to correctly
+ // look up our launch intent (via BrowserApp's Activity-ness) and pull
+ // out the arguments. Be careful if you change that!
final GeckoProfile p = GeckoProfile.get(this);
+
if (p != null && !p.inGuestMode()) {
// This is *only* valid because we never want to use the guest mode
// profile concurrently with a normal profile -- no syncing to it,
@@ -548,7 +723,7 @@ public class BrowserApp extends GeckoApp
GuestSession.handleIntent(this, intent);
}
- if (NewTabletUI.isEnabled(this)) {
+ if (HardwareUtils.isTablet()) {
mTabStrip = (Refreshable) (((ViewStub) findViewById(R.id.new_tablet_tab_strip)).inflate());
}
@@ -598,6 +773,7 @@ public class BrowserApp extends GeckoApp
"Accounts:Create",
"CharEncoding:Data",
"CharEncoding:State",
+ "Favicon:CacheLoad",
"Feedback:LastUrl",
"Feedback:MaybeLater",
"Feedback:OpenPlayStore",
@@ -606,20 +782,20 @@ public class BrowserApp extends GeckoApp
"Reader:Share",
"Settings:Show",
"Telemetry:Gather",
- "Updater:Launch",
- "BrowserToolbar:Visibility");
+ "Updater:Launch");
Distribution distribution = Distribution.init(this);
// Init suggested sites engine in BrowserDB.
final SuggestedSites suggestedSites = new SuggestedSites(appContext, distribution);
- BrowserDB.setSuggestedSites(suggestedSites);
+ final BrowserDB db = getProfile().getDB();
+ db.setSuggestedSites(suggestedSites);
JavaAddonManager.getInstance().init(appContext);
mSharedPreferencesHelper = new SharedPreferencesHelper(appContext);
mOrderedBroadcastHelper = new OrderedBroadcastHelper(appContext);
mBrowserHealthReporter = new BrowserHealthReporter();
- mReadingListHelper = new ReadingListHelper(appContext);
+ mReadingListHelper = new ReadingListHelper(appContext, getProfile());
if (AppConstants.MOZ_ANDROID_BEAM) {
NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
@@ -649,30 +825,42 @@ public class BrowserApp extends GeckoApp
}
});
+ mDragHelper = new DragHelper();
+ mRootLayout.setDraggableCallback(mDragHelper);
+
// Set the maximum bits-per-pixel the favicon system cares about.
IconDirectoryEntry.setMaxBPP(GeckoAppShell.getScreenDepth());
+
+ if (ZOOMED_VIEW_ENABLED) {
+ ViewStub stub = (ViewStub) findViewById(R.id.zoomed_view_stub);
+ mZoomedView = (ZoomedView) stub.inflate();
+ }
}
/**
- * Check and show Onboarding start pane if Firefox has never been launched and
+ * Check and show the firstrun pane if the browser has never been launched and
* is not opening an external link from another application.
*
- * @param context Context of application; used to show Start Pane if appropriate
- * @param intentAction Intent that launched this activity
+ * @param context Context of application; used to show firstrun pane if appropriate
+ * @param intent Intent that launched this activity
*/
- private void checkStartPane(Context context, String intentAction) {
+ private void checkFirstrun(Context context, SafeIntent intent) {
+ if (intent.getBooleanExtra(EXTRA_SKIP_STARTPANE, false)) {
+ // Note that we don't set the pref, so subsequent launches can result
+ // in the firstrun pane being shown.
+ return;
+ }
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
try {
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(this);
- if (prefs.getBoolean(PREF_STARTPANE_ENABLED, false)) {
- if (!Intent.ACTION_VIEW.equals(intentAction)) {
- final DialogFragment dialog = new org.adblockplus.browser.StartPane();
- dialog.show(getSupportFragmentManager(), ONBOARD_STARTPANE_TAG);
+ if (prefs.getBoolean(FirstrunPane.PREF_FIRSTRUN_ENABLED, false)) {
+ if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
+ showFirstrunPager();
}
// Don't bother trying again to show the v1 minimal first run.
- prefs.edit().putBoolean(PREF_STARTPANE_ENABLED, false).apply();
+ prefs.edit().putBoolean(FirstrunPane.PREF_FIRSTRUN_ENABLED, false).apply();
}
} finally {
StrictMode.setThreadPolicy(savedPolicy);
@@ -708,13 +896,31 @@ public class BrowserApp extends GeckoApp
return;
}
+ if (hideFirstrunPager()) {
+ Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.BACK, "firstrun-pane");
+ return;
+ }
+
super.onBackPressed();
}
@Override
public void onAttachedToWindow() {
- // We can't show Onboarding until Gecko has finished initialization (bug 1077583).
- checkStartPane(this, getIntent().getAction());
+ // We can't show the first run experience until Gecko has finished initialization (bug 1077583).
Felix Dahlke 2015/07/22 16:19:18 lol, their term for this is even worse, "first run
+ checkFirstrun(this, new SafeIntent(getIntent()));
+ }
+
+ private void processTabQueue() {
+ if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_ANDROID_TAB_QUEUE) {
+ ThreadUtils.postToBackgroundThread(new Runnable() {
+ @Override
+ public void run() {
+ if (TabQueueHelper.shouldOpenTabQueueUrls(BrowserApp.this)) {
+ TabQueueHelper.openQueuedUrls(BrowserApp.this, mProfile, TabQueueHelper.FILE_NAME);
+ }
+ }
+ });
+ }
}
@Override
@@ -735,6 +941,8 @@ public class BrowserApp extends GeckoApp
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this,
"Prompt:ShowTop");
+
+ processTabQueue();
}
@Override
@@ -743,9 +951,6 @@ public class BrowserApp extends GeckoApp
// Register for Prompt:ShowTop so we can foreground this activity even if it's hidden.
EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this,
"Prompt:ShowTop");
-
- final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
- lbm.unregisterReceiver(mOnboardingReceiver);
}
@Override
@@ -919,7 +1124,7 @@ public class BrowserApp extends GeckoApp
if (enabled) {
if (mLayerView != null) {
- mLayerView.setOnMetricsChangedListener(this);
+ mLayerView.setOnMetricsChangedDynamicToolbarViewportListener(this);
}
setToolbarMargin(0);
mHomePagerContainer.setPadding(0, mBrowserChrome.getHeight(), 0, 0);
@@ -927,7 +1132,7 @@ public class BrowserApp extends GeckoApp
// Immediately show the toolbar when disabling the dynamic
// toolbar.
if (mLayerView != null) {
- mLayerView.setOnMetricsChangedListener(null);
+ mLayerView.setOnMetricsChangedDynamicToolbarViewportListener(null);
}
mHomePagerContainer.setPadding(0, 0, 0, 0);
if (mBrowserChrome != null) {
@@ -1098,6 +1303,9 @@ public class BrowserApp extends GeckoApp
mReadingListHelper.uninit();
mReadingListHelper = null;
}
+ if (mZoomedView != null) {
+ mZoomedView.destroy();
+ }
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this,
"Menu:Open",
@@ -1110,6 +1318,7 @@ public class BrowserApp extends GeckoApp
"Accounts:Create",
"CharEncoding:Data",
"CharEncoding:State",
+ "Favicon:CacheLoad",
"Feedback:LastUrl",
"Feedback:MaybeLater",
"Feedback:OpenPlayStore",
@@ -1118,8 +1327,7 @@ public class BrowserApp extends GeckoApp
"Reader:Share",
"Settings:Show",
"Telemetry:Gather",
- "Updater:Launch",
- "BrowserToolbar:Visibility");
+ "Updater:Launch");
if (AppConstants.MOZ_ANDROID_BEAM) {
NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
@@ -1175,16 +1383,6 @@ public class BrowserApp extends GeckoApp
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
}
- @Override
- protected void loadStartupTab(String url, int flags) {
- // We aren't showing about:home, so cancel the telemetry timer
- if (url != null || mShouldRestore) {
- mAboutHomeStartupTimer.cancel();
- }
-
- super.loadStartupTab(url, flags);
- }
-
private void setToolbarMargin(int margin) {
((RelativeLayout.LayoutParams) mGeckoLayout.getLayoutParams()).topMargin = margin;
mGeckoLayout.requestLayout();
@@ -1322,6 +1520,7 @@ public class BrowserApp extends GeckoApp
invalidateOptionsMenu();
if (mTabsPanel != null) {
+ mRootLayout.reset();
updateSideBarState();
mTabsPanel.refresh();
}
@@ -1338,15 +1537,8 @@ public class BrowserApp extends GeckoApp
return (mTabsPanel != null && mTabsPanel.isSideBar());
}
- private void setBrowserToolbarVisible(final boolean visible) {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- if (mDynamicToolbar.isEnabled()) {
- mDynamicToolbar.setVisible(visible, VisibilityTransition.IMMEDIATE);
- }
- }
- });
+ private boolean isSideBar() {
+ return (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE);
}
private void updateSideBarState() {
@@ -1357,7 +1549,7 @@ public class BrowserApp extends GeckoApp
if (mMainLayoutAnimator != null)
mMainLayoutAnimator.stop();
- boolean isSideBar = (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE);
+ boolean isSideBar = isSideBar();
final int sidebarWidth = getResources().getDimensionPixelSize(R.dimen.tabs_sidebar_width);
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mTabsPanel.getLayoutParams();
@@ -1369,6 +1561,7 @@ public class BrowserApp extends GeckoApp
mMainLayout.scrollTo(mainLayoutScrollX, 0);
mTabsPanel.setIsSideBar(isSideBar);
+ mRootLayout.updateDragHelperParameters();
}
@Override
@@ -1433,6 +1626,10 @@ public class BrowserApp extends GeckoApp
}
});
+ } else if ("Favicon:CacheLoad".equals(event)) {
+ final String url = message.getString("url");
+ getFaviconFromCache(callback, url);
+
} else if ("Feedback:LastUrl".equals(event)) {
getLastUrl(callback);
@@ -1491,31 +1688,71 @@ public class BrowserApp extends GeckoApp
}
} else if ("Telemetry:Gather".equals(event)) {
- Telemetry.addToHistogram("PLACES_PAGES_COUNT",
- BrowserDB.getCount(getContentResolver(), "history"));
- Telemetry.addToHistogram("PLACES_BOOKMARKS_COUNT",
- BrowserDB.getCount(getContentResolver(), "bookmarks"));
- Telemetry.addToHistogram("FENNEC_FAVICONS_COUNT",
- BrowserDB.getCount(getContentResolver(), "favicons"));
- Telemetry.addToHistogram("FENNEC_THUMBNAILS_COUNT",
- BrowserDB.getCount(getContentResolver(), "thumbnails"));
- Telemetry.addToHistogram("FENNEC_READING_LIST_COUNT",
- BrowserDB.getCount(getContentResolver(), "readinglist"));
+ final BrowserDB db = getProfile().getDB();
+ final ContentResolver cr = getContentResolver();
+ Telemetry.addToHistogram("PLACES_PAGES_COUNT", db.getCount(cr, "history"));
+ Telemetry.addToHistogram("PLACES_BOOKMARKS_COUNT", db.getCount(cr, "bookmarks"));
+ Telemetry.addToHistogram("FENNEC_FAVICONS_COUNT", db.getCount(cr, "favicons"));
+ Telemetry.addToHistogram("FENNEC_THUMBNAILS_COUNT", db.getCount(cr, "thumbnails"));
+ Telemetry.addToHistogram("FENNEC_READING_LIST_COUNT", db.getReadingListAccessor().getCount(cr));
Telemetry.addToHistogram("BROWSER_IS_USER_DEFAULT", (isDefaultBrowser(Intent.ACTION_VIEW) ? 1 : 0));
if (Versions.feature16Plus) {
Telemetry.addToHistogram("BROWSER_IS_ASSIST_DEFAULT", (isDefaultBrowser(Intent.ACTION_ASSIST) ? 1 : 0));
}
} else if ("Updater:Launch".equals(event)) {
handleUpdaterLaunch();
-
- } else if ("BrowserToolbar:Visibility".equals(event)) {
- setBrowserToolbarVisible(message.getBoolean("visible"));
-
} else {
super.handleMessage(event, message, callback);
}
}
+ private void getFaviconFromCache(final EventCallback callback, final String url) {
+ final OnFaviconLoadedListener listener = new OnFaviconLoadedListener() {
+ @Override
+ public void onFaviconLoaded(final String url, final String faviconURL, final Bitmap favicon) {
+ ThreadUtils.assertOnUiThread();
+ // Convert Bitmap to Base64 data URI in background.
+ ThreadUtils.postToBackgroundThread(new Runnable() {
+ @Override
+ public void run() {
+ ByteArrayOutputStream out = null;
+ Base64OutputStream b64 = null;
+
+ // Failed to load favicon from local.
+ if (favicon == null) {
+ callback.sendError("Failed to get favicon from cache");
+ } else {
+ try {
+ out = new ByteArrayOutputStream();
+ out.write("data:image/png;base64,".getBytes());
+ b64 = new Base64OutputStream(out, Base64.NO_WRAP);
+ favicon.compress(Bitmap.CompressFormat.PNG, 100, b64);
+ callback.sendSuccess(new String(out.toByteArray()));
+ } catch (IOException e) {
+ Log.w(LOGTAG, "Failed to convert to base64 data URI");
+ callback.sendError("Failed to convert favicon to a base64 data URI");
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ if (b64 != null) {
+ b64.close();
+ }
+ } catch (IOException e) {
+ Log.w(LOGTAG, "Failed to close the streams");
+ }
+ }
+ }
+ }
+ });
+ }
+ };
+ Favicons.getSizedFaviconForPageFromLocal(getContext(),
+ url,
+ listener);
+ }
+
/**
* Use a dummy Intent to do a default browser check.
*
@@ -1703,11 +1940,14 @@ public class BrowserApp extends GeckoApp
@Override
public void onGlobalLayout() {
mTabsPanel.getViewTreeObserver().removeGlobalOnLayoutListener(this);
- mTabsPanel.show(panel);
+ showTabs(panel);
}
});
}
} else {
+ if (mDoorHangerPopup != null) {
+ mDoorHangerPopup.disable();
+ }
mTabsPanel.show(panel);
}
}
@@ -1715,6 +1955,9 @@ public class BrowserApp extends GeckoApp
@Override
public void hideTabs() {
mTabsPanel.hide();
+ if (mDoorHangerPopup != null) {
+ mDoorHangerPopup.enable();
+ }
}
@Override
@@ -1792,10 +2035,13 @@ public class BrowserApp extends GeckoApp
if (!areTabsShown()) {
mTabsPanel.setVisibility(View.INVISIBLE);
mTabsPanel.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ mRootLayout.setClosed();
+ mBrowserToolbar.setContextMenuEnabled(true);
} else {
// Cancel editing mode to return to page content when the TabsPanel closes. We cancel
// it here because there are graphical glitches if it's canceled while it's visible.
mBrowserToolbar.cancelEdit();
+ mRootLayout.setOpen();
}
mTabsPanel.finishTabsAnimation();
@@ -1905,12 +2151,21 @@ public class BrowserApp extends GeckoApp
&& mHomePagerContainer != null && mHomePagerContainer.getVisibility() == View.VISIBLE);
}
+ private boolean isFirstrunVisible() {
+ return (mFirstrunPane != null && mFirstrunPane.isVisible()
+ && mHomePagerContainer != null && mHomePagerContainer.getVisibility() == View.VISIBLE);
+ }
+
/**
* Enters editing mode with the current tab's URL. There might be no
* tabs loaded by the time the user enters editing mode e.g. just after
* the app starts. In this case, we simply fallback to an empty URL.
*/
private void enterEditingMode() {
+ if (hideFirstrunPager()) {
+ Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.ACTIONBAR, "firstrun-pane");
+ }
+
String url = "";
final Tab tab = Tabs.getInstance().getSelectedTab();
@@ -1939,7 +2194,14 @@ public class BrowserApp extends GeckoApp
}
final Tab selectedTab = Tabs.getInstance().getSelectedTab();
- mTargetTabForEditingMode = (selectedTab != null ? selectedTab.getId() : null);
+ final String panelId;
+ if (selectedTab != null) {
+ mTargetTabForEditingMode = selectedTab.getId();
+ panelId = selectedTab.getMostRecentHomePanel();
+ } else {
+ mTargetTabForEditingMode = null;
+ panelId = null;
+ }
final PropertyAnimator animator = new PropertyAnimator(250);
animator.setUseHardwareLayer(false);
@@ -1948,7 +2210,6 @@ public class BrowserApp extends GeckoApp
mBrowserToolbar.startEditing(url, animator);
- final String panelId = selectedTab.getMostRecentHomePanel();
showHomePagerWithAnimator(panelId, animator);
animator.start();
@@ -1994,6 +2255,7 @@ public class BrowserApp extends GeckoApp
}
// Otherwise, check for a bookmark keyword.
+ final BrowserDB db = getProfile().getDB();
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
@@ -2009,7 +2271,7 @@ public class BrowserApp extends GeckoApp
keywordSearch = url.substring(index + 1);
}
- final String keywordUrl = BrowserDB.getUrlForKeyword(getContentResolver(), keyword);
+ final String keywordUrl = db.getUrlForKeyword(getContentResolver(), keyword);
// If there isn't a bookmark keyword, load the url. This may result in a query
// using the default search engine.
@@ -2022,7 +2284,11 @@ public class BrowserApp extends GeckoApp
recordSearch(null, "barkeyword");
// Otherwise, construct a search query from the bookmark keyword.
- final String searchUrl = keywordUrl.replace("%s", URLEncoder.encode(keywordSearch));
+ // Replace lower case bookmark keywords with URLencoded search query or
+ // replace upper case bookmark keywords with un-encoded search query.
+ // This makes it match the same behaviour as on Firefox for the desktop.
+ final String searchUrl = keywordUrl.replace("%s", URLEncoder.encode(keywordSearch)).replace("%S", keywordSearch);
+
Tabs.getInstance().loadUrl(searchUrl, Tabs.LOADURL_USER_ENTERED);
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL,
TelemetryContract.Method.ACTIONBAR,
@@ -2059,17 +2325,22 @@ public class BrowserApp extends GeckoApp
* @param query
* a search query to store. We won't store empty queries.
*/
- private void storeSearchQuery(String query) {
+ private void storeSearchQuery(final String query) {
if (TextUtils.isEmpty(query)) {
return;
}
- final ContentValues values = new ContentValues();
- values.put(SearchHistory.QUERY, query);
+ final GeckoProfile profile = getProfile();
+ // Don't bother storing search queries in guest mode
+ if (profile.inGuestMode()) {
+ return;
+ }
+
+ final BrowserDB db = profile.getDB();
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
- getContentResolver().insert(SearchHistory.CONTENT_URI, values);
+ db.getSearches().insert(getContentResolver(), query);
}
});
}
@@ -2096,7 +2367,7 @@ public class BrowserApp extends GeckoApp
* temporarily selected tab is visible to users.
*/
private void selectTargetTabForEditingMode() {
- if (NewTabletUI.isEnabled(this)) {
+ if (HardwareUtils.isTablet()) {
return;
}
@@ -2177,6 +2448,22 @@ public class BrowserApp extends GeckoApp
}
}
+ private void showFirstrunPager() {
Felix Dahlke 2015/07/22 16:19:18 Also more for the record, there's a change for re-
+ if (mFirstrunPane == null) {
+ final ViewStub firstrunPagerStub = (ViewStub) findViewById(R.id.firstrun_pager_stub);
+ mFirstrunPane = (FirstrunPane) firstrunPagerStub.inflate();
+ mFirstrunPane.load(getSupportFragmentManager());
+ mFirstrunPane.registerOnFinishListener(new FirstrunPane.OnFinishListener() {
+ @Override
+ public void onFinish() {
+ BrowserApp.this.mFirstrunPane = null;
+ }
+ });
+ }
+
+ mHomePagerContainer.setVisibility(View.VISIBLE);
+ }
+
private void showHomePager(String panelId) {
showHomePagerWithAnimator(panelId, null);
}
@@ -2264,6 +2551,15 @@ public class BrowserApp extends GeckoApp
mLayerView.setVisibility(View.INVISIBLE);
}
+ public boolean hideFirstrunPager() {
+ if (!isFirstrunVisible()) {
+ return false;
+ }
+
+ mFirstrunPane.hide();
+ return true;
+ }
+
/**
* Hides the HomePager, using the url of the currently selected tab as the url to be
* loaded.
@@ -2585,8 +2881,12 @@ public class BrowserApp extends GeckoApp
// Action providers are available only ICS+.
if (Versions.feature14Plus) {
GeckoMenuItem share = (GeckoMenuItem) mMenu.findItem(R.id.share);
+ final GeckoMenuItem quickShare = (GeckoMenuItem) mMenu.findItem(R.id.quickshare);
+
GeckoActionProvider provider = GeckoActionProvider.getForType(GeckoActionProvider.DEFAULT_MIME_TYPE, this);
+
share.setActionProvider(provider);
+ quickShare.setActionProvider(provider);
}
org.adblockplus.browser.BrowserAppUtils.updateBlockAdsMenuItem(
@@ -2599,7 +2899,7 @@ public class BrowserApp extends GeckoApp
public void openOptionsMenu() {
// Disable menu access (for hardware buttons) when the software menu button is inaccessible.
// Note that the software button is always accessible on new tablet.
- if (mBrowserToolbar.isEditing() && !NewTabletUI.isEnabled(this)) {
+ if (mBrowserToolbar.isEditing() && !HardwareUtils.isTablet()) {
return;
}
@@ -2673,9 +2973,11 @@ public class BrowserApp extends GeckoApp
Tab tab = Tabs.getInstance().getSelectedTab();
MenuItem bookmark = aMenu.findItem(R.id.bookmark);
+ final MenuItem reader = aMenu.findItem(R.id.reading_list);
MenuItem back = aMenu.findItem(R.id.back);
MenuItem forward = aMenu.findItem(R.id.forward);
MenuItem share = aMenu.findItem(R.id.share);
+ final MenuItem quickShare = aMenu.findItem(R.id.quickshare);
MenuItem saveAsPDF = aMenu.findItem(R.id.save_as_pdf);
MenuItem charEncoding = aMenu.findItem(R.id.char_encoding);
MenuItem findInPage = aMenu.findItem(R.id.find_in_page);
@@ -2693,12 +2995,15 @@ public class BrowserApp extends GeckoApp
ClearOnShutdownPref.PREF,
new HashSet<String>()).isEmpty();
aMenu.findItem(R.id.quit).setVisible(visible);
+ aMenu.findItem(R.id.logins).setVisible(AppConstants.NIGHTLY_BUILD);
if (tab == null || tab.getURL() == null) {
bookmark.setEnabled(false);
+ reader.setEnabled(false);
back.setEnabled(false);
forward.setEnabled(false);
share.setEnabled(false);
+ quickShare.setEnabled(false);
saveAsPDF.setEnabled(false);
findInPage.setEnabled(false);
@@ -2716,11 +3021,23 @@ public class BrowserApp extends GeckoApp
return true;
}
- bookmark.setEnabled(!AboutPages.isAboutReader(tab.getURL()));
- bookmark.setVisible(!GeckoProfile.get(this).inGuestMode());
+ final boolean inGuestMode = GeckoProfile.get(this).inGuestMode();
+
+ final boolean isAboutReader = AboutPages.isAboutReader(tab.getURL());
+ bookmark.setEnabled(!isAboutReader);
+ bookmark.setVisible(!inGuestMode);
bookmark.setCheckable(true);
bookmark.setChecked(tab.isBookmark());
bookmark.setIcon(resolveBookmarkIconID(tab.isBookmark()));
+ bookmark.setTitle(resolveBookmarkTitleID(tab.isBookmark()));
+
+ reader.setEnabled(isAboutReader || !AboutPages.isAboutPage(tab.getURL()));
+ reader.setVisible(!inGuestMode);
+ reader.setCheckable(true);
+ final boolean isPageInReadingList = tab.isInReadingList();
+ reader.setChecked(isPageInReadingList);
+ reader.setIcon(resolveReadingListIconID(isPageInReadingList));
+ reader.setTitle(resolveReadingListTitleID(isPageInReadingList));
back.setEnabled(tab.canDoBack());
forward.setEnabled(tab.canDoForward());
@@ -2736,9 +3053,10 @@ public class BrowserApp extends GeckoApp
}
// Disable share menuitem for about:, chrome:, file:, and resource: URIs
- final boolean shareEnabled = RestrictedProfiles.isAllowed(this, RestrictedProfiles.Restriction.DISALLOW_SHARE);
- share.setVisible(shareEnabled);
- share.setEnabled(StringUtils.isShareableUrl(url) && shareEnabled);
+ final boolean shareVisible = RestrictedProfiles.isAllowed(this, RestrictedProfiles.Restriction.DISALLOW_SHARE);
+ share.setVisible(shareVisible);
+ final boolean shareEnabled = StringUtils.isShareableUrl(url) && shareVisible;
+ share.setEnabled(shareEnabled);
MenuUtils.safeSetEnabled(aMenu, R.id.apps, RestrictedProfiles.isAllowed(this, RestrictedProfiles.Restriction.DISALLOW_INSTALL_APPS));
MenuUtils.safeSetEnabled(aMenu, R.id.addons, RestrictedProfiles.isAllowed(this, RestrictedProfiles.Restriction.DISALLOW_INSTALL_EXTENSION));
MenuUtils.safeSetEnabled(aMenu, R.id.downloads, RestrictedProfiles.isAllowed(this, RestrictedProfiles.Restriction.DISALLOW_DOWNLOADS));
@@ -2755,6 +3073,10 @@ public class BrowserApp extends GeckoApp
// Action providers are available only ICS+.
if (Versions.feature14Plus) {
+ quickShare.setVisible(shareVisible);
+ quickShare.setEnabled(shareEnabled);
+
+ // This provider also applies to the quick share menu item.
final GeckoActionProvider provider = ((GeckoMenuItem) share).getGeckoActionProvider();
if (provider != null) {
Intent shareIntent = provider.getIntent();
@@ -2770,6 +3092,7 @@ public class BrowserApp extends GeckoApp
shareIntent.putExtra(Intent.EXTRA_TEXT, url);
shareIntent.putExtra(Intent.EXTRA_SUBJECT, tab.getDisplayTitle());
shareIntent.putExtra(Intent.EXTRA_TITLE, tab.getDisplayTitle());
+ shareIntent.putExtra(ShareDialog.INTENT_EXTRA_DEVICES_ONLY, true);
// Clear the existing thumbnail extras so we don't share an old thumbnail.
shareIntent.removeExtra("share_screenshot_uri");
@@ -2835,6 +3158,18 @@ public class BrowserApp extends GeckoApp
}
}
+ private int resolveBookmarkTitleID(final boolean isBookmark) {
+ return (isBookmark ? R.string.bookmark_remove : R.string.bookmark);
+ }
+
+ private int resolveReadingListIconID(final boolean isInReadingList) {
+ return (isInReadingList ? R.drawable.ic_menu_reader_remove : R.drawable.ic_menu_reader_add);
+ }
+
+ private int resolveReadingListTitleID(final boolean isInReadingList) {
+ return (isInReadingList ? R.string.reading_list_remove : R.string.overlay_share_reading_list_btn_label);
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Tab tab = null;
@@ -2857,10 +3192,30 @@ public class BrowserApp extends GeckoApp
Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.MENU, "bookmark");
tab.removeBookmark();
item.setIcon(resolveBookmarkIconID(false));
+ item.setTitle(resolveBookmarkTitleID(false));
} else {
Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "bookmark");
tab.addBookmark();
item.setIcon(resolveBookmarkIconID(true));
+ item.setTitle(resolveBookmarkTitleID(true));
+ }
+ }
+ return true;
+ }
+
+ if (itemId == R.id.reading_list) {
+ tab = Tabs.getInstance().getSelectedTab();
+ if (tab != null) {
+ if (item.isChecked()) {
+ Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.MENU, "reading_list");
+ tab.removeFromReadingList();
+ item.setIcon(resolveReadingListIconID(false));
+ item.setTitle(resolveReadingListTitleID(false));
+ } else {
+ Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "reading_list");
+ tab.addToReadingList();
+ item.setIcon(resolveReadingListIconID(true));
+ item.setTitle(resolveReadingListTitleID(true));
}
}
return true;
@@ -2921,6 +3276,11 @@ public class BrowserApp extends GeckoApp
return true;
}
+ if (itemId == R.id.logins) {
+ Tabs.getInstance().loadUrlInTab(AboutPages.PASSWORDS);
+ return true;
+ }
+
if (itemId == R.id.apps) {
Tabs.getInstance().loadUrlInTab(AboutPages.APPS);
return true;
@@ -3066,11 +3426,15 @@ public class BrowserApp extends GeckoApp
final boolean isViewAction = Intent.ACTION_VIEW.equals(action);
final boolean isBookmarkAction = GeckoApp.ACTION_HOMESCREEN_SHORTCUT.equals(action);
+ final boolean isTabQueueAction = TabQueueHelper.LOAD_URLS_ACTION.equals(action);
if (mInitialized && (isViewAction || isBookmarkAction)) {
// Dismiss editing mode if the user is loading a URL from an external app.
mBrowserToolbar.cancelEdit();
+ // Hide firstrun-pane if the user is loading a URL from an external app.
+ hideFirstrunPager();
+
// GeckoApp.ACTION_HOMESCREEN_SHORTCUT means we're opening a bookmark that
// was added to Android's homescreen.
final TelemetryContract.Method method =
@@ -3091,6 +3455,17 @@ public class BrowserApp extends GeckoApp
GuestSession.handleIntent(this, intent);
}
+ // If the user has clicked the tab queue notification then load the tabs.
+ if(AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_ANDROID_TAB_QUEUE && mInitialized && isTabQueueAction) {
+ int queuedTabCount = TabQueueHelper.getTabQueueLength(this);
+ TabQueueHelper.openQueuedUrls(this, mProfile, TabQueueHelper.FILE_NAME);
+
+ // If there's more than one tab then also show the tabs panel.
+ if (queuedTabCount > 1) {
+ showNormalTabs();
+ }
+ }
+
if (!mInitialized || !Intent.ACTION_MAIN.equals(action)) {
return;
}
@@ -3131,22 +3506,23 @@ public class BrowserApp extends GeckoApp
}
private void getLastUrl(final EventCallback callback) {
+ final BrowserDB db = getProfile().getDB();
(new UIAsyncTask.WithoutParams<String>(ThreadUtils.getBackgroundHandler()) {
@Override
public synchronized String doInBackground() {
// Get the most recent URL stored in browser history.
- String url = "";
- Cursor c = null;
+ final Cursor c = db.getRecentHistory(getContentResolver(), 1);
+ if (c == null) {
+ return "";
+ }
try {
- c = BrowserDB.getRecentHistory(getContentResolver(), 1);
if (c.moveToFirst()) {
- url = c.getString(c.getColumnIndexOrThrow(Combined.URL));
+ return c.getString(c.getColumnIndexOrThrow(Combined.URL));
}
+ return "";
} finally {
- if (c != null)
- c.close();
+ c.close();
}
- return url;
}
@Override
@@ -3242,6 +3618,12 @@ public class BrowserApp extends GeckoApp
return GeckoProfile.getDefaultProfileName(this);
}
+ // For use from tests only.
+ @RobocopTarget
+ public ReadingListHelper getReadingListHelper() {
+ return mReadingListHelper;
+ }
+
/**
* Launch UI that lets the user update Firefox.
*

Powered by Google App Engine
This is Rietveld