Bug 695199: Add/Remove bookmarks through menus [r=mfinkle]
authorSriram Ramasubramanian <sriram@mozilla.com>
Wed, 26 Oct 2011 14:33:16 -0700
changeset 83283 97403e114b581f4496318b472f8aaa7b9e42c04f
parent 83282 365da62068e07b59de97812e9e9eec1fef823a2e
child 83284 adad73e2ee6f20d0a88839af079dbf70060766df
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle
bugs695199
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 695199: Add/Remove bookmarks through menus [r=mfinkle]
embedding/android/GeckoApp.java
embedding/android/GeckoBookmarks.java
embedding/android/Makefile.in
embedding/android/Tab.java
embedding/android/Tabs.java
embedding/android/locales/en-US/android_strings.dtd
embedding/android/resources/drawable/bookmark_add.png
embedding/android/resources/drawable/bookmark_remove.png
embedding/android/resources/layout/bookmark_list_row.xml
embedding/android/resources/layout/bookmarks.xml
embedding/android/resources/layout/gecko_menu.xml
embedding/android/strings.xml.in
--- a/embedding/android/GeckoApp.java
+++ b/embedding/android/GeckoApp.java
@@ -433,40 +433,64 @@ abstract public class GeckoApp
                                 }
                             }
                         });
                     }
                 }
                 mi.setOnMenuItemClickListener(item);
             }
         }
+
+        Tab tab = Tabs.getInstance().getSelectedTab();
+        MenuItem bookmark = aMenu.findItem(R.id.bookmark);
+
+        if (tab == null) {
+            bookmark.setVisible(false);
+            return true;
+        }
+        
+        bookmark.setVisible(true);
+        bookmark.setCheckable(true);
+        
+        if (tab.isBookmark()) {
+            bookmark.setChecked(true);
+            bookmark.setIcon(R.drawable.bookmark_remove);
+            bookmark.setTitle(R.string.bookmark_remove);
+        } else {
+            bookmark.setChecked(false);
+            bookmark.setIcon(R.drawable.bookmark_add);
+            bookmark.setTitle(R.string.bookmark_add);
+        }
+
         return true;
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         Tab tab = null;
         Tab.HistoryEntry he = null;
+        Intent intent = null;
         switch (item.getItemId()) {
            case R.id.quit:
                quit();
                return true;
-           case R.id.bookmarks:
-               Intent intent = new Intent(this, GeckoBookmarks.class);
+           case R.id.bookmark:
                tab = Tabs.getInstance().getSelectedTab();
-               if (tab == null) {
-                   startActivity(intent);
-                   return true;
-               }
-
-               he = tab.getLastHistoryEntry();
-               if (he != null) {
-                   intent.setData(android.net.Uri.parse(he.mUri));
-                   intent.putExtra("title", he.mTitle);
-                   startActivity(intent);
+               if (tab != null) {
+                   if (item.isChecked()) {
+                       tab.removeBookmark();
+                       Toast.makeText(this, R.string.bookmark_removed, Toast.LENGTH_SHORT).show();
+                       item.setIcon(R.drawable.bookmark_add);
+                       item.setTitle(R.string.bookmark_add);
+                   } else {
+                       tab.addBookmark();
+                       Toast.makeText(this, R.string.bookmark_added, Toast.LENGTH_SHORT).show();
+                       item.setIcon(R.drawable.bookmark_remove);
+                       item.setTitle(R.string.bookmark_remove);
+                   }
                }
                return true;
            case R.id.share:
                tab = Tabs.getInstance().getSelectedTab();
                if (tab == null)
                    return true;
 
                he = tab.getLastHistoryEntry();
@@ -1069,22 +1093,25 @@ abstract public class GeckoApp
 
         // setup gecko layout
         mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
         mMainLayout = (LinearLayout) findViewById(R.id.main_layout);
         mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browser_toolbar);
 
         mDoorHanger = new DoorHanger(this);
 
-        Tab tab = Tabs.getInstance().getSelectedTab();
+        Tabs tabs = Tabs.getInstance();
+        Tab tab = tabs.getSelectedTab();
         if (tab != null) {
             mBrowserToolbar.setTitle(tab.getTitle());
             mBrowserToolbar.setFavicon(tab.getFavicon());
             mBrowserToolbar.updateTabs(Tabs.getInstance().getCount()); 
-        } 
+        }
+
+        tabs.setContentResolver(getContentResolver()); 
 
         if (surfaceView == null) {
             surfaceView = new GeckoSurfaceView(this);
             mGeckoLayout.addView(surfaceView);
         } else if (mGeckoLayout.getChildCount() == 0) {
            //surfaceView still holds to the old one during rotation. re-add it to new activity
            ((ViewGroup) surfaceView.getParent()).removeAllViews();
            mGeckoLayout.addView(surfaceView);
deleted file mode 100644
--- a/embedding/android/GeckoBookmarks.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Android code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2009-2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Brad Lassey <blassey@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-package org.mozilla.gecko;
-
-import android.app.ListActivity;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.Browser;
-import android.util.Log;
-import android.view.View;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.SimpleCursorAdapter;
-
-public class GeckoBookmarks extends ListActivity {
-    private static final String LOG_NAME = "GeckoBookmarks";
-    private static final String TITLE_KEY = "title";
-    private static final String kBookmarksWhereClause = Browser.BookmarkColumns.BOOKMARK + " = 1";
-
-    private Cursor mCursor;
-    private Uri mUri;
-    private String mTitle;
-
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.bookmarks);
-        mCursor = managedQuery(Browser.BOOKMARKS_URI,
-                               null, kBookmarksWhereClause, null, null);
-        startManagingCursor(mCursor);
-
-        ListAdapter adapter =
-          new SimpleCursorAdapter(this, R.layout.bookmark_list_row, mCursor,
-        		      new String[] {Browser.BookmarkColumns.TITLE,
-        				    Browser.BookmarkColumns.URL},
-        		      new int[] {R.id.bookmark_title, R.id.bookmark_url});
-        setListAdapter(adapter);
-    }
-
-    @Override
-    protected void onListItemClick(ListView l, View v, int position, long id) {
-        mCursor.moveToPosition(position);
-        String spec = mCursor.getString(mCursor.getColumnIndex(Browser.BookmarkColumns.URL));
-        Log.i(LOG_NAME, "clicked: " + spec);
-        Intent intent = new Intent(this, GeckoApp.mAppContext.getClass());
-        intent.setAction(Intent.ACTION_VIEW);
-        intent.setData(Uri.parse(spec));
-        startActivity(intent);
-    }
-
-    public void addBookmark(View v) {
-        if (mUri != null)
-            Browser.saveBookmark(this, mTitle, mUri.toString());
-
-        finish();
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-      // just save the uri from the intent
-      mUri = intent.getData();
-      mTitle = intent.getStringExtra(TITLE_KEY);
-    }
-}
--- a/embedding/android/Makefile.in
+++ b/embedding/android/Makefile.in
@@ -51,17 +51,16 @@ JAVAFILES = \
   GeckoAppShell.java \
   GeckoConnectivityReceiver.java \
   GeckoEvent.java \
   GeckoSurfaceView.java \
   GeckoInputConnection.java \
   AlertNotification.java \
   AwesomeBar.java \
   AwesomeBarTabs.java \
-  GeckoBookmarks.java \
   Tab.java \
   Tabs.java \
   GeckoEventListener.java \
   GeckoPreferences.java \
   PromptService.java \
   SurfaceInfo.java \
   DoorHanger.java \
   DoorHangerPopup.java \
@@ -140,18 +139,16 @@ RES_LAYOUT = \
   res/layout/notification_icon_text.xml \
   res/layout/launch_app_list.xml \
   res/layout/launch_app_listitem.xml \
   res/layout/awesomebar_search.xml \
   res/layout/awesomebar_header_row.xml \
   res/layout/awesomebar_row.xml \
   res/layout/awesomebar_tabs.xml \
   res/layout/browser_toolbar.xml \
-  res/layout/bookmarks.xml \
-  res/layout/bookmark_list_row.xml \
   res/layout/tabs_tray.xml \
   res/layout/tabs_row.xml \
   res/layout/doorhangerpopup.xml \
   $(NULL)
 
 RES_VALUES = \
   res/values/colors.xml \
   res/values/styles.xml \
@@ -180,16 +177,19 @@ MOZ_ANDROID_DRAWABLES += embedding/andro
                          embedding/android/resources/drawable/favicon.png                     \
                          embedding/android/resources/drawable/reload.png                      \
                          embedding/android/resources/drawable/quit.png                        \
                          embedding/android/resources/drawable/start.png                       \
                          embedding/android/resources/drawable/address_bar_button_left.9.png   \
                          embedding/android/resources/drawable/address_bar_button_right.9.png  \
                          embedding/android/resources/drawable/address_bar_button_middle.9.png \
                          embedding/android/resources/drawable/address_bar_bg.9.png            \
+                         embedding/android/resources/drawable/bookmark_add.png                \
+                         embedding/android/resources/drawable/bookmark_remove.png             \
+                         embedding/android/resources/drawable/addons.png                      \
                          embedding/android/resources/drawable/share.png                       \
                          $(NULL)
 
 
 MOZ_ANDROID_DRAWABLES += $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn | tr '\n' ' ';  fi)
 
 include $(topsrcdir)/config/rules.mk
 
--- a/embedding/android/Tab.java
+++ b/embedding/android/Tab.java
@@ -35,29 +35,32 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko;
 
 import java.util.*;
 
 import android.content.*;
+import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.AsyncTask;
 import android.graphics.drawable.*;
 import android.util.Log;
+import android.provider.Browser;
 
 public class Tab {
 
     private static final String LOG_FILE_NAME = "Tab";
     private int id;
     private String url, title;
     private Drawable favicon, thumbnail;
     private Stack<HistoryEntry> history;
     private boolean loading;
+    private boolean bookmark;
 
     static class HistoryEntry {
         public final String mUri;
         public final String mTitle;
 
         public HistoryEntry(String uri, String title) {
             mUri = uri;
             mTitle = title;
@@ -66,25 +69,27 @@ public class Tab {
 
     public Tab() {
         this.id = -1;
         this.url = new String();
         this.title = new String();
         this.favicon = null;
         this.thumbnail = null;
         this.history = new Stack<HistoryEntry>();
+        this.bookmark = false;
     }
 
     public Tab(int id, String url) {
         this.id = id;
         this.url = new String(url);
         this.title = new String();
         this.favicon = null;
         this.thumbnail = null;
         this.history = new Stack<HistoryEntry>();
+        this.bookmark = false;
     }
 
     public int getId() {
         return id;
     }
 
     public String getURL() {
         return url;
@@ -97,39 +102,48 @@ public class Tab {
     public Drawable getFavicon() {
         return favicon;
     }
 
     public boolean isLoading() {
         return loading;
     }
 
+    public boolean isBookmark() {
+        return bookmark;
+    }
+
     public Stack<HistoryEntry> getHistory() {
         return history;
     }
 
     public void updateURL(String url) {
 
         if(url != null && url.length() > 0) {
             this.url = new String(url);
             Log.i(LOG_FILE_NAME, "Updated url: " + url + " for tab with id: " + this.id);
+            updateBookmark();
         }
     }
 
     public void updateTitle(String title) {
         if(title != null && title.length() > 0) {
             this.title = new String(title);
             Log.i(LOG_FILE_NAME, "Updated title: " + title + " for tab with id: " + this.id);
         }
     }
 
     public void setLoading(boolean loading) {
         this.loading = loading;
     }
 
+    private void setBookmark(boolean bookmark) {
+        this.bookmark = bookmark;
+    }
+
     public void addHistory(HistoryEntry entry) {
        if (history.empty() || !history.peek().mUri.equals(entry.mUri)) {
            history.push(entry);
            new HistoryEntryTask().execute(entry);
        }
     }
 
     public HistoryEntry getLastHistoryEntry() {
@@ -137,16 +151,28 @@ public class Tab {
            return null;
        return history.peek();
     }
 
     public void updateFavicon(Drawable favicon) {
         this.favicon = favicon;
         Log.i(LOG_FILE_NAME, "Updated favicon for tab with id: " + this.id);
     }
+ 
+    private void updateBookmark() {
+        new CheckBookmarkTask().execute();
+    }
+
+    public void addBookmark() {
+        new AddBookmarkTask().execute();
+    }
+
+    public void removeBookmark() {
+        new RemoveBookmarkTask().execute();
+    }
 
     public boolean doReload() {
         if (history.empty())
             return false;
         GeckoEvent e = new GeckoEvent("session-reload", "");
         GeckoAppShell.sendEventToGecko(e);
         return true;
     }
@@ -163,9 +189,85 @@ public class Tab {
 
     private class HistoryEntryTask extends AsyncTask<HistoryEntry, Void, Void> {
         protected Void doInBackground(HistoryEntry... entries) {
             HistoryEntry entry = entries[0];
             GlobalHistory.getInstance().add(entry.mUri);
             return null;
         }
     }
+
+    private class CheckBookmarkTask extends AsyncTask<Void, Void, Boolean> {
+        @Override
+        protected Boolean doInBackground(Void... unused) {
+            ContentResolver resolver = Tabs.getInstance().getContentResolver();
+            Cursor cursor = resolver.query(Browser.BOOKMARKS_URI,
+                                           null,
+                                           Browser.BookmarkColumns.URL + " = ? and " + Browser.BookmarkColumns.BOOKMARK + " = ?",
+                                           new String[] { getURL(), "1" },
+                                           Browser.BookmarkColumns.URL);
+            if (cursor.getCount() == 1)
+                return true;
+            else
+                return false;
+        }
+
+        @Override
+        protected void onPostExecute(Boolean isBookmark) {
+            setBookmark(isBookmark.booleanValue());
+        }
+    }
+
+    private class AddBookmarkTask extends AsyncTask<Void, Void, Void> {
+        @Override
+        protected Void doInBackground(Void... unused) {
+            ContentResolver resolver = Tabs.getInstance().getContentResolver();
+            Cursor cursor = resolver.query(Browser.BOOKMARKS_URI,
+                                           null,
+                                           Browser.BookmarkColumns.URL + " = ?",
+                                           new String[] { getURL() },
+                                           Browser.BookmarkColumns.URL);
+
+            ContentValues values = new ContentValues();
+            values.put(Browser.BookmarkColumns.BOOKMARK, "1");
+            values.put(Browser.BookmarkColumns.TITLE, getTitle());
+
+            if (cursor.getCount() == 1) {
+                //entry exists, update the bookmark flag
+                resolver.update(Browser.BOOKMARKS_URI,
+                                values,
+                                Browser.BookmarkColumns.URL + " = ?",
+                                new String[] { getURL() });
+            } else {
+                //add a new entry
+                values.put(Browser.BookmarkColumns.URL, url);
+                resolver.insert(Browser.BOOKMARKS_URI,
+                                values);
+           }
+
+           return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void unused) {
+            setBookmark(true);
+        }
+    }
+
+    private class RemoveBookmarkTask extends AsyncTask<Void, Void, Void> {
+        @Override
+        protected Void doInBackground(Void... unused) {
+            ContentResolver resolver = Tabs.getInstance().getContentResolver();
+            ContentValues values = new ContentValues();
+            values.put(Browser.BookmarkColumns.BOOKMARK, "0");
+            resolver.update(Browser.BOOKMARKS_URI,
+                            values,
+                            Browser.BookmarkColumns.URL + " = ?",
+                            new String[] { getURL() });
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void unused) {
+            setBookmark(false);
+        }
+    }
 } 
--- a/embedding/android/Tabs.java
+++ b/embedding/android/Tabs.java
@@ -34,25 +34,27 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko;
 
 import java.util.*;
 
+import android.content.ContentResolver;
 import android.graphics.drawable.*;
 import android.util.Log;
 
 public class Tabs {
 
     private static final String LOG_NAME = "Tabs";
     private static int selectedTab = -1;
     private HashMap<Integer, Tab> tabs;
     private ArrayList<Tab> order;
+    private ContentResolver resolver;
 
     private Tabs() {
         tabs = new HashMap<Integer, Tab>();
         order = new ArrayList<Tab>();
     }
 
     public int getCount() {
         return tabs.size();
@@ -121,16 +123,24 @@ public class Tabs {
 
     public HashMap<Integer, Tab> getTabs() {
         if (getCount() == 0)
             return null;
 
         return tabs;
     }
 
+    public void setContentResolver(ContentResolver resolver) {
+        this.resolver = resolver;
+    }
+
+    public ContentResolver getContentResolver() {
+        return resolver;
+    }
+
     //Making Tabs a singleton class
     private static class TabsInstanceHolder {
         private static final Tabs INSTANCE = new Tabs();
     }
 
     public static Tabs getInstance() {
        return Tabs.TabsInstanceHolder.INSTANCE;
     }
--- a/embedding/android/locales/en-US/android_strings.dtd
+++ b/embedding/android/locales/en-US/android_strings.dtd
@@ -19,18 +19,20 @@
 <!ENTITY  exit_label "Exit">
 <!ENTITY  continue_label "Continue">
 
 <!ENTITY  launcher_shortcuts_title "&brandShortName; Web Apps">
 <!ENTITY  launcher_shortcuts_empty "No web apps were found">
 
 <!ENTITY choose_file "Choose File">
 <!ENTITY bookmarks_title "Bookmarks">
-<!ENTITY bookmarks "Bookmarks">
-<!ENTITY bookmark_add "Add Bookmark">
+<!ENTITY bookmark_add "Bookmark">
+<!ENTITY bookmark_remove "Remove">
+<!ENTITY bookmark_added "Bookmark added successfully!">
+<!ENTITY bookmark_removed "Bookmark removed!">
 
 <!ENTITY history_today_section "Today">
 <!ENTITY history_yesterday_section "Yesterday">
 <!ENTITY history_week_section "7 days ago">
 <!ENTITY history_older_section "Older than 7 days">
 
 <!ENTITY reload "Reload">
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..afd9c89544d6a5255505967b44b11040b5150465
GIT binary patch
literal 1839
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ee1defd1185c85af9853de567c21e4c6283d35ad
GIT binary patch
literal 2686
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
EFCPVZXaE2J
deleted file mode 100644
--- a/embedding/android/resources/layout/bookmark_list_row.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="?android:attr/listPreferredItemHeight"
-    android:padding="6dip">
-    <ImageView
-        android:id="@+id/bookmark_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="fill_parent"
-        android:layout_marginRight="6dip"/>
-    <LinearLayout
-        android:orientation="vertical"
-        android:layout_width="0dip"
-        android:layout_weight="1"
-        android:layout_height="fill_parent">
-        <TextView
-            android:id="@+id/bookmark_title"
-            android:layout_width="fill_parent"
-            android:layout_height="0dip"
-            android:layout_weight="1"
-            android:gravity="center_vertical"
-        />
-        <TextView
-            android:layout_width="fill_parent"
-            android:layout_height="0dip"
-            android:layout_weight="1"
-            android:id="@+id/bookmark_url"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-        />
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
deleted file mode 100644
--- a/embedding/android/resources/layout/bookmarks.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="vertical"
-              android:layout_width="fill_parent"
-              android:layout_height="fill_parent"
-              >
-  <Button
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:text="@string/bookmark_add"
-      android:onClick="addBookmark"/>
-  <ListView
-      android:id="@+id/android:list"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      />
-</LinearLayout>
--- a/embedding/android/resources/layout/gecko_menu.xml
+++ b/embedding/android/resources/layout/gecko_menu.xml
@@ -1,16 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
 
     <item android:id="@+id/reload"
           android:title="@string/reload"/>
 
-    <item android:id="@+id/bookmarks"
-          android:title="@string/bookmarks"
+    <item android:id="@+id/bookmark"
+          android:title="@string/bookmark_add"
+          android:icon="@drawable/bookmark_add"
           android:showAsAction="ifRoom"/>
 
     <item android:id="@+id/preferences"
           android:icon="@android:drawable/ic_menu_preferences"
           android:enabled="false"
           android:title="@string/preferences" />
 
     <item android:id="@+id/share"
--- a/embedding/android/strings.xml.in
+++ b/embedding/android/strings.xml.in
@@ -26,18 +26,20 @@
 
   <string name="launcher_shortcuts_title">&launcher_shortcuts_title;</string>
   <string name="launcher_shortcuts_empty">&launcher_shortcuts_empty;</string>
   
   <string name="choose_file">&choose_file;</string>
 
   <string name="quit">&quit;</string>
   <string name="bookmarks_title">&bookmarks_title;</string>
-  <string name="bookmarks">&bookmarks;</string>
   <string name="bookmark_add">&bookmark_add;</string>
+  <string name="bookmark_remove">&bookmark_remove;</string>
+  <string name="bookmark_added">&bookmark_added;</string>
+  <string name="bookmark_removed">&bookmark_removed;</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_section;</string>
   <string name="history_older_section">&history_older_section;</string>
 
   <string name="share">&share;</string>
   <string name="saveaspdf">&saveaspdf;</string>