Bug 1247689 - Pre: Move bookmark snackbars / helper UIs into BookmarkStateChangeDelegate r?sebastian
MozReview-Commit-ID: BRGMN0vE8nf
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -6,17 +6,16 @@
package org.mozilla.gecko;
import android.Manifest;
import android.app.DownloadManager;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import org.json.JSONArray;
import org.mozilla.gecko.adjust.AdjustHelperInterface;
import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
import org.mozilla.gecko.Tabs.TabEvents;
import org.mozilla.gecko.animation.PropertyAnimator;
@@ -54,17 +53,17 @@ 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.overlays.ui.ShareDialog;
import org.mozilla.gecko.permissions.Permissions;
import org.mozilla.gecko.preferences.ClearOnShutdownPref;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.promotion.AddToHomeScreenPromotion;
-import org.mozilla.gecko.promotion.SimpleHelperUI;
+import org.mozilla.gecko.promotion.BookmarkStateChangeDelegate;
import org.mozilla.gecko.prompts.Prompt;
import org.mozilla.gecko.prompts.PromptListItem;
import org.mozilla.gecko.reader.SavedReaderViewHelper;
import org.mozilla.gecko.reader.ReaderModeUtils;
import org.mozilla.gecko.reader.ReadingListHelper;
import org.mozilla.gecko.restrictions.Restrictable;
import org.mozilla.gecko.restrictions.RestrictedProfileConfiguration;
import org.mozilla.gecko.restrictions.Restrictions;
@@ -127,17 +126,16 @@ import android.nfc.NfcAdapter;
import android.nfc.NfcEvent;
import android.os.Build;
import android.os.Bundle;
import android.os.StrictMode;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.NotificationCompat;
-import android.support.v4.content.ContextCompat;
import android.support.v4.view.MenuItemCompat;
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;
@@ -209,19 +207,19 @@ 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";
// Request ID for startActivityForResult.
private static final int ACTIVITY_REQUEST_PREFERENCES = 1001;
private static final int ACTIVITY_REQUEST_TAB_QUEUE = 2001;
- private static final int ACTIVITY_REQUEST_FIRST_READERVIEW_BOOKMARK = 3001;
- private static final int ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_GOTO_BOOKMARKS = 3002;
- private static final int ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_IGNORE = 3003;
+ public static final int ACTIVITY_REQUEST_FIRST_READERVIEW_BOOKMARK = 3001;
+ public static final int ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_GOTO_BOOKMARKS = 3002;
+ public static final int ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_IGNORE = 3003;
public static final String ACTION_VIEW_MULTIPLE = AppConstants.ANDROID_PACKAGE_NAME + ".action.VIEW_MULTIPLE";
@RobocopTarget
public static final String EXTRA_SKIP_STARTPANE = "skipstartpane";
private static final String EOL_NOTIFIED = "eol_notified";
private BrowserSearch mBrowserSearch;
@@ -302,17 +300,18 @@ public class BrowserApp extends GeckoApp
// both the web content and the HomePager will be hidden. This flag is used to prevent the
// race by determining if the web content should be hidden at the animation's end.
private boolean mHideWebContentOnAnimationEnd;
private final DynamicToolbar mDynamicToolbar = new DynamicToolbar();
private final List<BrowserAppDelegate> delegates = Collections.unmodifiableList(Arrays.asList(
(BrowserAppDelegate) new AddToHomeScreenPromotion(),
- (BrowserAppDelegate) new ScreenshotDelegate()
+ (BrowserAppDelegate) new ScreenshotDelegate(),
+ (BrowserAppDelegate) new BookmarkStateChangeDelegate()
));
@NonNull
private SearchEngineManager searchEngineManager; // Contains reference to Context - DO NOT LEAK!
@Override
public View onCreateView(final String name, final Context context, final AttributeSet attrs) {
final View view;
@@ -368,49 +367,16 @@ public class BrowserApp extends GeckoApp
case MENU_UPDATED:
if (Tabs.getInstance().isSelectedTab(tab)) {
invalidateOptionsMenu();
}
break;
case PAGE_SHOW:
tab.loadFavicon();
break;
- case BOOKMARK_ADDED:
- // We always show the special offline snackbar whenever we bookmark a reader page.
- // It's possible that the page is already stored offline, however this is highly
- // unlikely, and even so it is probably nicer to show the same offline notification
- // every time we bookmark an about:reader page.
- if (!AboutPages.isAboutReader(tab.getURL())) {
- showBookmarkAddedSnackbar();
- } else {
- final SharedPreferences prefs = GeckoSharedPrefs.forProfile(this);
-
- final boolean hasFirstReaderViewPromptBeenShownBefore = prefs.getBoolean(SimpleHelperUI.PREF_FIRST_RVBP_SHOWN, false);
-
- if (hasFirstReaderViewPromptBeenShownBefore) {
- showReaderModeBookmarkAddedSnackbar();
- } else {
- SimpleHelperUI.show(this,
- SimpleHelperUI.FIRST_RVBP_SHOWN_TELEMETRYEXTRA,
- ACTIVITY_REQUEST_FIRST_READERVIEW_BOOKMARK,
- R.string.helper_first_offline_bookmark_title, R.string.helper_first_offline_bookmark_message,
- R.drawable.helper_first_readerview_bookmark, R.string.helper_first_offline_bookmark_button,
- ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_GOTO_BOOKMARKS,
- ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_IGNORE);
-
- GeckoSharedPrefs.forProfile(this)
- .edit()
- .putBoolean(SimpleHelperUI.PREF_FIRST_RVBP_SHOWN, true)
- .apply();
- }
- }
- break;
- case BOOKMARK_REMOVED:
- showBookmarkRemovedSnackbar();
- break;
case UNSELECTED:
// We receive UNSELECTED immediately after the SELECTED listeners run
// so we are ensured that the unselectedTabEditingText has not changed.
if (tab.isEditing()) {
// Copy to avoid constructing new objects.
tab.getEditingState().copyFrom(mLastTabEditingState);
}
@@ -456,59 +422,16 @@ public class BrowserApp extends GeckoApp
// must be called after we restore the editing state in the edit text View.
if (editingState.isBrowserSearchShown()) {
showBrowserSearch();
} else {
hideBrowserSearch();
}
}
- private void showBookmarkAddedSnackbar() {
- // This flow is from the option menu which has check to see if a bookmark was already added.
- // So, it is safe here to show the snackbar that bookmark_added without any checks.
-
- final SnackbarHelper.SnackbarCallback callback = new SnackbarHelper.SnackbarCallback() {
- @Override
- public void onClick(View v) {
- Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.TOAST, "bookmark_options");
- showBookmarkDialog();
- }
- };
-
- SnackbarHelper.showSnackbarWithAction(this,
- getResources().getString(R.string.bookmark_added),
- Snackbar.LENGTH_LONG,
- getResources().getString(R.string.bookmark_options),
- callback);
- }
-
- private void showReaderModeBookmarkAddedSnackbar() {
- final Drawable iconDownloaded = DrawableUtil.tintDrawable(getContext(), R.drawable.status_icon_readercache, Color.WHITE);
-
- final SnackbarHelper.SnackbarCallback callback = new SnackbarHelper.SnackbarCallback() {
- @Override
- public void onClick(View v) {
- openUrlAndStopEditing("about:home?panel=" + HomeConfig.getIdForBuiltinPanelType(PanelType.BOOKMARKS));
- }
- };
-
- SnackbarHelper.showSnackbarWithActionAndColors(this,
- getResources().getString(R.string.reader_saved_offline),
- Snackbar.LENGTH_LONG,
- getResources().getString(R.string.reader_switch_to_bookmarks),
- callback,
- iconDownloaded,
- ContextCompat.getColor(this, R.color.link_blue),
- Color.WHITE);
- }
-
- private void showBookmarkRemovedSnackbar() {
- SnackbarHelper.showSnackbar(this, getResources().getString(R.string.bookmark_removed), Snackbar.LENGTH_LONG);
- }
-
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (AndroidGamepadManager.handleKeyEvent(event)) {
return true;
}
// Global onKey handler. This is called if the focused UI doesn't
// handle the key event, and before Gecko swallows the events.
@@ -1076,17 +999,17 @@ public class BrowserApp extends GeckoApp
final boolean enableGuestSession = GuestSession.shouldUse(this, args);
final boolean inGuestSession = GeckoProfile.get(this).inGuestMode();
if (enableGuestSession != inGuestSession) {
doRestart(getIntent());
return;
}
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener) this,
- "Prompt:ShowTop");
+ "Prompt:ShowTop");
processTabQueue();
for (BrowserAppDelegate delegate : delegates) {
delegate.onResume(this);
}
}
@@ -1255,67 +1178,16 @@ public class BrowserApp extends GeckoApp
}
}
});
// Intercept key events for gamepad shortcuts
mBrowserToolbar.setOnKeyListener(this);
}
- private void showBookmarkDialog() {
- final Resources res = getResources();
- final Tab tab = Tabs.getInstance().getSelectedTab();
-
- final Prompt ps = new Prompt(this, new Prompt.PromptCallback() {
- @Override
- public void onPromptFinished(String result) {
- int itemId = -1;
- try {
- itemId = new JSONObject(result).getInt("button");
- } catch (JSONException ex) {
- Log.e(LOGTAG, "Exception reading bookmark prompt result", ex);
- }
-
- if (tab == null) {
- return;
- }
-
- if (itemId == 0) {
- final String extrasId = res.getResourceEntryName(R.string.contextmenu_edit_bookmark);
- Telemetry.sendUIEvent(TelemetryContract.Event.ACTION,
- TelemetryContract.Method.DIALOG, extrasId);
-
- new EditBookmarkDialog(BrowserApp.this).show(tab.getURL());
- } else if (itemId == 1) {
- final String extrasId = res.getResourceEntryName(R.string.contextmenu_add_to_launcher);
- Telemetry.sendUIEvent(TelemetryContract.Event.ACTION,
- TelemetryContract.Method.DIALOG, extrasId);
-
- final String url = tab.getURL();
- final String title = tab.getDisplayTitle();
-
- if (url != null && title != null) {
- ThreadUtils.postToBackgroundThread(new Runnable() {
- @Override
- public void run() {
- GeckoAppShell.createShortcut(title, url);
- }
- });
- }
- }
- }
- });
-
- final PromptListItem[] items = new PromptListItem[2];
- items[0] = new PromptListItem(res.getString(R.string.contextmenu_edit_bookmark));
- items[1] = new PromptListItem(res.getString(R.string.contextmenu_add_to_launcher));
-
- ps.show("", "", items, ListView.CHOICE_MODE_NONE);
- }
-
private void setDynamicToolbarEnabled(boolean enabled) {
ThreadUtils.assertOnUiThread();
if (enabled) {
if (mLayerView != null) {
mLayerView.getDynamicToolbarAnimator().addTranslationListener(this);
}
setToolbarMargin(0);
@@ -2331,17 +2203,17 @@ public class BrowserApp extends GeckoApp
mTargetTabForEditingMode = null;
tabs.selectTab(tab.getId());
mBrowserToolbar.cancelEdit();
return true;
}
- private void openUrlAndStopEditing(String url) {
+ public void openUrlAndStopEditing(String url) {
openUrlAndStopEditing(url, null, false);
}
private void openUrlAndStopEditing(String url, boolean newTab) {
openUrlAndStopEditing(url, null, newTab);
}
private void openUrlAndStopEditing(String url, String searchEngine) {
@@ -2683,25 +2555,21 @@ public class BrowserApp extends GeckoApp
}
});
break;
case ACTIVITY_REQUEST_TAB_QUEUE:
TabQueueHelper.processTabQueuePromptResponse(resultCode, this);
break;
- case ACTIVITY_REQUEST_FIRST_READERVIEW_BOOKMARK:
- if (resultCode == ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_GOTO_BOOKMARKS) {
- openUrlAndStopEditing("about:home?panel=" + HomeConfig.getIdForBuiltinPanelType(PanelType.BOOKMARKS));
- } else if (resultCode == ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_IGNORE) {
- showReaderModeBookmarkAddedSnackbar();
+ default:
+ for (final BrowserAppDelegate delegate : delegates) {
+ delegate.onActivityResult(this, requestCode, resultCode, data);
}
- break;
-
- default:
+
super.onActivityResult(requestCode, resultCode, data);
}
}
private void showFirstrunPager() {
if (mFirstrunAnimationContainer == null) {
final ViewStub firstrunPagerStub = (ViewStub) findViewById(R.id.firstrun_pager_stub);
mFirstrunAnimationContainer = (FirstrunAnimationContainer) firstrunPagerStub.inflate();
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserAppDelegate.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserAppDelegate.java
@@ -1,15 +1,16 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
+import android.content.Intent;
import android.os.Bundle;
import org.mozilla.gecko.tabs.TabsPanel;
/**
* Abstract class for extending the behavior of BrowserApp without adding additional code to the
* already huge class.
*/
@@ -53,9 +54,18 @@ public abstract class BrowserAppDelegate
* Called when the tabs tray is opened.
*/
public void onTabsTrayShown(BrowserApp browserApp, TabsPanel tabsPanel) {}
/**
* Called when the tabs tray is closed.
*/
public void onTabsTrayHidden(BrowserApp browserApp, TabsPanel tabsPanel) {}
+
+ /**
+ * Called when an activity started using startActivityForResult() returns.
+ *
+ * Delegates should only use request and result codes declared in BrowserApp itself (as opposed
+ * to declarations in the delegate), in order to avoid conflicts.
+ */
+ public void onActivityResult(BrowserApp browserApp, int requestCode, int resultCode, Intent data) {}
}
+
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/promotion/BookmarkStateChangeDelegate.java
@@ -0,0 +1,235 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.promotion;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+import android.view.View;
+import android.widget.ListView;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.mozilla.gecko.AboutPages;
+import org.mozilla.gecko.BrowserApp;
+import org.mozilla.gecko.BrowserAppDelegate;
+import org.mozilla.gecko.EditBookmarkDialog;
+import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoSharedPrefs;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.SnackbarHelper;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
+import org.mozilla.gecko.home.HomeConfig;
+import org.mozilla.gecko.prompts.Prompt;
+import org.mozilla.gecko.prompts.PromptListItem;
+import org.mozilla.gecko.util.DrawableUtil;
+import org.mozilla.gecko.util.ThreadUtils;
+
+import java.lang.ref.WeakReference;
+
+public class BookmarkStateChangeDelegate extends BrowserAppDelegate implements Tabs.OnTabsChangedListener {
+ private static final String LOGTAG = "BookmarkDelegate";
+
+ private WeakReference<BrowserApp> mBrowserApp;
+
+ @Override
+ public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
+ mBrowserApp = new WeakReference<>(browserApp);
+ }
+
+ @Override
+ public void onResume(BrowserApp browserApp) {
+ Tabs.registerOnTabsChangedListener(this);
+ }
+
+ @Override
+ public void onPause(BrowserApp browserApp) {
+ Tabs.unregisterOnTabsChangedListener(this);
+ }
+
+ @Override
+ public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) {
+ switch (msg) {
+ case BOOKMARK_ADDED:
+ // We always show the special offline snackbar whenever we bookmark a reader page.
+ // It's possible that the page is already stored offline, however this is highly
+ // unlikely, and even so it is probably nicer to show the same offline notification
+ // every time we bookmark an about:reader page.
+ if (!AboutPages.isAboutReader(tab.getURL())) {
+ showBookmarkAddedSnackbar();
+ } else {
+ if (!promoteReaderViewBookmarkAdded()) {
+ showReaderModeBookmarkAddedSnackbar();
+ }
+ }
+ break;
+
+ case BOOKMARK_REMOVED:
+ showBookmarkRemovedSnackbar();
+ break;
+ }
+ }
+
+ @Override
+ public void onActivityResult(BrowserApp browserApp, int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case BrowserApp.ACTIVITY_REQUEST_FIRST_READERVIEW_BOOKMARK:
+ if (resultCode == BrowserApp.ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_GOTO_BOOKMARKS) {
+ browserApp.openUrlAndStopEditing("about:home?panel=" + HomeConfig.getIdForBuiltinPanelType(HomeConfig.PanelType.BOOKMARKS));
+ } else if (resultCode == BrowserApp.ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_IGNORE) {
+ showReaderModeBookmarkAddedSnackbar();
+ }
+ break;
+ }
+ }
+
+ private boolean promoteReaderViewBookmarkAdded() {
+ final BrowserApp browserApp = mBrowserApp.get();
+ if (browserApp == null) {
+ return false;
+ }
+
+ final SharedPreferences prefs = GeckoSharedPrefs.forProfile(browserApp.getContext());
+
+ final boolean hasFirstReaderViewPromptBeenShownBefore = prefs.getBoolean(SimpleHelperUI.PREF_FIRST_RVBP_SHOWN, false);
+
+ if (hasFirstReaderViewPromptBeenShownBefore) {
+ return false;
+ }
+
+ SimpleHelperUI.show(browserApp,
+ SimpleHelperUI.FIRST_RVBP_SHOWN_TELEMETRYEXTRA,
+ BrowserApp.ACTIVITY_REQUEST_FIRST_READERVIEW_BOOKMARK,
+ R.string.helper_first_offline_bookmark_title, R.string.helper_first_offline_bookmark_message,
+ R.drawable.helper_first_readerview_bookmark, R.string.helper_first_offline_bookmark_button,
+ BrowserApp.ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_GOTO_BOOKMARKS,
+ BrowserApp.ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_IGNORE);
+
+ GeckoSharedPrefs.forProfile(browserApp.getContext())
+ .edit()
+ .putBoolean(SimpleHelperUI.PREF_FIRST_RVBP_SHOWN, true)
+ .apply();
+
+ return true;
+ }
+
+ private void showBookmarkAddedSnackbar() {
+ final BrowserApp browserApp = mBrowserApp.get();
+ if (browserApp == null) {
+ return;
+ }
+
+ // This flow is from the option menu which has check to see if a bookmark was already added.
+ // So, it is safe here to show the snackbar that bookmark_added without any checks.
+ final SnackbarHelper.SnackbarCallback callback = new SnackbarHelper.SnackbarCallback() {
+ @Override
+ public void onClick(View v) {
+ Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.TOAST, "bookmark_options");
+ showBookmarkDialog(browserApp);
+ }
+ };
+
+ SnackbarHelper.showSnackbarWithAction(browserApp,
+ browserApp.getResources().getString(R.string.bookmark_added),
+ Snackbar.LENGTH_LONG,
+ browserApp.getResources().getString(R.string.bookmark_options),
+ callback);
+ }
+
+ private void showBookmarkRemovedSnackbar() {
+ final BrowserApp browserApp = mBrowserApp.get();
+ if (browserApp == null) {
+ return;
+ }
+
+ SnackbarHelper.showSnackbar(browserApp, browserApp.getResources().getString(R.string.bookmark_removed), Snackbar.LENGTH_LONG);
+ }
+
+ private static void showBookmarkDialog(final BrowserApp browserApp) {
+ final Resources res = browserApp.getResources();
+ final Tab tab = Tabs.getInstance().getSelectedTab();
+
+ final Prompt ps = new Prompt(browserApp, new Prompt.PromptCallback() {
+ @Override
+ public void onPromptFinished(String result) {
+ int itemId = -1;
+ try {
+ itemId = new JSONObject(result).getInt("button");
+ } catch (JSONException ex) {
+ Log.e(LOGTAG, "Exception reading bookmark prompt result", ex);
+ }
+
+ if (tab == null) {
+ return;
+ }
+
+ if (itemId == 0) {
+ final String extrasId = res.getResourceEntryName(R.string.contextmenu_edit_bookmark);
+ Telemetry.sendUIEvent(TelemetryContract.Event.ACTION,
+ TelemetryContract.Method.DIALOG, extrasId);
+
+ new EditBookmarkDialog(browserApp).show(tab.getURL());
+ } else if (itemId == 1) {
+ final String extrasId = res.getResourceEntryName(R.string.contextmenu_add_to_launcher);
+ Telemetry.sendUIEvent(TelemetryContract.Event.ACTION,
+ TelemetryContract.Method.DIALOG, extrasId);
+
+ final String url = tab.getURL();
+ final String title = tab.getDisplayTitle();
+
+ if (url != null && title != null) {
+ ThreadUtils.postToBackgroundThread(new Runnable() {
+ @Override
+ public void run() {
+ GeckoAppShell.createShortcut(title, url);
+ }
+ });
+ }
+ }
+ }
+ });
+
+ final PromptListItem[] items = new PromptListItem[2];
+ items[0] = new PromptListItem(res.getString(R.string.contextmenu_edit_bookmark));
+ items[1] = new PromptListItem(res.getString(R.string.contextmenu_add_to_launcher));
+
+ ps.show("", "", items, ListView.CHOICE_MODE_NONE);
+ }
+
+ private void showReaderModeBookmarkAddedSnackbar() {
+ final BrowserApp browserApp = mBrowserApp.get();
+ if (browserApp == null) {
+ return;
+ }
+
+ final Drawable iconDownloaded = DrawableUtil.tintDrawable(browserApp, R.drawable.status_icon_readercache, Color.WHITE);
+
+ final SnackbarHelper.SnackbarCallback callback = new SnackbarHelper.SnackbarCallback() {
+ @Override
+ public void onClick(View v) {
+ browserApp.openUrlAndStopEditing("about:home?panel=" + HomeConfig.getIdForBuiltinPanelType(HomeConfig.PanelType.BOOKMARKS));
+ }
+ };
+
+ SnackbarHelper.showSnackbarWithActionAndColors(browserApp,
+ browserApp.getResources().getString(R.string.reader_saved_offline),
+ Snackbar.LENGTH_LONG,
+ browserApp.getResources().getString(R.string.reader_switch_to_bookmarks),
+ callback,
+ iconDownloaded,
+ ContextCompat.getColor(browserApp, R.color.link_blue),
+ Color.WHITE);
+ }
+}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -503,16 +503,17 @@ gbjar.sources += ['java/org/mozilla/geck
'preferences/SearchEnginePreference.java',
'preferences/SearchPreferenceCategory.java',
'preferences/SetHomepagePreference.java',
'preferences/SyncPreference.java',
'PrefsHelper.java',
'PrintHelper.java',
'PrivateTab.java',
'promotion/AddToHomeScreenPromotion.java',
+ 'promotion/BookmarkStateChangeDelegate.java',
'promotion/HomeScreenPrompt.java',
'promotion/SimpleHelperUI.java',
'prompts/ColorPickerInput.java',
'prompts/IconGridInput.java',
'prompts/IntentChooserPrompt.java',
'prompts/IntentHandler.java',
'prompts/Prompt.java',
'prompts/PromptInput.java',