Bug 1232439 - Part 2: convert edit bookmark dialog into fullscreen fragment r=mcomella draft
authorAndrzej Hunt <ahunt@mozilla.com>
Tue, 09 Feb 2016 16:01:48 -0800
changeset 329917 83100c041fddd898f2d0f5878c98c5c7e25aa430
parent 329916 0ad4bf5ae2bb14afbbf4161454b9e7b4111c6a9a
child 514076 6512ed41626489ea27b38b19ab51def27276fe93
push id10650
push userahunt@mozilla.com
push dateWed, 10 Feb 2016 01:01:05 +0000
reviewersmcomella
bugs1232439
milestone47.0a1
Bug 1232439 - Part 2: convert edit bookmark dialog into fullscreen fragment r=mcomella MozReview-Commit-ID: 4rRHyevmSU6
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/EditBookmarkDialog.java
mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/resources/layout/bookmark_edit.xml
mobile/android/base/resources/menu/edit_bookmarks_menu.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
@@ -1164,17 +1164,17 @@ public class BrowserApp extends GeckoApp
                     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());
+                    new EditBookmarkDialog().show(BrowserApp.this, getSupportFragmentManager(), 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/EditBookmarkDialog.java
+++ b/mobile/android/base/java/org/mozilla/gecko/EditBookmarkDialog.java
@@ -5,40 +5,70 @@
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.UIAsyncTask;
 
-import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
 import android.database.Cursor;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.widget.Toolbar;
 import android.support.design.widget.Snackbar;
+import android.support.design.widget.TextInputLayout;
 import android.text.Editable;
+import android.view.MenuItem;
 import android.text.TextWatcher;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.EditText;
 
 /**
  * A dialog that allows editing a bookmarks url, title, or keywords
  * <p>
  * Invoked by calling one of the {@link org.mozilla.gecko.EditBookmarkDialog#show(String)}
  * methods.
  */
-public class EditBookmarkDialog {
-    private final Context mContext;
+public class EditBookmarkDialog extends DialogFragment {
+    private Bookmark mBookmark;
+
+    private static final String SAVED_STATE_ID = "id";
+    private static final String SAVED_STATE_TITLE = "title";
+    private static final String SAVED_STATE_URL= "url";
+    private static final String SAVED_STATE_KEYWORD = "keyword";
+
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
 
-    public EditBookmarkDialog(Context context) {
-        mContext = context;
+        outState.putInt(SAVED_STATE_ID, mBookmark.id);
+        outState.putString(SAVED_STATE_TITLE, mBookmark.title);
+        outState.putString(SAVED_STATE_URL, mBookmark.url);
+        outState.putString(SAVED_STATE_KEYWORD, mBookmark.keyword);
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState != null) {
+            mBookmark = new Bookmark(savedInstanceState.getInt(SAVED_STATE_ID),
+                    savedInstanceState.getString(SAVED_STATE_TITLE),
+                    savedInstanceState.getString(SAVED_STATE_URL),
+                    savedInstanceState.getString(SAVED_STATE_KEYWORD));
+        }
     }
 
     /**
      * A private struct to make it easier to pass bookmark data across threads
      */
     private class Bookmark {
         final int id;
         final String title;
@@ -56,98 +86,143 @@ public class EditBookmarkDialog {
     /**
      * This text watcher to enable or disable the OK button if the dialog contains
      * valid information. This class is overridden to do data checking on different fields.
      * By itself, it always enables the button.
      *
      * Callers can also assign a paired partner to the TextWatcher, and callers will check
      * that both are enabled before enabling the ok button.
      */
-    private class EditBookmarkTextWatcher implements TextWatcher {
+    private class LocationTextWatcher implements TextWatcher {
         // A stored reference to the dialog containing the text field being watched
-        protected AlertDialog mDialog;
-
-        // A stored text watcher to do the real verification of a field
-        protected EditBookmarkTextWatcher mPairedTextWatcher;
-
-        // Whether or not the ok button should be enabled.
-        protected boolean mEnabled = true;
+        protected EditText mEditText;
+        protected TextInputLayout mTextInputLayout;
 
-        public EditBookmarkTextWatcher(AlertDialog aDialog) {
-            mDialog = aDialog;
-        }
-
-        public void setPairedTextWatcher(EditBookmarkTextWatcher aTextWatcher) {
-            mPairedTextWatcher = aTextWatcher;
-        }
-
-        public boolean isEnabled() {
-            return mEnabled;
+        public LocationTextWatcher(TextInputLayout textInputLayout) {
+            mTextInputLayout = textInputLayout;
         }
 
         // Textwatcher interface
         @Override
         public void onTextChanged(CharSequence s, int start, int before, int count) {
-            // Disable if the we're disabled or the paired partner is disabled
-            boolean enabled = mEnabled && (mPairedTextWatcher == null || mPairedTextWatcher.isEnabled());
-            mDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled);
+            boolean enabled = (s.toString().trim().length() > 0);
+
+            if (enabled) {
+                // By doing setErrorEnabled to false, we completely remove the error line from the layout. Instead
+                // if we setError(null) or setError(EMPTY_STRING) we still leave an empty space in the layout
+                // (which is the equivalent of having errorEnabled=true in the xml layout). Leaving the empty space
+                // results in disproportionately large spacing, hence it seems nicer to enable/disable the error (which
+                // results in the rest of the layout content being shifted down when the error appears, and back up
+                // when the error disappears).
+                mTextInputLayout.setErrorEnabled(false);
+            } else {
+                mTextInputLayout.setError(getString(R.string.bookmark_edit_location_empty_error));
+            }
         }
 
         @Override
         public void afterTextChanged(Editable s) {}
         @Override
         public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
     }
 
-    /**
-     * A version of the EditBookmarkTextWatcher for the url field of the dialog.
-     * Only checks if the field is empty or not.
-     */
-    private class LocationTextWatcher extends EditBookmarkTextWatcher {
-        public LocationTextWatcher(AlertDialog aDialog) {
-            super(aDialog);
-        }
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
+        final View editView = inflater.inflate(R.layout.bookmark_edit, container, false);
+
+        final EditText nameText = ((EditText) editView.findViewById(R.id.edit_bookmark_name));
+        final EditText locationText = ((EditText) editView.findViewById(R.id.edit_bookmark_location));
+        final EditText keywordText = ((EditText) editView.findViewById(R.id.edit_bookmark_keyword));
+
+        final Toolbar toolbar = (Toolbar) editView.findViewById(R.id.my_toolbar);
+        toolbar.setTitle(R.string.bookmark_edit_title);
+        toolbar.setNavigationIcon(R.drawable.tabs_panel_nav_back);
+        toolbar.inflateMenu(R.menu.edit_bookmarks_menu);
+
+        // Insert text BEFORE the cursor - when the user opens the dialog we want the cursor to be at the end of the name
+        // field. We've already requested focus in the layout file using <requestFocus/>, however using setText
+        // results in the cursor staying at the start of the text, whereas insert(0,...) results in the text being
+        // inserted in front of the cursor.
+        nameText.getText().insert(0, mBookmark.title);
+        locationText.setText(mBookmark.url);
+        keywordText.setText(mBookmark.keyword);
+
+        final BrowserDB db = GeckoProfile.get(getContext()).getDB();
+        toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
+            @Override
+            public boolean onMenuItemClick(MenuItem item) {
+                int id = item.getItemId();
+                if (id == R.id.bin) {
+                    (new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
+                        @Override
+                        public Void doInBackground() {
+                            db.removeBookmarkWithID(getContext().getContentResolver(), mBookmark.id);
+                            return null;
+                        }
 
-        // Disables the ok button if the location field is empty.
-        @Override
-        public void onTextChanged(CharSequence s, int start, int before, int count) {
-            mEnabled = (s.toString().trim().length() > 0);
-            super.onTextChanged(s, start, before, count);
-        }
-    }
+                        @Override
+                        public void onPostExecute(Void result) {
+                            Snackbar.make(container, R.string.bookmark_removed, Snackbar.LENGTH_SHORT).show();
+                            getFragmentManager().popBackStack();
+                        }
+                    }).execute();
+                }
+                return false;
+            }
+        });
+
+        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                if (locationText.getText().toString().trim().isEmpty()) {
+                    // locationText is already showing an error message when it is empty - we give it focus
+                    // to make it obvious to the user that they can't save a bookmark without a URL.
+                    locationText.requestFocus();
+                    return;
+                }
 
-    /**
-     * A version of the EditBookmarkTextWatcher for the keyword field of the dialog.
-     * Checks if the field has any (non leading or trailing) spaces.
-     */
-    private class KeywordTextWatcher extends EditBookmarkTextWatcher {
-        public KeywordTextWatcher(AlertDialog aDialog) {
-            super(aDialog);
-        }
+                (new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
+                    @Override
+                    public Void doInBackground() {
+                        String newUrl = locationText.getText().toString().trim();
+                        String newKeyword = keywordText.getText().toString().trim();
+
+                        db.updateBookmark(getContext().getContentResolver(), mBookmark.id, newUrl, nameText.getText().toString().trim(), newKeyword);
+                        return null;
+                    }
 
-        @Override
-        public void onTextChanged(CharSequence s, int start, int before, int count) {
-            // Disable if the keyword contains spaces
-            mEnabled = (s.toString().trim().indexOf(' ') == -1);
-            super.onTextChanged(s, start, before, count);
-       }
+                    @Override
+                    public void onPostExecute(Void result) {
+                        Snackbar.make(container, R.string.bookmark_updated, Snackbar.LENGTH_SHORT).show();
+                        getFragmentManager().popBackStack();
+                    }
+                }).execute();
+            }
+        });
+
+        LocationTextWatcher locationTextWatcher = new LocationTextWatcher((TextInputLayout) locationText.getParent());
+        locationText.addTextChangedListener(locationTextWatcher);
+
+        return editView;
     }
 
     /**
      * Show the Edit bookmark dialog for a particular url. If the url is bookmarked multiple times
      * this will just edit the first instance it finds.
      *
      * @param url The url of the bookmark to edit. The dialog will look up other information like the id,
      *            current title, or keywords associated with this url. If the url isn't bookmarked, the
      *            dialog will fail silently. If the url is bookmarked multiple times, this will only show
      *            information about the first it finds.
      */
-    public void show(final String url) {
-        final ContentResolver cr = mContext.getContentResolver();
-        final BrowserDB db = GeckoProfile.get(mContext).getDB();
+    public void show(Context context, final FragmentManager fm, final String url) {
+        final ContentResolver cr = context.getContentResolver();
+        final BrowserDB db = GeckoProfile.get(context).getDB();
+
         (new UIAsyncTask.WithoutParams<Bookmark>(ThreadUtils.getBackgroundHandler()) {
             @Override
             public Bookmark doInBackground() {
                 final Cursor cursor = db.getBookmarkForUrl(cr, url);
                 if (cursor == null) {
                     return null;
                 }
 
@@ -165,87 +240,32 @@ public class EditBookmarkDialog {
             }
 
             @Override
             public void onPostExecute(Bookmark bookmark) {
                 if (bookmark == null) {
                     return;
                 }
 
-                show(bookmark.id, bookmark.title, bookmark.url, bookmark.keyword);
+                show(fm, bookmark);
             }
         }).execute();
     }
 
     /**
      * Show the Edit bookmark dialog for a set of data. This will show the dialog whether
      * a bookmark with this url exists or not, but the results will NOT be saved if the id
      * is not a valid bookmark id.
      *
      * @param id The id of the bookmark to change. If there is no bookmark with this ID, the dialog
      *           will fail silently.
      * @param title The initial title to show in the dialog
      * @param url The initial url to show in the dialog
      * @param keyword The initial keyword to show in the dialog
      */
-    public void show(final int id, final String title, final String url, final String keyword) {
-        final Context context = mContext;
-
-        AlertDialog.Builder editPrompt = new AlertDialog.Builder(context);
-        final View editView = LayoutInflater.from(context).inflate(R.layout.bookmark_edit, null);
-        editPrompt.setTitle(R.string.bookmark_edit_title);
-        editPrompt.setView(editView);
-
-        final EditText nameText = ((EditText) editView.findViewById(R.id.edit_bookmark_name));
-        final EditText locationText = ((EditText) editView.findViewById(R.id.edit_bookmark_location));
-        final EditText keywordText = ((EditText) editView.findViewById(R.id.edit_bookmark_keyword));
-        nameText.setText(title);
-        locationText.setText(url);
-        keywordText.setText(keyword);
-
-        final BrowserDB db = GeckoProfile.get(mContext).getDB();
-        editPrompt.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int whichButton) {
-                (new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
-                    @Override
-                    public Void doInBackground() {
-                        String newUrl = locationText.getText().toString().trim();
-                        String newKeyword = keywordText.getText().toString().trim();
-
-                        db.updateBookmark(context.getContentResolver(), id, newUrl, nameText.getText().toString(), newKeyword);
-                        return null;
-                    }
+    private void show(final FragmentManager fm, final Bookmark bookmark) {
+        this.mBookmark = bookmark;
 
-                    @Override
-                    public void onPostExecute(Void result) {
-                        SnackbarHelper.showSnackbar((Activity) context,
-                                context.getString(R.string.bookmark_updated),
-                                Snackbar.LENGTH_SHORT);
-                    }
-                }).execute();
-            }
-        });
-
-        editPrompt.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int whichButton) {
-                  // do nothing
-              }
-        });
-
-        final AlertDialog dialog = editPrompt.create();
-
-        // Create our TextWatchers
-        LocationTextWatcher locationTextWatcher = new LocationTextWatcher(dialog);
-        KeywordTextWatcher keywordTextWatcher = new KeywordTextWatcher(dialog);
-
-        // Cross reference the TextWatchers
-        locationTextWatcher.setPairedTextWatcher(keywordTextWatcher);
-        keywordTextWatcher.setPairedTextWatcher(locationTextWatcher);
-
-        // Add the TextWatcher Listeners
-        locationText.addTextChangedListener(locationTextWatcher);
-        keywordText.addTextChangedListener(keywordTextWatcher);
-
-        dialog.show();
+        FragmentTransaction transaction = fm.beginTransaction();
+        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+        transaction.add(android.R.id.content, this).addToBackStack(null).commitAllowingStateLoss();
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
@@ -255,17 +255,17 @@ public abstract class HomeFragment exten
 
             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);
+            new EditBookmarkDialog().show(context, getFragmentManager(), 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;
 
             (new RemoveItemByUrlTask(context, info.url, info.itemType, position)).execute();
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -476,16 +476,17 @@ size. -->
      any page is removed frome about:home. This includes pages that are in history,
      bookmarks, or reading list. -->
 <!ENTITY page_removed "Page removed">
 
 <!ENTITY bookmark_edit_title "Edit Bookmark">
 <!ENTITY bookmark_edit_name "Name">
 <!ENTITY bookmark_edit_location "Location">
 <!ENTITY bookmark_edit_keyword "Keyword">
+<!ENTITY bookmark_edit_location_empty_error "Bookmark location must contain a URL">
 
 <!-- Localization note (site_settings_*) : These strings are used in the "Site Settings"
      dialog that appears after selecting the "Edit Site Settings" context menu item. -->
 <!ENTITY site_settings_title3       "Site Settings">
 <!ENTITY site_settings_cancel       "Cancel">
 <!ENTITY site_settings_clear        "Clear">
 
 <!-- Localization note (reading_list_added3) : Used in a toast, please keep as short
--- a/mobile/android/base/resources/layout/bookmark_edit.xml
+++ b/mobile/android/base/resources/layout/bookmark_edit.xml
@@ -3,49 +3,80 @@
    - 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"
               android:layout_width="match_parent"
               android:orientation="vertical"
-              android:layout_height="match_parent">
+              android:layout_height="match_parent"
+              android:background="@android:color/white"
+              android:theme="@style/GeckoAlertDialog">
 
-    <android.support.design.widget.TextInputLayout
+    <android.support.v7.widget.Toolbar
+            android:id="@+id/my_toolbar"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content">
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:background="?attr/colorPrimary"
+            android:minHeight="?attr/actionBarSize"
+            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+            android:title="@string/bookmark_edit_title"/>
 
-        <EditText
-                android:id="@+id/edit_bookmark_name"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:hint="@string/bookmark_edit_name"
-                />
-    </android.support.design.widget.TextInputLayout>
+    <ScrollView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/scrollView"
+            android:layout_gravity="center_horizontal">
 
-    <android.support.design.widget.TextInputLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
+        <LinearLayout android:layout_width="match_parent"
+                      android:layout_height="match_parent"
+                      android:padding="24dp"
+                      android:orientation="vertical">
+
+            <android.support.design.widget.TextInputLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    app:hintAnimationEnabled="true">
 
-        <EditText
-                android:id="@+id/edit_bookmark_location"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:hint="@string/bookmark_edit_location"
-                android:inputType="textNoSuggestions"/>
-    </android.support.design.widget.TextInputLayout>
+                <EditText
+                        android:id="@+id/edit_bookmark_name"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:singleLine="true"
+                        android:hint="@string/bookmark_edit_name">
+                    <requestFocus/>
+                </EditText>
+            </android.support.design.widget.TextInputLayout>
+
+            <android.support.design.widget.TextInputLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    app:hintAnimationEnabled="true">
 
-    <android.support.design.widget.TextInputLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
+                <EditText
+                        android:id="@+id/edit_bookmark_location"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:singleLine="true"
+                        android:hint="@string/bookmark_edit_location"
+                        android:inputType="textNoSuggestions"/>
+            </android.support.design.widget.TextInputLayout>
 
-        <EditText
-                android:id="@+id/edit_bookmark_keyword"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:hint="@string/bookmark_edit_keyword"/>
-    </android.support.design.widget.TextInputLayout>
+            <android.support.design.widget.TextInputLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    app:hintAnimationEnabled="true">
+
+                <EditText
+                        android:id="@+id/edit_bookmark_keyword"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:singleLine="true"
+                        android:hint="@string/bookmark_edit_keyword"/>
+            </android.support.design.widget.TextInputLayout>
+
+        </LinearLayout>
+
+    </ScrollView>
 
 </LinearLayout>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/menu/edit_bookmarks_menu.xml
@@ -0,0 +1,9 @@
+<?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/. -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item android:id="@+id/bin" android:icon="@android:drawable/ic_menu_delete" android:title="@string/bookmark_remove" app:showAsAction="always"/>
+</menu>
\ No newline at end of file
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -409,16 +409,17 @@
   <string name="pref_scroll_title_bar_summary">&pref_scroll_title_bar_summary2;</string>
 
   <string name="page_removed">&page_removed;</string>
 
   <string name="bookmark_edit_title">&bookmark_edit_title;</string>
   <string name="bookmark_edit_name">&bookmark_edit_name;</string>
   <string name="bookmark_edit_location">&bookmark_edit_location;</string>
   <string name="bookmark_edit_keyword">&bookmark_edit_keyword;</string>
+  <string name="bookmark_edit_location_empty_error">&bookmark_edit_location_empty_error;</string>
 
   <string name="pref_use_master_password">&pref_use_master_password;</string>
   <string name="masterpassword_create_title">&masterpassword_create_title;</string>
   <string name="masterpassword_remove_title">&masterpassword_remove_title;</string>
   <string name="masterpassword_password">&masterpassword_password;</string>
   <string name="masterpassword_confirm">&masterpassword_confirm;</string>
 
   <string name="button_ok">&button_ok;</string>