Bug 1232439 - Part 3: Implement full-page bookmark edit dialog. r=ahunt,Grisha
authorJing-wei Wu <topwu.tw@gmail.com>
Thu, 20 Apr 2017 09:04:28 +0800
changeset 566312 5a0b0722bb130a9fc1c696ae0f538725eebe3843
parent 566311 59af50c06ae546baa5124dcdf814260acf4ff311
child 566313 0cbed2ce192a796646ed92ad4ad91fa3847ea95e
push id55180
push userjjong@mozilla.com
push dateFri, 21 Apr 2017 09:36:13 +0000
reviewersahunt, Grisha
bugs1232439
milestone55.0a1
Bug 1232439 - Part 3: Implement full-page bookmark edit dialog. r=ahunt,Grisha MozReview-Commit-ID: 8wB9CEptVeu
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/bookmarks/BookmarkEditFragment.java
mobile/android/base/java/org/mozilla/gecko/bookmarks/EditBookmarkTask.java
mobile/android/base/java/org/mozilla/gecko/delegates/BookmarkStateChangeDelegate.java
mobile/android/base/java/org/mozilla/gecko/home/BookmarksPanel.java
mobile/android/base/java/org/mozilla/gecko/home/BrowserSearch.java
mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/moz.build
mobile/android/base/resources/drawable-hdpi/arrow.png
mobile/android/base/resources/drawable-ldrtl-hdpi/arrow.png
mobile/android/base/resources/drawable-ldrtl-xhdpi/arrow.png
mobile/android/base/resources/drawable-ldrtl-xxhdpi/arrow.png
mobile/android/base/resources/drawable-xhdpi/arrow.png
mobile/android/base/resources/drawable-xxhdpi/arrow.png
mobile/android/base/resources/layout/bookmark_edit_with_full_page.xml
mobile/android/base/resources/menu/bookmark_edit_menu.xml
mobile/android/base/resources/values/themes.xml
mobile/android/base/strings.xml.in
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -17,16 +17,19 @@ import android.support.annotation.UiThre
 import org.mozilla.gecko.activitystream.ActivityStream;
 import org.mozilla.gecko.adjust.AdjustBrowserAppDelegate;
 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;
 import org.mozilla.gecko.animation.ViewHelper;
+import org.mozilla.gecko.bookmarks.BookmarkEditFragment;
+import org.mozilla.gecko.bookmarks.BookmarkUtils;
+import org.mozilla.gecko.bookmarks.EditBookmarkTask;
 import org.mozilla.gecko.cleanup.FileCleanupController;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.SuggestedSites;
 import org.mozilla.gecko.delegates.BrowserAppDelegate;
 import org.mozilla.gecko.delegates.OfflineTabStatusDelegate;
 import org.mozilla.gecko.delegates.ScreenshotDelegate;
 import org.mozilla.gecko.distribution.Distribution;
@@ -158,18 +161,16 @@ import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.animation.Interpolator;
 import android.widget.Button;
 import android.widget.ListView;
 import android.widget.RelativeLayout;
 import android.widget.ViewFlipper;
 import org.mozilla.gecko.switchboard.AsyncConfigLoader;
 import org.mozilla.gecko.switchboard.SwitchBoard;
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.net.URLEncoder;
 import java.util.Arrays;
 import java.util.Collections;
@@ -186,17 +187,18 @@ public class BrowserApp extends GeckoApp
                                    View.OnKeyListener,
                                    LayerView.DynamicToolbarListener,
                                    BrowserSearch.OnSearchListener,
                                    BrowserSearch.OnEditSuggestionListener,
                                    OnUrlOpenListener,
                                    OnUrlOpenInBackgroundListener,
                                    AnchoredPopup.OnVisibilityChangeListener,
                                    ActionModePresenter,
-                                   LayoutInflater.Factory {
+                                   LayoutInflater.Factory,
+                                   BookmarkEditFragment.Callbacks {
     private static final String LOGTAG = "GeckoBrowserApp";
 
     private static final int TABS_ANIMATION_DURATION = 450;
 
     // Intent String extras used to specify custom Switchboard configurations.
     private static final String INTENT_KEY_SWITCHBOARD_SERVER = "switchboard-server";
 
     // TODO: Replace with kinto endpoint.
@@ -4172,9 +4174,27 @@ public class BrowserApp extends GeckoApp
         }
 
         if (GeckoProfile.get(this).inGuestMode()) {
             Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH, method, "guest");
         } else if (Restrictions.isRestrictedProfile(this)) {
             Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH, method, "restricted");
         }
     }
+
+    /**
+     * Launch edit bookmark dialog. The {@link BookmarkEditFragment} needs to be started by an activity
+     * that implements the interface({@link BookmarkEditFragment.Callbacks}) for handling callback method.
+     */
+    public void showEditBookmarkDialog(String pageUrl) {
+        if (BookmarkUtils.isEnabled(this)) {
+            BookmarkEditFragment dialog = BookmarkEditFragment.newInstance(pageUrl);
+            dialog.show(getSupportFragmentManager(), "edit-bookmark");
+        } else {
+            new EditBookmarkDialog(this).show(pageUrl);
+        }
+    }
+
+    @Override
+    public void onEditBookmark(@NonNull Bundle bundle) {
+        new EditBookmarkTask(this, bundle).execute();
+    }
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/bookmarks/BookmarkEditFragment.java
@@ -0,0 +1,498 @@
+/* -*- 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.bookmarks;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.Nullable;
+import android.support.design.widget.TextInputLayout;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.AsyncTaskLoader;
+import android.support.v4.content.Loader;
+import android.support.v7.widget.Toolbar;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.db.BrowserContract.Bookmarks;
+import org.mozilla.gecko.db.BrowserDB;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * A dialog fragment that allows editing bookmark's url, title and changing the parent."
+ */
+public class BookmarkEditFragment extends DialogFragment {
+
+    private static final String ARG_ID = "id";
+    private static final String ARG_URL = "url";
+    private static final String ARG_BOOKMARK = "bookmark";
+
+    private long bookmarkId;
+    private String url;
+    private Bookmark bookmark;
+
+    private Toolbar toolbar;
+    private EditText nameText;
+    private TextInputLayout locationLayout;
+    private EditText locationText;
+    private EditText folderText;
+
+    public interface Callbacks {
+        /**
+         * A callback method to tell caller that bookmark has been modified.
+         * Caller takes charge for the change(e.g. update database).
+         */
+        void onEditBookmark(Bundle bundle);
+    }
+    private Callbacks callbacks;
+
+    public static BookmarkEditFragment newInstance(long id) {
+        final Bundle args = new Bundle();
+        args.putLong(ARG_ID, id);
+
+        return newInstance(args);
+    }
+
+    public static BookmarkEditFragment newInstance(String url) {
+        final Bundle args = new Bundle();
+        args.putString(ARG_URL, url);
+
+        return newInstance(args);
+    }
+
+    private static BookmarkEditFragment newInstance(Bundle args) {
+        final BookmarkEditFragment fragment = new BookmarkEditFragment();
+        fragment.setArguments(args);
+
+        return fragment;
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+
+        Fragment fragment = getTargetFragment();
+        if (fragment != null && fragment instanceof Callbacks) {
+            callbacks = (Callbacks) fragment;
+        } else if (context instanceof Callbacks) {
+            callbacks = (Callbacks) context;
+        }
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Apply DialogWhenLarge theme
+        setStyle(DialogFragment.STYLE_NO_TITLE, R.style.Bookmark_Gecko);
+
+        Bundle args = getArguments();
+        bookmarkId = args.getLong(ARG_ID);
+        url = args.getString(ARG_URL);
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.bookmark_edit_with_full_page, container);
+        toolbar = (Toolbar) view.findViewById(R.id.toolbar);
+        nameText = (EditText) view.findViewById(R.id.edit_bookmark_name);
+        locationLayout = (TextInputLayout) view.findViewById(R.id.edit_bookmark_location_layout);
+        locationText = (EditText) view.findViewById(R.id.edit_bookmark_location);
+        folderText = (EditText) view.findViewById(R.id.edit_parent_folder);
+
+        toolbar.inflateMenu(R.menu.bookmark_edit_menu);
+        toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
+            @Override
+            public boolean onMenuItemClick(MenuItem item) {
+                switch (item.getItemId()) {
+                    case R.id.done:
+                        final String newUrl = locationText.getText().toString().trim();
+                        final String newTitle = nameText.getText().toString();
+                        if (callbacks != null) {
+                            if (TextUtils.equals(newTitle, bookmark.originalTitle) &&
+                                TextUtils.equals(newUrl, bookmark.originalUrl) &&
+                                bookmark.parentId == bookmark.originalParentId) {
+                                // Nothing changed, skip callback.
+                                break;
+                            }
+
+                            final Bundle bundle = new Bundle();
+                            bundle.putLong(Bookmarks._ID, bookmark.id);
+                            bundle.putString(Bookmarks.TITLE, newTitle);
+                            bundle.putString(Bookmarks.URL, newUrl);
+                            bundle.putString(Bookmarks.KEYWORD, bookmark.keyword);
+                            if (bookmark.parentId != bookmark.originalParentId) {
+                                bundle.putLong(Bookmarks.PARENT, bookmark.parentId);
+                                bundle.putLong(BrowserContract.PARAM_OLD_BOOKMARK_PARENT, bookmark.originalParentId);
+                            }
+
+                            callbacks.onEditBookmark(bundle);
+                        }
+                        break;
+                }
+
+                dismiss();
+                return true;
+            }
+        });
+        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                dismiss();
+            }
+        });
+
+        return view;
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        if (savedInstanceState != null) {
+            final Bookmark bookmark = savedInstanceState.getParcelable(ARG_BOOKMARK);
+            if (bookmark != null) {
+                invalidateView(bookmark);
+                return;
+            }
+        }
+
+        getLoaderManager().initLoader(0, null, new BookmarkLoaderCallbacks());
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+
+        getLoaderManager().destroyLoader(0);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        if (bookmark != null) {
+            bookmark.url = locationText.getText().toString().trim();
+            bookmark.title = nameText.getText().toString();
+            bookmark.folder = folderText.getText().toString();
+            outState.putParcelable(ARG_BOOKMARK, bookmark);
+        }
+
+        super.onSaveInstanceState(outState);
+    }
+
+    private void invalidateView(Bookmark bookmark) {
+        this.bookmark = bookmark;
+
+        nameText.setText(bookmark.title);
+
+        if (bookmark.type == Bookmarks.TYPE_FOLDER) {
+            locationLayout.setVisibility(View.GONE);
+        } else {
+            locationLayout.setVisibility(View.VISIBLE);
+        }
+        locationText.setText(bookmark.url);
+
+        if (Bookmarks.MOBILE_FOLDER_GUID.equals(bookmark.guid)) {
+            folderText.setText(R.string.bookmarks_folder_mobile);
+        } else {
+            folderText.setText(bookmark.folder);
+        }
+
+        // Enable menu item after bookmark is set to view
+        final MenuItem doneItem = toolbar.getMenu().findItem(R.id.done);
+        doneItem.setEnabled(true);
+
+        // Add a TextWatcher to prevent invalid input(e.g. empty string).
+        if (bookmark.type == Bookmarks.TYPE_FOLDER) {
+            BookmarkTextWatcher nameTextWatcher = new BookmarkTextWatcher(doneItem);
+            nameText.addTextChangedListener(nameTextWatcher);
+        } else {
+            BookmarkTextWatcher locationTextWatcher = new BookmarkTextWatcher(doneItem);
+            locationText.addTextChangedListener(locationTextWatcher);
+        }
+    }
+
+    /**
+     * A private struct to make it easier to pass bookmark data across threads
+     */
+    private static class Bookmark implements Parcelable {
+        // Cannot be modified in this fragment.
+        final long id;
+        final String keyword;
+        final int type; // folder or bookmark
+        final String guid;
+        final String originalTitle;
+        final String originalUrl;
+        final long originalParentId;
+        final String originalFolder;
+
+        // Can be modified in this fragment.
+        String title;
+        String url;
+        long parentId;
+        String folder;
+
+        public Bookmark(long id, String url, String title, String keyword, long parentId,
+                        String folder, int type, String guid) {
+            this(id, url, title, keyword, parentId, folder, type, guid, url, title, parentId, folder);
+        }
+
+        private Bookmark(long id, String originalUrl, String originalTitle, String keyword,
+                         long originalParentId, String originalFolder, int type, String guid,
+                         String modifiedUrl, String modifiedTitle, long modifiedParentId, String modifiedFolder) {
+            this.id = id;
+            this.originalUrl = originalUrl;
+            this.originalTitle = originalTitle;
+            this.keyword = keyword;
+            this.originalParentId = originalParentId;
+            this.originalFolder = originalFolder;
+            this.type = type;
+            this.guid = guid;
+
+            this.url = modifiedUrl;
+            this.title = modifiedTitle;
+            this.parentId = modifiedParentId;
+            this.folder = modifiedFolder;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeLong(id);
+            parcel.writeString(url);
+            parcel.writeString(title);
+            parcel.writeString(keyword);
+            parcel.writeLong(parentId);
+            parcel.writeString(folder);
+            parcel.writeInt(type);
+            parcel.writeString(guid);
+            parcel.writeString(originalUrl);
+            parcel.writeString(originalTitle);
+            parcel.writeLong(originalParentId);
+            parcel.writeString(originalFolder);
+        }
+
+        public static final Creator<Bookmark> CREATOR = new Creator<Bookmark>() {
+            @Override
+            public Bookmark createFromParcel(final Parcel source) {
+                final long id = source.readLong();
+                final String modifiedUrl = source.readString();
+                final String modifiedTitle = source.readString();
+                final String keyword = source.readString();
+                final long modifiedParentId = source.readLong();
+                final String modifiedFolder = source.readString();
+                final int type = source.readInt();
+                final String guid = source.readString();
+
+                final String originalUrl = source.readString();
+                final String originalTitle = source.readString();
+                final long originalParentId = source.readLong();
+                final String originalFolder = source.readString();
+
+                return new Bookmark(id, originalUrl, originalTitle, keyword, originalParentId, originalFolder,
+                                    type, guid, modifiedUrl, modifiedTitle, modifiedParentId, modifiedFolder);
+            }
+
+            @Override
+            public Bookmark[] newArray(final int size) {
+                return new Bookmark[size];
+            }
+        };
+    }
+
+    private class BookmarkLoaderCallbacks implements LoaderManager.LoaderCallbacks<Bookmark> {
+        @Override
+        public Loader<Bookmark> onCreateLoader(int id, Bundle args) {
+            return new BookmarkLoader(getContext(), bookmarkId, url);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Bookmark> loader, final Bookmark bookmark) {
+            if (bookmark == null) {
+                return;
+            }
+
+            invalidateView(bookmark);
+        }
+
+        @Override
+        public void onLoaderReset(Loader<Bookmark> loader) {
+        }
+    }
+
+    /**
+     * An AsyncTaskLoader to load {@link Bookmark} from a cursor.
+     */
+    private static class BookmarkLoader extends AsyncTaskLoader<Bookmark> {
+        private final long bookmarkId;
+        private final String url;
+        private final ContentResolver contentResolver;
+        private final BrowserDB db;
+        private Bookmark bookmark;
+
+        private BookmarkLoader(Context context, long id, String url) {
+            super(context);
+
+            this.bookmarkId = id;
+            this.url = url;
+            this.contentResolver = context.getContentResolver();
+            this.db = BrowserDB.from(context);
+        }
+
+        @Override
+        public Bookmark loadInBackground() {
+            final Cursor cursor;
+
+            if (url != null) {
+                cursor = db.getBookmarkForUrl(contentResolver, url);
+            } else {
+                cursor = db.getBookmarkById(contentResolver, bookmarkId);
+            }
+            if (cursor == null) {
+                return null;
+            }
+
+            Bookmark bookmark = null;
+            try {
+                if (cursor.moveToFirst()) {
+                    final long id = cursor.getLong(cursor.getColumnIndexOrThrow(Bookmarks._ID));
+                    final String url = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.URL));
+                    final String title = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.TITLE));
+                    final String keyword = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.KEYWORD));
+
+                    final long parentId = cursor.getLong(cursor.getColumnIndexOrThrow(Bookmarks.PARENT));
+                    final String parentName = queryParentName(parentId);
+
+                    final int type = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE));
+                    final String guid = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.GUID));
+                    bookmark = new Bookmark(id, url, title, keyword, parentId, parentName, type, guid);
+                }
+            } finally {
+                cursor.close();
+            }
+            return bookmark;
+        }
+
+        private String queryParentName(long folderId) {
+            Cursor cursor = db.getBookmarkById(contentResolver, folderId);
+            if (cursor == null) {
+                return "";
+            }
+
+            String folderName = "";
+            try {
+                if (cursor.moveToFirst()) {
+                    final String guid = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.GUID));
+                    if (Bookmarks.MOBILE_FOLDER_GUID.equals(guid)) {
+                        folderName = getContext().getString(R.string.bookmarks_folder_mobile);
+                    } else {
+                        folderName = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.TITLE));
+                    }
+                }
+            } finally {
+                cursor.close();
+            }
+            return folderName;
+        }
+
+        @Override
+        public void deliverResult(Bookmark bookmark) {
+            if (isReset()) {
+                this.bookmark = null;
+                return;
+            }
+
+            this.bookmark = bookmark;
+
+            if (isStarted()) {
+                super.deliverResult(bookmark);
+            }
+        }
+
+        @Override
+        protected void onStartLoading() {
+            if (bookmark != null) {
+                deliverResult(bookmark);
+            }
+
+            if (takeContentChanged() || bookmark == null) {
+                forceLoad();
+            }
+        }
+
+        @Override
+        protected void onStopLoading() {
+            cancelLoad();
+        }
+
+        @Override
+        public void onCanceled(Bookmark bookmark) {
+            this.bookmark = null;
+        }
+
+        @Override
+        protected void onReset() {
+            super.onReset();
+
+            // Ensure the loader is stopped.
+            onStopLoading();
+
+            bookmark = null;
+        }
+    }
+
+    /**
+     * This text watcher enables the menu item if the dialog contains valid information, or disables otherwise.
+     */
+    private class BookmarkTextWatcher implements TextWatcher {
+        // A stored reference to the dialog containing the text field being watched.
+        private final WeakReference<MenuItem> doneItemWeakReference;
+
+        // Whether or not the menu item should be enabled.
+        private boolean enabled = true;
+
+        private BookmarkTextWatcher(MenuItem doneItem) {
+            doneItemWeakReference = new WeakReference<>(doneItem);
+        }
+
+        public boolean isEnabled() {
+            return enabled;
+        }
+
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+            // Disables the menu item if the input field is empty.
+            final boolean enabled = (s.toString().trim().length() > 0);
+
+            final MenuItem doneItem = doneItemWeakReference.get();
+            if (doneItem != null) {
+                doneItem.setEnabled(enabled);
+            }
+        }
+
+        @Override
+        public void afterTextChanged(Editable s) {}
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/bookmarks/EditBookmarkTask.java
@@ -0,0 +1,68 @@
+/* -*- 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.bookmarks;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.SnackbarBuilder;
+import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.gecko.util.UIAsyncTask;
+
+import java.lang.ref.WeakReference;
+
+public class EditBookmarkTask extends UIAsyncTask.WithoutParams<Void> {
+    private final WeakReference<Activity> activityWeakReference;
+    private final BrowserDB db;
+    private final ContentResolver contentResolver;
+    private final Bundle bundle;
+
+    public EditBookmarkTask(Activity activity, @NonNull Bundle bundle) {
+        super(ThreadUtils.getBackgroundHandler());
+
+        this.activityWeakReference = new WeakReference<>(activity);
+        this.db = BrowserDB.from(activity);
+        this.contentResolver = activity.getContentResolver();
+        this.bundle = bundle;
+    }
+
+    @Override
+    public Void doInBackground() {
+        final long bookmarkId = bundle.getLong(BrowserContract.Bookmarks._ID);
+        final String url = bundle.getString(BrowserContract.Bookmarks.URL);
+        final String title = bundle.getString(BrowserContract.Bookmarks.TITLE);
+        final String keyword = bundle.getString(BrowserContract.Bookmarks.KEYWORD);
+
+        if (bundle.containsKey(BrowserContract.Bookmarks.PARENT) &&
+            bundle.containsKey(BrowserContract.PARAM_OLD_BOOKMARK_PARENT)) {
+            final long newParentId = bundle.getLong(BrowserContract.Bookmarks.PARENT);
+            final long oldParentId = bundle.getLong(BrowserContract.PARAM_OLD_BOOKMARK_PARENT);
+            db.updateBookmark(contentResolver, bookmarkId, url, title, keyword, newParentId, oldParentId);
+        } else {
+            db.updateBookmark(contentResolver, bookmarkId, url, title, keyword);
+        }
+        return null;
+    }
+
+    @Override
+    public void onPostExecute(Void result) {
+        final Activity activity = activityWeakReference.get();
+        if (activity == null || activity.isFinishing()) {
+            return;
+        }
+
+        SnackbarBuilder.builder(activity)
+                .message(R.string.bookmark_updated)
+                .duration(Snackbar.LENGTH_LONG)
+                .buildAndShow();
+    }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/delegates/BookmarkStateChangeDelegate.java
+++ b/mobile/android/base/java/org/mozilla/gecko/delegates/BookmarkStateChangeDelegate.java
@@ -12,17 +12,16 @@ import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.support.design.widget.Snackbar;
 import android.support.v4.content.ContextCompat;
 import android.view.View;
 import android.widget.ListView;
 
 import org.mozilla.gecko.AboutPages;
 import org.mozilla.gecko.BrowserApp;
-import org.mozilla.gecko.EditBookmarkDialog;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SnackbarBuilder;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
@@ -166,17 +165,17 @@ public class BookmarkStateChangeDelegate
             public void onPromptFinished(final GeckoBundle result) {
                 final int itemId = result.getInt("button", -1);
 
                 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());
+                    browserApp.showEditBookmarkDialog(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();
--- a/mobile/android/base/java/org/mozilla/gecko/home/BookmarksPanel.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/BookmarksPanel.java
@@ -4,18 +4,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 
+import org.mozilla.gecko.EditBookmarkDialog;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.bookmarks.BookmarkEditFragment;
+import org.mozilla.gecko.bookmarks.BookmarkUtils;
+import org.mozilla.gecko.bookmarks.EditBookmarkTask;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.distribution.PartnerBookmarksProviderProxy;
 import org.mozilla.gecko.home.BookmarksListAdapter.FolderInfo;
 import org.mozilla.gecko.home.BookmarksListAdapter.OnRefreshFolderListener;
 import org.mozilla.gecko.home.BookmarksListAdapter.RefreshType;
 import org.mozilla.gecko.home.HomeContextMenuInfo.RemoveItemType;
@@ -27,27 +31,29 @@ import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.Cursor;
 import android.database.MergeCursor;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.Loader;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 /**
  * A page in about:home that displays a ListView of bookmarks.
  */
-public class BookmarksPanel extends HomeFragment {
+public class BookmarksPanel extends HomeFragment implements BookmarkEditFragment.Callbacks {
     public static final String LOGTAG = "GeckoBookmarksPanel";
 
     // Cursor loader ID for list of bookmarks.
     private static final int LOADER_ID_BOOKMARKS_LIST = 0;
 
     // Information about the target bookmarks folder.
     private static final String BOOKMARKS_FOLDER_INFO = "folder_info";
 
@@ -157,16 +163,48 @@ public class BookmarksPanel extends Home
         mList.setAdapter(mListAdapter);
 
         // Create callbacks before the initial loader is started.
         mLoaderCallbacks = new CursorLoaderCallbacks();
         loadIfVisible();
     }
 
     @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        if (super.onContextItemSelected(item)) {
+            // HomeFragment was able to handle to selected item.
+            return true;
+        }
+
+        final ContextMenuInfo menuInfo = item.getMenuInfo();
+        if (!(menuInfo instanceof HomeContextMenuInfo)) {
+            return false;
+        }
+
+        final HomeContextMenuInfo info = (HomeContextMenuInfo) menuInfo;
+
+        final int itemId = item.getItemId();
+        final Context context = getContext();
+
+        if (itemId == R.id.home_edit_bookmark) {
+            if (BookmarkUtils.isEnabled(getContext())) {
+                final BookmarkEditFragment dialog = BookmarkEditFragment.newInstance(info.bookmarkId);
+                dialog.setTargetFragment(this, 0);
+                dialog.show(getFragmentManager(), "edit-bookmark");
+            } else {
+                // UI Dialog associates to the activity context, not the applications'.
+                new EditBookmarkDialog(context).show(info.url);
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
     public void onDestroyView() {
         mList = null;
         mListAdapter = null;
         mEmptyView = null;
         super.onDestroyView();
     }
 
     @Override
@@ -189,16 +227,21 @@ public class BookmarksPanel extends Home
             bundle.putParcelable(BOOKMARKS_REFRESH_TYPE, RefreshType.CHILD);
         } else {
             bundle = null;
         }
 
         getLoaderManager().initLoader(LOADER_ID_BOOKMARKS_LIST, bundle, mLoaderCallbacks);
     }
 
+    @Override
+    public void onEditBookmark(@NonNull Bundle bundle) {
+        new EditBookmarkTask(getActivity(), bundle).execute();
+    }
+
     private void updateUiFromCursor(Cursor c) {
         if ((c == null || c.getCount() == 0) && mEmptyView == null) {
             // Set empty page view. We delay this so that the empty view won't flash.
             final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub);
             mEmptyView = emptyViewStub.inflate();
 
             final ImageView emptyIcon = (ImageView) mEmptyView.findViewById(R.id.home_empty_image);
             emptyIcon.setImageResource(R.drawable.icon_bookmarks_empty);
@@ -300,17 +343,17 @@ public class BookmarksPanel extends Home
                 RefreshType refreshType = (RefreshType) args.getParcelable(BOOKMARKS_REFRESH_TYPE);
                 final int targetPosition = args.getInt(BOOKMARKS_SCROLL_POSITION);
                 return new BookmarksLoader(getActivity(), folderInfo, refreshType, targetPosition);
             }
         }
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
-            BookmarksLoader bl = (BookmarksLoader) loader;
+            final BookmarksLoader bl = (BookmarksLoader) loader;
             mListAdapter.swapCursor(c, bl.getFolderInfo(), bl.getRefreshType());
 
             if (mPanelStateChangeListener != null) {
                 final List<FolderInfo> parentStack = mListAdapter.getParentStack();
                 final Bundle bundle = new Bundle();
 
                 // Bundle likes to store ArrayLists or Arrays, but we've got a generic List (which
                 // is actually an unmodifiable wrapper around a LinkedList). We'll need to do a
@@ -319,17 +362,17 @@ public class BookmarksPanel extends Home
                 bundle.putParcelableArrayList("parentStack", new ArrayList<FolderInfo>(parentStack));
 
                 mPanelStateChangeListener.onStateChanged(bundle);
             }
 
             // BrowserDB updates (e.g. through sync, or when opening a new tab) will trigger
             // a refresh which reuses the same loader - in that case we don't want to reset
             // the scroll position again.
-            int currentLoaderHash = bl.hashCode();
+            final int currentLoaderHash = bl.hashCode();
             if (mList != null && currentLoaderHash != mLastLoaderHash) {
                 mList.setSelection(bl.getTargetPosition());
                 mLastLoaderHash = currentLoaderHash;
             }
             updateUiFromCursor(c);
         }
 
         @Override
--- a/mobile/android/base/java/org/mozilla/gecko/home/BrowserSearch.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/BrowserSearch.java
@@ -437,17 +437,17 @@ public class BrowserSearch extends HomeF
         final Context context = getActivity();
 
         final int itemId = item.getItemId();
 
         if (itemId == R.id.browsersearch_remove) {
             // Position for Top Sites grid items, but will always be -1 since this is only for BrowserSearch result
             final int position = -1;
 
-            new RemoveItemByUrlTask(context, info.url, info.itemType, position).execute();
+            new RemoveItemTask(getActivity(), info, position).execute();
             return true;
         }
 
         return false;
     }
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
--- a/mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
@@ -1,28 +1,27 @@
 /* -*- 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.home;
 
+import java.lang.ref.WeakReference;
 import java.util.EnumSet;
 
-import org.mozilla.gecko.EditBookmarkDialog;
 import org.mozilla.gecko.GeckoAppShell;
 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.db.BrowserContract;
 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;
 import org.mozilla.gecko.preferences.GeckoPreferences;
@@ -291,30 +290,24 @@ public abstract class HomeFragment exten
 
             mUrlOpenInBackgroundListener.onUrlOpenInBackground(url, flags);
 
             Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.CONTEXT_MENU);
 
             return true;
         }
 
-        if (itemId == R.id.home_edit_bookmark) {
-            // UI Dialog associates to the activity context, not the applications'.
-            new EditBookmarkDialog(context).show(info.url);
-            return true;
-        }
-
         if (itemId == R.id.home_remove) {
             // For Top Sites grid items, position is required in case item is Pinned.
             final int position = info instanceof TopSitesGridContextMenuInfo ? info.position : -1;
 
             if (info.hasPartnerBookmarkId()) {
-                new RemovePartnerBookmarkTask(context, info.bookmarkId).execute();
+                new RemovePartnerBookmarkTask(getActivity(), info.bookmarkId).execute();
             } else {
-                new RemoveItemByUrlTask(context, info.url, info.itemType, position).execute();
+                new RemoveItemTask(getActivity(), info, position).execute();
             }
             return true;
         }
 
         if (itemId == R.id.home_set_as_homepage) {
             final SharedPreferences prefs = GeckoSharedPrefs.forProfile(context);
             final SharedPreferences.Editor editor = prefs.edit();
             editor.putString(GeckoPreferences.PREFS_HOMEPAGE, info.url);
@@ -390,124 +383,137 @@ public abstract class HomeFragment exten
         if (!canLoad() || mIsLoaded) {
             return;
         }
 
         load();
         mIsLoaded = true;
     }
 
-    protected static class RemoveItemByUrlTask extends UIAsyncTask.WithoutParams<Void> {
-        private final Context mContext;
-        private final String mUrl;
-        private final RemoveItemType mType;
-        private final int mPosition;
-        private final BrowserDB mDB;
+    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;
 
         /**
-         * Remove bookmark/history/reading list type item by url, and also unpin the
+         * Remove bookmark/history/reading list type item, and also unpin the
          * Top Sites grid item at index <code>position</code>.
          */
-        public RemoveItemByUrlTask(Context context, String url, RemoveItemType type, int position) {
+        RemoveItemTask(Activity activity, HomeContextMenuInfo info, int position) {
             super(ThreadUtils.getBackgroundHandler());
 
-            mContext = context;
-            mUrl = url;
-            mType = type;
-            mPosition = position;
-            mDB = BrowserDB.from(context);
+            this.activityWeakReference = new WeakReference<>(activity);
+            this.context = activity.getApplicationContext();
+            this.info = info;
+            this.position = position;
+            this.db = BrowserDB.from(context);
         }
 
         @Override
         public Void doInBackground() {
-            ContentResolver cr = mContext.getContentResolver();
+            ContentResolver cr = context.getContentResolver();
 
-            if (mPosition > -1) {
-                mDB.unpinSite(cr, mPosition);
-                if (mDB.hideSuggestedSite(mUrl)) {
+            if (position > -1) {
+                db.unpinSite(cr, position);
+                if (db.hideSuggestedSite(info.url)) {
                     cr.notifyChange(SuggestedSites.CONTENT_URI, null);
                 }
             }
 
-            switch (mType) {
+            final RemoveItemType type = info.itemType;
+            switch (type) {
                 case BOOKMARKS:
                     removeBookmark(cr);
                     break;
 
                 case HISTORY:
                     removeHistory(cr);
                     break;
 
                 case COMBINED:
                     removeBookmark(cr);
                     removeHistory(cr);
                     break;
 
                 default:
-                    Log.e(LOGTAG, "Can't remove item type " + mType.toString());
+                    Log.e(LOGTAG, "Can't remove item type " + type.toString());
                     break;
             }
             return null;
         }
 
         @Override
         public void onPostExecute(Void result) {
-            SnackbarBuilder.builder((Activity) mContext)
+            final Activity activity = activityWeakReference.get();
+            if (activity == null || activity.isFinishing()) {
+                return;
+            }
+
+            SnackbarBuilder.builder(activity)
                     .message(R.string.page_removed)
                     .duration(Snackbar.LENGTH_LONG)
                     .buildAndShow();
         }
 
         private void removeBookmark(ContentResolver cr) {
-            SavedReaderViewHelper rch = SavedReaderViewHelper.getSavedReaderViewHelper(mContext);
-            final boolean isReaderViewPage = rch.isURLCached(mUrl);
+            SavedReaderViewHelper rch = SavedReaderViewHelper.getSavedReaderViewHelper(context);
+            final boolean isReaderViewPage = rch.isURLCached(info.url);
 
             final String extra;
             if (isReaderViewPage) {
                 extra = "bookmark_reader";
             } else {
                 extra = "bookmark";
             }
 
             Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.CONTEXT_MENU, extra);
-            mDB.removeBookmarksWithURL(cr, mUrl);
+            db.removeBookmarkWithId(cr, info.bookmarkId);
 
             if (isReaderViewPage) {
-                ReadingListHelper.removeCachedReaderItem(mUrl, mContext);
+                ReadingListHelper.removeCachedReaderItem(info.url, context);
             }
         }
 
         private void removeHistory(ContentResolver cr) {
-            mDB.removeHistoryEntry(cr, mUrl);
+            db.removeHistoryEntry(cr, info.url);
         }
     }
 
     private static class RemovePartnerBookmarkTask extends UIAsyncTask.WithoutParams<Void> {
+        private final WeakReference<Activity> activityWeakReference;
         private Context context;
         private long bookmarkId;
 
-        public RemovePartnerBookmarkTask(Context context, long bookmarkId) {
+        private RemovePartnerBookmarkTask(Activity activity, long bookmarkId) {
             super(ThreadUtils.getBackgroundHandler());
 
-            this.context = context;
+            this.activityWeakReference = new WeakReference<>(activity);
+            this.context = activity.getApplicationContext();
             this.bookmarkId = bookmarkId;
         }
 
         @Override
         protected Void doInBackground() {
             context.getContentResolver().delete(
                     PartnerBookmarksProviderProxy.getUriForBookmark(context, bookmarkId),
                     null,
                     null
             );
 
             return null;
         }
 
         @Override
         protected void onPostExecute(Void aVoid) {
-            SnackbarBuilder.builder((Activity) context)
+            final Activity activity = activityWeakReference.get();
+            if (activity == null || activity.isFinishing()) {
+                return;
+            }
+
+            SnackbarBuilder.builder(activity)
                     .message(R.string.page_removed)
                     .duration(Snackbar.LENGTH_LONG)
                     .buildAndShow();
         }
     }
 }
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -70,27 +70,29 @@
 <!ENTITY bookmark_added "Bookmark added">
 <!-- Localization note (bookmark_already_added) : This string is
      used as a label in a toast. It is the verb "to bookmark", not
      the noun "a bookmark". -->
 <!ENTITY bookmark_already_added "Already bookmarked">
 <!ENTITY bookmark_removed "Bookmark removed">
 <!ENTITY bookmark_updated "Bookmark updated">
 <!ENTITY bookmark_options "Options">
+<!ENTITY bookmark_save "Save">
 <!ENTITY screenshot_added_to_bookmarks "Screenshot added to bookmarks">
 <!-- Localization note (screenshot_folder_label_in_bookmarks): We save links to screenshots
      the user takes. The folder we store these links in is located in the bookmarks list
      and is labeled by this String. -->
 <!ENTITY screenshot_folder_label_in_bookmarks "Screenshots">
 <!ENTITY readinglist_smartfolder_label_in_bookmarks "Reading List">
 
 <!-- Localization note (bookmark_folder_items): The variable is replaced by the number of items
      in the folder. -->
 <!ENTITY bookmark_folder_items "&formatD; items">
 <!ENTITY bookmark_folder_one_item "1 item">
+<!ENTITY bookmark_parent_folder "Parent Folder">
 
 <!ENTITY reader_saved_offline "Saved offline">
 <!-- Localization note (reader_switch_to_bookmarks) : This
      string is used as an action in a snackbar - it lets you
      "switch" to the bookmarks (saved items) panel. -->
 <!ENTITY reader_switch_to_bookmarks "Switch">
 
 <!ENTITY history_today_section "Today">
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -477,17 +477,19 @@ gbjar.sources += ['java/org/mozilla/geck
     'activitystream/Utils.java',
     'adjust/AdjustBrowserAppDelegate.java',
     'animation/AnimationUtils.java',
     'animation/HeightChangeAnimation.java',
     'animation/PropertyAnimator.java',
     'animation/Rotate3DAnimation.java',
     'animation/ViewHelper.java',
     'ANRReporter.java',
+    'bookmarks/BookmarkEditFragment.java',
     'bookmarks/BookmarkUtils.java',
+    'bookmarks/EditBookmarkTask.java',
     'BootReceiver.java',
     'BrowserApp.java',
     'BrowserLocaleManager.java',
     'cleanup/FileCleanupController.java',
     'cleanup/FileCleanupService.java',
     'CustomEditText.java',
     'customtabs/ActionBarPresenter.java',
     'customtabs/CustomTabsActivity.java',
new file mode 100644
index 0000000000000000000000000000000000000000..795377c664bde31182f64a0e25dfae29eb03138e
GIT binary patch
literal 20527
zc%1E=30O>R|Hn^dsVHO#p)sk)HqD&bXVkQ7@|4n^{mhv&8O>&9s+o}NyRsy$A|%RA
ziIg>zQWPo@B~&V;C&K>>B8tcRKJWj3{om`FbInrseSg27``q{M{+;`ruB)|fQyukm
zhwB0Wpy$N4cSpa+sNUMW(9bGh#}4#MN5b}#0YIODs<#FZ6E_q9bY1v%c5ZI-#B#B0
zo>+o$va`cTq+%XlfB=A^EZ!s7cWdhqR%I*F&)fEH)6powQUGg`s(rP;-|qff0BA*j
z1MABx*XVi=8ayD5?R9Bci7q>S&7;Jz8U8lw)=XdTHIx3rxFY%F;*iFMhIe<~ovn?l
zZ7)z3_6r+eV7B?m?)d=2eWeBSu5q|4`_i?nFwMd2hdnU;pX6Qe?|B&jnzWRPQ27z7
zc8$YgKogLZn2#~Yt@^k;)6L8Wz-$H{Vhw8hXhgUJSIs@I4g#(Y1&(+e=jsC606?Hz
zWQzq3=mGVwomf4AyD3{M41v45N2F*28#RFFv5sRjt<L~+y_UL9)Xcd6B&J$p&3oh=
z(g^cAMRe2*%jyXPZSsxiXFV4Pi?*GZ4`Aq;fZeEpH?%O$T8V_>?1w3HBr7x68bD57
z<c+95i%EkrB5w{4e%bW!(db7P1V?Ql!*Tt2I@Q>?s92MM>{>17g1!JSyshZ`2f9^6
z?UU-NC%jrg<Lk@2KLj+1v8>U}l^@P-(Ch*9pIX>hHm9bhR7;YpaU?K1AY}$n<_*lN
zY@0<Y*GZhNdo!eMy^`K;Jt|?B+f|E^BXs-pX)xvN$|5{o&-zHJ5<grY`Ub82P#)3*
zEQqm=PRmJJ{(b=@3OeMzsWz{NEhuWOHTjUca>@Hm<BA@UkItMpcwMva+6*_WORn;?
z`)2#m*CHPoJX=C+Hs1bTXKSz?kh;n>XX>fmC-Ve3p)WKv*X#Qh2LaZhuW%cN_t^~e
z_a_sTtpKn!yDi*$f(CFSYR_W;IQw+y%>CO8ov&yEfPLc9@keZW_dP!-HAg@0{K&I8
zhMG_>d+WYqaxD7W_NopWp}TA7cAH_IIn28~P9D)3o;V<;s{e`eAYHHap~1|hbrQo~
zbB88e7<zV<P7j-_+5@4{%ryfzo9<!@v<;&-^}s@G@|LhYm}hGSO~YQkD&3=*Vmp8H
zEVju@oOBRl<4o(f5#?7g#C7fK&N_nImL8j?bz3vg`t*L|;p?s>%8KOVezU^1?+z#$
zeqxkm*s0ygMSngGlmm;`ZGyv!Kz-hZd(g}cs~zU9^&M`OI|Q!rrFe%^;E}$xput8t
z2~eZ<7|r!z1M9VCujqe4uq-0N(bw76Zog^0$q9o@Z6BTGE5HkXV7KmfJz*MR&~{0|
zVIGa}ot$d+V)Urd38u6E&>uKwD|?LowJp_Cbw`^<5S{us1+dH6xIG?wSjGMMan5FS
z5yEQ^s<n@WrDCEsr$_XQaNY9m+QsVhLCni*9y#mm9^<}eo52(cVpY>)?@pp^bmD<&
zH&POAYadv{+&SGzm>A@S_gI}WkQ^9edLwp-UEUsCQpwF2zCXWp9{!m;Q|JEjQ4Ucz
zqlyEb_I_6TZ29ZpzIv<lmgllLV}}I{TR1FvX!<aGwDZ26x1yr2dJ^ssjiUW`^xGl!
z@*V$X{G!2|uNy=%BfTR-BWn+Mdda-3N|XoscpmiXcW}1%;(e37j=Mch(YZb3U`fhI
zuQMssLj~@^?%{5Q)8-_doAZ9zTUhVds+E%ySFw{k;q)uRHjW;0g@`o!+LlVawdy7Y
z>~P`CIM!D09MtojQ4oHivxlAR056rdnHT?L!3_=lD@PwTG491pDHvU#SFjMLg|osP
zEuEjnOAAe-m4%d@E8{ds8-_I0l{E%R;;rK+#<wtq%+^4iOlsz|%$1qtfrZamx8OSE
zGo6;}E&9*y7df^FUnjK~K5r~-EHb7M$lBD1VFHj2H|0mbPXl)FGm4s4RP7&-H!pMZ
zrFlt3PDy`cn7vr-|CpjMWbsnPBRHw#2S`PL1;pVK%lBN`bGiE5xEtF7VtAQFCvM5}
z^RG^<NWGhSt$q`uBrYHUI(P4EdHcqddmZ8~Pgzf0pS!(m`=Y#_+g-M|rta`&c`LnJ
zyzitsmCmf2SSKv?5x5!o#P*G4#^#;cpB9^TDJWK0Iq1=#@NvcCT0BH$V10YNZhcIB
zuLi~&=#A+c;hVyd`6CQQmQIpSS|pVR<_8HRXY2M<kuRoPO1b#V$KS|*mn{Aj#c<!B
z5qSntK~aVt!sK<yqMadmku}3#WEP*i)shr@CblB&p78ele9Oy=5599Tk2ShB>!M|&
z&FiLC-$x7bAIB%hCSQ0JQH!4)G}~|XyR1>J$K%WrN(sWN<(FnW%*tChm&vk3-WEL5
zVqIZr?hM(v`rN*A*h<TDCZVOF(V@ND6W&dFoA*Bauk0%2%OTBEd(PE3uTi6swRHH>
z<5~e)#yz5Y?CnkIxv}S&UiiMO!M1&M^!$vaTg5qsFOAw@QicD-n4-bgwc`g*H+oCR
zH*+3UI$+bPLhZ`_q2hgQ&d>5sX|H+cC|HwW%pVqQ*tU4kxbegT=Fg{{@Q^Q%w@xfg
zt@eB8pS7tuJY~b_N#*lTNrU`T=L_5gaD8k>SjK^jrHa*mIW$KZ$On$mX;@o*>Fy=V
zwU1?*wSpK@Ty>1+b~${gregPL!za%Q@)CkA{3w1Ai|8wN7(^Yjs#%V)b;A#Jtw|QS
zj2!rKMeIsfhjY6(?w)ouJ!6W+mM8Jvk#FetCoQ&nJ8zZi<iIN=9ar}W%FV;JoHlvo
z;%#60Ahqz`f_pf|wzzF^N$z_5?I$MOA*7E@cjZl(uvM>G`9yf#XzS|ht<@%NvRCDU
z1uLD4l1{iEa?>B;jNd;&F=OZqMFu;IJ^ePRlK0_xn@OK#Ll$L49C6Dya{O+F!ms%u
zHrwIM5R>&DhQa3GEcZ;Vg5OlUIB#CwvF-2O9{IX^y_~PCc)7`adGX%8b`zHQkRwyJ
zUCR?4dOiI#JQ#}PdNt0EDw*NEWV~VIoK#8b{zHEqnVj+Dk<ZjQC5u{=!m_dlQ_`<D
z)UoUK)QM7C{jv{F$(nCda&uLaUYb$!#u}qJ!YL2ryF%u-Y`Ssc#`POo6o}F=BmY!t
z`O96@q&Fe)OJ?PHzfU`ue(s)GS!wqD;DxXBmyaAg=|SVR`m-k|D0~9492U2TieD_d
zA3mI8l$re`^nQk8u<PZ7@P&I9_I~5gST|oT7E~P0&OW9ibf<0{#b!!sV#CB`u?;c7
zCneLz>tC>`IsZrHzC{ZkyFJ}iaIxazjmqdH=`GrIY~_SvW_DmIE=ibZxht#j>4v2E
zk+VlSo=g82+6X>|19K_iJ_(8OV=bH8s+VnjoXky5h@Wa@c{{p7+3MUL9WZO!tSo=K
z!UQWr`3vXtIjz@TJ*W+z)pS<!8r#Y#H%<RD&GL|?W2oeP75nfEaUIf{*WhDuNdDTT
zZc<U>ttD^g=ak6q{B_5C)=p(s<CS`&bdlNp5gDeLEq?{QX)p}dec@a&D<oWTxcyw&
z`{%vi_ItkM#D}9uj_~1Mb93evUi9nNsIRj+;ls9%w^mmbJLWr<m`pa=7#?qIY@|0l
z>m|2fYgO{G<bj3PL%lyL>tqLOE8jnQ_h+GR``xBKZR-oO4=Gb#NgEHQ9A@U4<QktX
zP+B(cYfmdEU)-3{m|2;dWgI=W-TH&utD?7XZt_jnR#xqz5AFEY6|dfz7VjjXmwXK0
z(^u~6;>?7_LUV{K<{;(@p#)GJ7FG%g1P35;3<vS&i!8B?XR@&vKGzcKLvbOvNbHb#
ze0GQw@d%mf35Nv03@+A+rE8&Jq6R`l4q+5Rfk?(wSYki<GSQz^#ds{{QxkcBCDuk&
zf$?>5!`O+X2!>)##lZwB4MSs?gA^K#Oq+-y5<m)`K*Ey<I3kHjCNRlF%$Ebp(nUWl
zq+A};-G1^H9QvCjcAi`=VdC+@!NKOiBy+LU9}hAZ3_O8|ClYaJ3!F?Tl0yodNM`cc
zq{GJ^k-<{FM9vqBFsk1njyO<miN)$-IvromODOpY63M=d9Xg751th_P<^+6qA};(@
zBMFoWK6Q-?;}HQOL_~5Kssp>#p)ePhU*m+rZj~~*Ly-K}N|~opg5cc|nK)1iBMw1m
zPfWVEaz}XoiWS<tTO&!wjZF7uU#ovQjB-BjCmm7M{8WZ0_&<@UYCg+Y-*gt}s59-P
z2qYIvJ;h=H>vOn%yA4C{!c^gj8S4VUe37bfoCW?zQFnfbJ^E&$S{jZ(LSLRB$fPis
zMA`%bok<{cGWp!{2M-r?8F3*w^qmVx_5_Jc0*y%qJG*?>_D3Hvm(NrFmdm%6ojkZO
zlP8u6AvudLg!~b_MC5OQ|5o@t&BcZ3B$CM?5sWz5v(N%_KA+2^b9o4zLgM0JkW0jo
z$s8_@0YM}jkwT%7s9YYAPKG~q;d^-(YkM&qs9Hj3-REq>6~n0g*HB_IKpqj~Q0X`_
zh04HzAPvGn2*|}DR0<8IQ5hUAkK6(3WYz`bDn&QsB1q7wjp|&uD2zaXKn93*9)^ho
z92o*(97LxgI35)t5;+6}B++>th;D4UpxAsF8bnIgcai)lO?e;-zAx>l5b!^xEeRx*
zA*!WliS1a6*e(K<=2LcHLa-{~vEWYu$F;zJSNd}qzw31V-Vk(=|C|v#4-x%pp>%5X
za}$}ECl7|Ch>brw(!aNgyAyU9as1~Y|6iC285H!-CW1mBfkXxw#Bq2WGLB5+Avih(
zqTx6kGLgq+fG~l8{DX<0dV*9Y`XW*OKTO0o06Y&8`6FBwzC+ki@ZU6dL^LrGV3>mg
zX>=}{6G0w2i*yjjB@nnE7bbJaJn-M2yKXYPdhWiaK(QzYk^U=55amGVwic1fSUjm%
zh=C*$0Uw4`DtwTL`(=;u^*V%+i!q%pmR&sl56_j0NcsOtq;~4%*I~l^m!0!JUYozS
zUs12kA2u!cuDh0wolw_ZPREw+b9_!#McD7nxW%VClz);^e~@?Q&i%WcCXYlS@jx1k
zBXNkdFFQ>F6~-YnE<&IZAUcCe?_hDe+x~rHiT~+l^QSgl=b_VZcdq?*UUp)ESc={V
zAS{vvzPsXXRZezxZlC{B#ge%@+G3m>?5H4vO2rY)!Ok|F+I8#G(RUkkW>f+5L)!S1
z8f+o|pXH37%oUkLqY>#0bQCFcl4`CPI0gl#;9vp|rlSiR=75~POGiJkrgF&;kqAOK
z)Qb2y4Cr(&0f!)Dh(<<u3?jn#H?4m!!v4m(Bap}hw3jfAfFm&&5RObhJIf)1BpjJe
zqmdaz4i};kf7^^o<RKJ<OvQ0Q5Z!h%I6NGKNFm}F2o>Ql7-R;84*s?ogG(WTT#$e!
zS2Efk9%@EMjc`1K3o{4|m<W=f-!^lYOrhJ@5-3!HodXCukm)1>l}@EP5U4f|BnHvu
zSBdNUxau6Wzlur=)w2lIjUHP6>DtIbPrXaokL`U`xlIuGsg6b@pa&jQeSVR4uKrQ#
z_iL%&FQnh9yhMCCi}*$ILvbgS>Ve7UrSBnuU_^ClKNkJi+KsP31itosbi8D$K9Hzi
zG(T2!EA;!B(65W|^Q3ipl=t~LA^yvQLY6L?@;V+;c6ZJD&mU@t8lr}%A!>*kqK2p;
zYKR)5hNvNGh#I1Xs3B^I8lr}%A!>*kqK2p;YKR)5hNvNGh#I1Xs3B^I8lr}%A!>*k
zqK2p;YKR)5hNvNGh#K-AhUk9#w;zazYl#iUTCtAq$z22h00w5ey8u9lDFB2m2Y|MZ
z==U=K2qXZ&^SJ=P+yMZF;w^J7PX+)Df|I?Cr=qO>&d~|I7L3SkzVjFa*H~#RzYTA-
zvc5L?LJpTad$ZHDW31&|g2Jp<%MM?G8RFyXLNyy#AblPZ(zl{{;i}5u%)&sQ#uu$0
z7QZ;OWnaqF81s?T0b?~C60;Lt?0gV0#>v<mT-RfD<*XTp?|iJ$xiNP{7Cp!Fsp*+3
z0YNh_>#VZXybn{Ndf&8nWv3Y3&8q=N>@tu>rB#iOGCVrU;Hh38MSgraZoIN+RLF&y
z(OaUXZJ-IqSiMWDMks+=?@rIm8`H0sr|`IQ@aUyMg!nweyZSxJ;Tva$<`_J%14@WX
z&gnMyJg(3`n)mj&&t&m-kJN`$`+i;vHdGA>kJ%NI>Gen^SQ&D9>WR&YVaB_{dgX1)
yX!>K~RB-J8!H~#|!12wMEc9Ho2Ps&+fk6uD3)7N`3e|sJadMbyf5>+3vi|{~O~3*G
new file mode 100644
index 0000000000000000000000000000000000000000..6269637bf43d568be6e660646a4e1d64883e67cd
GIT binary patch
literal 20519
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
new file mode 100644
index 0000000000000000000000000000000000000000..e1c61d2bfb79594b7897c747179caebe6071c0ec
GIT binary patch
literal 20702
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
S*NT4><6yVY_L%j`jsFAfiw;Hr
new file mode 100644
index 0000000000000000000000000000000000000000..0e01eabde5b6db975445a197bbc57babeb4da940
GIT binary patch
literal 20769
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
E0Ts(L)&Kwi
new file mode 100644
index 0000000000000000000000000000000000000000..7b1a41b03a60477d9a9bbbdf80ab2ced902910ed
GIT binary patch
literal 20704
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
W@1S|7K=H3)Y^`QaJ#68*@_zt$D;x&^
new file mode 100644
index 0000000000000000000000000000000000000000..3834ad18e25ff5de90c2f3353cb4777b5d5a974e
GIT binary patch
literal 20761
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
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/bookmark_edit_with_full_page.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/white"
+    android:orientation="vertical">
+
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="56dp"
+        android:background="@color/text_and_tabs_tray_grey"
+        android:minHeight="?actionBarSize"
+        app:navigationIcon="@drawable/abc_ic_clear_mtrl_alpha"
+        app:subtitleTextColor="@android:color/white"
+        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+        app:title="@string/bookmark_edit_title"
+        app:titleTextColor="@android:color/white" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingEnd="16dp"
+        android:paddingLeft="16dp"
+        android:paddingRight="16dp"
+        android:paddingStart="16dp">
+
+        <android.support.design.widget.TextInputLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="8dp"
+            android:layout_marginTop="16dp">
+
+            <EditText
+                android:id="@+id/edit_bookmark_name"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:ellipsize="end"
+                android:gravity="start"
+                android:hint="@string/bookmark_edit_name"
+                android:maxLines="1"
+                android:singleLine="true"
+                android:textColor="@color/text_and_tabs_tray_grey"
+                android:textSize="18sp"
+                android:focusable="true"
+                tools:text="Firefox: About your browser" />
+        </android.support.design.widget.TextInputLayout>
+
+        <android.support.design.widget.TextInputLayout
+            android:id="@+id/edit_bookmark_location_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="8dp"
+            android:layout_marginTop="16dp">
+
+            <EditText
+                android:id="@+id/edit_bookmark_location"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:ellipsize="end"
+                android:gravity="start"
+                android:hint="@string/bookmark_edit_location"
+                android:inputType="textNoSuggestions"
+                android:maxLines="1"
+                android:singleLine="true"
+                android:textColor="@color/text_and_tabs_tray_grey"
+                android:textSize="18sp" />
+        </android.support.design.widget.TextInputLayout>
+
+        <android.support.design.widget.TextInputLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="8dp"
+            android:layout_marginTop="16dp">
+
+            <EditText
+                android:id="@+id/edit_parent_folder"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:cursorVisible="false"
+                android:drawableEnd="@drawable/arrow"
+                android:drawableRight="@drawable/arrow"
+                android:drawablePadding="8dp"
+                android:ellipsize="end"
+                android:focusable="false"
+                android:focusableInTouchMode="false"
+                android:gravity="start"
+                android:hint="@string/bookmark_parent_folder"
+                android:inputType="none"
+                android:maxLines="1"
+                android:singleLine="true"
+                android:textColor="@color/text_and_tabs_tray_grey"
+                android:textSize="18sp" />
+        </android.support.design.widget.TextInputLayout>
+    </LinearLayout>
+</LinearLayout>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/menu/bookmark_edit_menu.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item android:id="@+id/done"
+        android:title="@string/bookmark_save"
+        android:enabled="false"
+        app:showAsAction="always"/>
+</menu>
\ No newline at end of file
--- a/mobile/android/base/resources/values/themes.xml
+++ b/mobile/android/base/resources/values/themes.xml
@@ -132,9 +132,23 @@
     </style>
 
     <style name="GeckoCustomTabs.Light" parent="Theme.AppCompat.Light.NoActionBar">
         <item name="menuItemActionBarStyle">@style/Widget.MenuItemActionBar</item>
         <item name="menuItemDefaultStyle">@style/Widget.MenuItemCustomTabs</item>
         <item name="windowActionModeOverlay">true</item>
     </style>
 
+    <!-- Bookmark full-page dialog theme -->
+    <style name="Bookmark" parent="Theme.AppCompat.Light.DialogWhenLarge"/>
+    <style name="Bookmark.Gecko" parent="Gecko">
+        <item name="toolbarStyle">@style/BookmarkToolbarStyle</item>
+        <item name="colorAccent">@color/fennec_ui_orange</item>
+        <item name="colorControlNormal">@color/disabled_grey</item>
+
+        <item name="android:textColorHint">@color/tabs_tray_icon_grey</item>
+    </style>
+    <style name="BookmarkToolbarStyle" parent="@style/Widget.AppCompat.Toolbar">
+        <item name="android:paddingRight">5dp</item>
+        <item name="android:paddingEnd">5dp</item>
+    </style>
+
 </resources>
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -92,21 +92,23 @@
   <string name="quit">&quit;</string>
   <string name="bookmark">&bookmark;</string>
   <string name="bookmark_remove">&bookmark_remove;</string>
   <string name="bookmark_added">&bookmark_added;</string>
   <string name="bookmark_already_added">&bookmark_already_added;</string>
   <string name="bookmark_removed">&bookmark_removed;</string>
   <string name="bookmark_updated">&bookmark_updated;</string>
   <string name="bookmark_options">&bookmark_options;</string>
+  <string name="bookmark_save">&bookmark_save;</string>
   <string name="screenshot_added_to_bookmarks">&screenshot_added_to_bookmarks;</string>
   <string name="screenshot_folder_label_in_bookmarks">&screenshot_folder_label_in_bookmarks;</string>
   <string name="readinglist_smartfolder_label_in_bookmarks">&readinglist_smartfolder_label_in_bookmarks;</string>
   <string name="bookmark_folder_items">&bookmark_folder_items;</string>
   <string name="bookmark_folder_one_item">&bookmark_folder_one_item;</string>
+  <string name="bookmark_parent_folder">&bookmark_parent_folder;</string>
 
   <string name="reader_saved_offline">&reader_saved_offline;</string>
   <string name="reader_switch_to_bookmarks">&reader_switch_to_bookmarks;</string>
 
   <string name="history_today_section">&history_today_section;</string>
   <string name="history_yesterday_section">&history_yesterday_section;</string>
   <string name="history_week_section">&history_week_section3;</string>
   <string name="history_older_section">&history_older_section3;</string>