Bug 1377286 - Allow pinning items to Activity Stream from other home panels r=sebastian
Menu item to pin/unpin items from Bookmarks and History panels is only displayed when
Activity Stream is enabled.
MozReview-Commit-ID: Ko3xmpF2R53
--- a/mobile/android/app/src/main/res/menu/home_contextmenu.xml
+++ b/mobile/android/app/src/main/res/menu/home_contextmenu.xml
@@ -27,15 +27,18 @@
android:title="@string/contextmenu_top_sites_unpin"/>
<item android:id="@+id/home_edit_bookmark"
android:title="@string/contextmenu_edit_bookmark"/>
<item android:id="@+id/home_remove"
android:title="@string/contextmenu_remove"/>
+ <item android:id="@+id/home_as_pin"
+ android:title="@string/contextmenu_top_sites_pin"/>
+
<item android:id="@+id/home_add_to_launcher"
android:title="@string/contextmenu_add_to_launcher"/>
<item android:id="@+id/home_set_as_homepage"
android:title="@string/contextmenu_set_as_homepage"/>
</menu>
--- a/mobile/android/base/java/org/mozilla/gecko/home/HomeContextMenuInfo.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/HomeContextMenuInfo.java
@@ -4,16 +4,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.home;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.util.StringUtils;
import android.database.Cursor;
+import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ExpandableListAdapter;
import android.widget.ListAdapter;
/**
* A ContextMenuInfo for HomeListView
@@ -22,16 +23,18 @@ public class HomeContextMenuInfo extends
public String url;
public String title;
/* package-private */ boolean isFolder;
/* package-private */ int historyId = -1;
public int bookmarkId = -1;
public RemoveItemType itemType = null;
+ /* package-private */ @Nullable Boolean isAsPinned;
+
// Item type to be handled with "Remove" selection.
/* package-private */ enum RemoveItemType {
BOOKMARKS, COMBINED, HISTORY
}
public HomeContextMenuInfo(View targetView, int position, long id) {
super(targetView, position, id);
}
@@ -50,16 +53,20 @@ public class HomeContextMenuInfo extends
/* package-private */ boolean hasPartnerBookmarkId() {
return bookmarkId <= BrowserContract.Bookmarks.FAKE_PARTNER_BOOKMARKS_START;
}
/* package-private */ boolean canRemove() {
return hasBookmarkId() || hasHistoryId() || hasPartnerBookmarkId();
}
+ /* package-private */ void updateAsPinned(boolean pinnedState) {
+ isAsPinned = pinnedState;
+ }
+
private boolean hasHistoryId() {
return historyId > -1;
}
/**
* Interface for creating ContextMenuInfo instances from cursors.
*/
public interface Factory {
--- a/mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
@@ -11,16 +11,17 @@ import java.util.EnumSet;
import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.IntentHelper;
import org.mozilla.gecko.R;
import org.mozilla.gecko.SnackbarBuilder;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
+import org.mozilla.gecko.activitystream.ActivityStream;
import org.mozilla.gecko.bookmarks.BookmarkUtils;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.BrowserContract.SuggestedSites;
import org.mozilla.gecko.distribution.PartnerBookmarksProviderProxy;
import org.mozilla.gecko.home.HomeContextMenuInfo.RemoveItemType;
import org.mozilla.gecko.home.HomePager.OnUrlOpenInBackgroundListener;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.home.TopSitesGridView.TopSitesGridContextMenuInfo;
@@ -157,17 +158,17 @@ public abstract class HomeFragment exten
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
if (!(menuInfo instanceof HomeContextMenuInfo)) {
return;
}
- HomeContextMenuInfo info = (HomeContextMenuInfo) menuInfo;
+ final HomeContextMenuInfo info = (HomeContextMenuInfo) menuInfo;
// Don't show the context menu for folders if full bookmark management isn't enabled.
final boolean enableFullBookmarkManagement = BookmarkUtils.isEnabled(getContext());
if (info.isFolder && !enableFullBookmarkManagement) {
return;
}
MenuInflater inflater = new MenuInflater(view.getContext());
@@ -204,17 +205,51 @@ public abstract class HomeFragment exten
// Hide unused menu items for bookmark folder.
if (info.isFolder) {
menu.findItem(R.id.home_open_new_tab).setVisible(false);
menu.findItem(R.id.home_open_private_tab).setVisible(false);
menu.findItem(R.id.home_copyurl).setVisible(false);
menu.findItem(R.id.home_share).setVisible(false);
menu.findItem(R.id.home_add_to_launcher).setVisible(false);
menu.findItem(R.id.home_set_as_homepage).setVisible(false);
+
+ menu.findItem(R.id.home_as_pin).setVisible(false);
+ return;
}
+
+ // If Activity Stream is disabled, simply hide "AS Pin" menu item as classic Top Sites do not
+ // support pinning from outside of the Top Site tiles.
+ if (!ActivityStream.isEnabled(getContext())) {
+ menu.findItem(R.id.home_as_pin).setVisible(false);
+ return;
+ }
+
+ // Asynchronously update pin state for this item.
+ // This code should be superseded by context menu unification work in Bug 1377292.
+ final MenuItem asPinItem = menu.findItem(R.id.home_as_pin);
+ // Do not let user interact with this menu item before we figure out pinned state.
+ asPinItem.setEnabled(false);
+
+ (new UIAsyncTask.WithoutParams<Boolean>(ThreadUtils.getBackgroundHandler()) {
+ @Override
+ protected Boolean doInBackground() {
+ return BrowserDB.from(getContext()).isPinnedForAS(
+ getContext().getContentResolver(), info.url);
+ }
+
+ @Override
+ protected void onPostExecute(Boolean hasPin) {
+ if (hasPin) {
+ asPinItem.setTitle(R.string.contextmenu_top_sites_unpin);
+ }
+
+ info.updateAsPinned(hasPin);
+ asPinItem.setEnabled(true);
+ }
+ }).execute();
}
@Override
public boolean onContextItemSelected(MenuItem item) {
// onContextItemSelected() is first dispatched to the activity and
// then dispatched to its fragments. Since fragments cannot "override"
// menu item selection handling, it's better to avoid menu id collisions
// between the activity and its fragments.
@@ -322,16 +357,22 @@ public abstract class HomeFragment exten
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(context);
final SharedPreferences.Editor editor = prefs.edit();
editor.putString(GeckoPreferences.PREFS_HOMEPAGE, info.url);
editor.apply();
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.CONTEXT_MENU,
getResources().getResourceEntryName(itemId));
return true;
}
+
+ if (itemId == R.id.home_as_pin) {
+ new ToggleASPinTask(getActivity(), info).execute();
+ return true;
+ }
+
return false;
}
@Override
public void setUserVisibleHint (boolean isVisibleToUser) {
if (isVisibleToUser == getUserVisibleHint()) {
return;
}
@@ -394,16 +435,69 @@ public abstract class HomeFragment exten
if (!canLoad() || mIsLoaded) {
return;
}
load();
mIsLoaded = true;
}
+ private static class ToggleASPinTask extends UIAsyncTask.WithoutParams<Void> {
+ private final WeakReference<Activity> activityWeakReference;
+ private final Context context;
+ private final HomeContextMenuInfo info;
+ private final BrowserDB db;
+ private final ContentResolver cr;
+ private final boolean toggleWillPin;
+
+ ToggleASPinTask(Activity activity, HomeContextMenuInfo info) {
+ super(ThreadUtils.getBackgroundHandler());
+
+ this.activityWeakReference = new WeakReference<Activity>(activity);
+ this.context = activity.getApplicationContext();
+ this.db = BrowserDB.from(context);
+ this.info = info;
+ this.cr = context.getContentResolver();
+
+ if (info.isAsPinned == null) {
+ throw new IllegalStateException("Tried changing pinned state before it's determined.");
+ }
+
+ this.toggleWillPin = !info.isAsPinned;
+ }
+
+ @Override
+ protected Void doInBackground() {
+ if (toggleWillPin) {
+ db.pinSiteForAS(cr, info.url, info.title);
+ } else {
+ db.unpinSiteForAS(cr, info.url);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void aVoid) {
+ final Activity activity = activityWeakReference.get();
+ if (activity == null || activity.isFinishing()) {
+ return;
+ }
+
+ int messageId = R.string.home_pinned_site;
+ if (!toggleWillPin) {
+ messageId = R.string.home_unpinned_site;
+ }
+
+ SnackbarBuilder.builder(activity)
+ .message(messageId)
+ .duration(Snackbar.LENGTH_LONG)
+ .buildAndShow();
+ }
+ }
+
static class RemoveItemTask extends UIAsyncTask.WithoutParams<Void> {
private final WeakReference<Activity> activityWeakReference;
private final Context context;
private final HomeContextMenuInfo info;
private final int position;
private final BrowserDB db;
/**
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -620,16 +620,21 @@
<!ENTITY home_remote_tabs_many_hidden_devices "&formatD; devices hidden">
<!-- Localization note (home_remote_tabs_hidden_devices_title) : This is the
title of a dialog; we expect more than one device. -->
<!ENTITY home_remote_tabs_hidden_devices_title "Hidden devices">
<!-- Localization note (home_remote_tabs_unhide_selected_devices) : This is
the text of a button; we expect more than one device. -->
<!ENTITY home_remote_tabs_unhide_selected_devices "Unhide selected devices">
+<!-- Localization note (home_pinned_site) : This is a snackbar label displayed after
+ a site is pinned or unpinned. -->
+<!ENTITY home_pinned_site "Pinned site">
+<!ENTITY home_unpinned_site "Unpinned site">
+
<!ENTITY remote_tabs_panel_moved_title "Where did my tabs go?">
<!ENTITY remote_tabs_panel_moved_desc "We\'ve moved your tabs from other devices into a panel on your home page that can be easily accessed every time you open a new tab.">
<!ENTITY remote_tabs_panel_moved_link "Take me to my new panel.">
<!ENTITY pin_site_dialog_hint "Enter a search keyword">
<!ENTITY filepicker_title "Choose File">
<!ENTITY filepicker_audio_title "Choose or record a sound">
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -476,16 +476,19 @@
<string name="home_most_recent_emptyhint">&home_most_recent_emptyhint2;</string>
<string name="home_default_empty">&home_default_empty;</string>
<string name="home_move_back_to_filter">&home_move_back_to_filter;</string>
<string name="home_remote_tabs_many_hidden_devices">&home_remote_tabs_many_hidden_devices;</string>
<string name="home_remote_tabs_hidden_devices_title">&home_remote_tabs_hidden_devices_title;</string>
<string name="home_remote_tabs_unhide_selected_devices">&home_remote_tabs_unhide_selected_devices;</string>
<string name="pin_site_dialog_hint">&pin_site_dialog_hint;</string>
+ <string name="home_pinned_site">&home_pinned_site;</string>
+ <string name="home_unpinned_site">&home_unpinned_site;</string>
+
<string name="remote_tabs_never_synced">&remote_tabs_never_synced;</string>
<string name="filepicker_title">&filepicker_title;</string>
<string name="filepicker_audio_title">&filepicker_audio_title;</string>
<string name="filepicker_image_title">&filepicker_image_title;</string>
<string name="filepicker_video_title">&filepicker_video_title;</string>
<!-- Default bookmarks. We used to use bookmark titles shared with XUL from mobile's