Adds context menu for folders (edit/delete)

 Bug: 3214901
 Adds support for folder context menus to edit and delete folders.

Change-Id: I89cb2fa4b139ba3f59ec62b34154d5837c7f471b
diff --git a/res/layout/browser_add_bookmark.xml b/res/layout/browser_add_bookmark.xml
index 58b7a6b..0eaf526 100644
--- a/res/layout/browser_add_bookmark.xml
+++ b/res/layout/browser_add_bookmark.xml
@@ -93,7 +93,8 @@
             android:textAppearance="?android:attr/textAppearanceMedium" />
       </TableRow>
 
-      <TableRow>
+      <TableRow
+          android:id="@+id/row_address">
         <TextView
             android:id="@+id/addressText"
             android:layout_height="wrap_content"
diff --git a/res/menu/bookmarkscontext.xml b/res/menu/bookmarkscontext.xml
index c58d459..4d7ec7a 100644
--- a/res/menu/bookmarkscontext.xml
+++ b/res/menu/bookmarkscontext.xml
@@ -15,7 +15,8 @@
 -->
 
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
-  <group android:id="@+id/CONTEXT_MENU">
+  <group android:id="@+id/BOOKMARK_CONTEXT_MENU"
+    android:visible="false">
     <item android:id="@+id/open_context_menu_id"
       android:title="@string/open_bookmark"/>
     <item android:id="@+id/new_window_context_menu_id"
@@ -33,4 +34,11 @@
     <item android:id="@+id/homepage_context_menu_id"
       android:title="@string/set_as_homepage"/>
   </group>
+  <group android:id="@+id/FOLDER_CONTEXT_MENU"
+    android:visible="false">
+    <item android:id="@+id/edit_context_menu_id"
+      android:title="@string/edit_folder"/>
+    <item android:id="@+id/delete_context_menu_id"
+      android:title="@string/delete_folder"/>
+  </group>
 </menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7d27dec..e98d9d5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -131,6 +131,10 @@
     <!-- Default name for a new folder and label for a button that allows the user to create a new folder
          in the add bookmark dialog -->
     <string name="new_folder">New folder</string>
+    <!-- Context menu item to edit a folder [CHAR LIMIT=50] -->
+    <string name="edit_folder">Edit folder</string>
+    <!-- Context menu item to delete a folder [CHAR LIMIT=50] -->
+    <string name="delete_folder">Delete folder</string>
     <!-- Label stating that the currently open folder has no subfolders
          in the add bookmark dialog [CHAR-LIMIT=none]-->
     <string name="no_subfolders">No subfolders</string>
@@ -230,6 +234,10 @@
     <string name="webarchive_saved">Web archive saved.</string>
     <!-- Toast informing the user that saving the page has failed. -->
     <string name="webarchive_failed">Failed to save web archive.</string>
+    <!-- The number of bookmarks in a folder [CHAR LIMT=50] -->
+    <string name="contextheader_folder_bookmarkcount"><xliff:g id="bookmark_count">%d</xliff:g> bookmarks</string>
+    <!-- No bookmarks in the folder [CHAR LIMIT=50] -->
+    <string name="contextheader_folder_empty">Empty folder</string>
     <!-- Context Menu item open the currently selected link in the current
             window.-->
     <string name="contextmenu_openlink">Open</string>
diff --git a/src/com/android/browser/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java
index 25c9217..de256a8 100644
--- a/src/com/android/browser/AddBookmarkPage.java
+++ b/src/com/android/browser/AddBookmarkPage.java
@@ -25,7 +25,6 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.CursorLoader;
-import android.content.Intent;
 import android.content.Loader;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
@@ -35,6 +34,7 @@
 import android.net.ParseException;
 import android.net.Uri;
 import android.net.WebAddress;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -60,7 +60,6 @@
 
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.ArrayList;
 import java.util.Stack;
 
 public class AddBookmarkPage extends Activity
@@ -74,6 +73,9 @@
     public static final String REMOVE_THUMBNAIL = "remove_thumbnail";
     public static final String USER_AGENT = "user_agent";
 
+    /* package */ static final String EXTRA_EDIT_BOOKMARK = "bookmark";
+    /* package */ static final String EXTRA_IS_FOLDER = "is_folder";
+
     private static final int MAX_CRUMBS_SHOWN = 2;
 
     private final String LOGTAG = "Bookmarks";
@@ -89,6 +91,7 @@
     private TextView    mButton;
     private View        mCancelButton;
     private boolean     mEditingExisting;
+    private boolean     mEditingFolder;
     private Bundle      mMap;
     private String      mTouchIconUrl;
     private String      mOriginalUrl;
@@ -238,6 +241,9 @@
             PopupMenu popup = new PopupMenu(this, mFolder);
             popup.getMenuInflater().inflate(R.menu.folder_choice,
                     popup.getMenu());
+            if (mEditingFolder) {
+                popup.getMenu().removeItem(R.id.home_screen);
+            }
             popup.setOnMenuItemClickListener(this);
             popup.show();
         } else if (v == mAddNewFolder) {
@@ -501,11 +507,15 @@
         mFakeTitle = (TextView) findViewById(R.id.fake_title);
 
         if (mMap != null) {
-            Bundle b = mMap.getBundle("bookmark");
+            Bundle b = mMap.getBundle(EXTRA_EDIT_BOOKMARK);
             if (b != null) {
+                mEditingFolder = mMap.getBoolean(EXTRA_IS_FOLDER, false);
                 mMap = b;
                 mEditingExisting = true;
                 mFakeTitle.setText(R.string.edit_bookmark);
+                if (mEditingFolder) {
+                    findViewById(R.id.row_address).setVisibility(View.GONE);
+                }
             } else {
                 int gravity = mMap.getInt("gravity", -1);
                 if (gravity != -1) {
@@ -648,6 +658,28 @@
         }
     }
 
+    private static class UpdateBookmarkTask extends AsyncTask<ContentValues, Void, Void> {
+        Context mContext;
+        Long mId;
+
+        public UpdateBookmarkTask(Context context, long id) {
+            mContext = context;
+            mId = id;
+        }
+
+        @Override
+        protected Void doInBackground(ContentValues... params) {
+            if (params.length != 1) {
+                throw new IllegalArgumentException("No ContentValues provided!");
+            }
+            Uri uri = ContentUris.withAppendedId(BookmarkUtils.getBookmarksUri(mContext), mId);
+            mContext.getContentResolver().update(
+                    uri,
+                    params[0], null, null);
+            return null;
+        }
+    }
+
     private void createHandler() {
         if (mHandler == null) {
             mHandler = new Handler() {
@@ -691,7 +723,7 @@
         boolean emptyTitle = title.length() == 0;
         boolean emptyUrl = unfilteredUrl.trim().length() == 0;
         Resources r = getResources();
-        if (emptyTitle || emptyUrl) {
+        if (emptyTitle || (emptyUrl && !mEditingFolder)) {
             if (emptyTitle) {
                 mTitle.setError(r.getText(R.string.bookmark_needs_title));
             }
@@ -702,36 +734,38 @@
 
         }
         String url = unfilteredUrl.trim();
-        try {
-            // We allow bookmarks with a javascript: scheme, but these will in most cases
-            // fail URI parsing, so don't try it if that's the kind of bookmark we have.
+        if (!mEditingFolder) {
+            try {
+                // We allow bookmarks with a javascript: scheme, but these will in most cases
+                // fail URI parsing, so don't try it if that's the kind of bookmark we have.
 
-            if (!url.toLowerCase().startsWith("javascript:")) {
-                URI uriObj = new URI(url);
-                String scheme = uriObj.getScheme();
-                if (!Bookmarks.urlHasAcceptableScheme(url)) {
-                    // If the scheme was non-null, let the user know that we
-                    // can't save their bookmark. If it was null, we'll assume
-                    // they meant http when we parse it in the WebAddress class.
-                    if (scheme != null) {
-                        mAddress.setError(r.getText(R.string.bookmark_cannot_save_url));
-                        return false;
+                if (!url.toLowerCase().startsWith("javascript:")) {
+                    URI uriObj = new URI(url);
+                    String scheme = uriObj.getScheme();
+                    if (!Bookmarks.urlHasAcceptableScheme(url)) {
+                        // If the scheme was non-null, let the user know that we
+                        // can't save their bookmark. If it was null, we'll assume
+                        // they meant http when we parse it in the WebAddress class.
+                        if (scheme != null) {
+                            mAddress.setError(r.getText(R.string.bookmark_cannot_save_url));
+                            return false;
+                        }
+                        WebAddress address;
+                        try {
+                            address = new WebAddress(unfilteredUrl);
+                        } catch (ParseException e) {
+                            throw new URISyntaxException("", "");
+                        }
+                        if (address.getHost().length() == 0) {
+                            throw new URISyntaxException("", "");
+                        }
+                        url = address.toString();
                     }
-                    WebAddress address;
-                    try {
-                        address = new WebAddress(unfilteredUrl);
-                    } catch (ParseException e) {
-                        throw new URISyntaxException("", "");
-                    }
-                    if (address.getHost().length() == 0) {
-                        throw new URISyntaxException("", "");
-                    }
-                    url = address.toString();
                 }
+            } catch (URISyntaxException e) {
+                mAddress.setError(r.getText(R.string.bookmark_url_not_valid));
+                return false;
             }
-        } catch (URISyntaxException e) {
-            mAddress.setError(r.getText(R.string.bookmark_url_not_valid));
-            return false;
         }
 
         if (mSaveToHomeScreen) {
@@ -741,13 +775,20 @@
         boolean urlUnmodified = url.equals(mOriginalUrl);
 
         if (mEditingExisting) {
-            mMap.putString(BrowserContract.Bookmarks.TITLE, title);
-            mMap.putString(BrowserContract.Bookmarks.URL, url);
-            mMap.putBoolean(REMOVE_THUMBNAIL, !urlUnmodified);
-            // FIXME: This does not work yet
-            mMap.putLong(BrowserContract.Bookmarks.PARENT, mCurrentFolder);
-            setResult(RESULT_OK, (new Intent()).setAction(
-                    getIntent().toString()).putExtras(mMap));
+            Long id = mMap.getLong(BrowserContract.Bookmarks._ID);
+            ContentValues values = new ContentValues();
+            values.put(BrowserContract.Bookmarks.TITLE, title);
+            values.put(BrowserContract.Bookmarks.PARENT, mCurrentFolder);
+            if (!mEditingFolder) {
+                values.put(BrowserContract.Bookmarks.URL, url);
+                if (!urlUnmodified) {
+                    values.putNull(BrowserContract.Bookmarks.THUMBNAIL);
+                }
+            }
+            if (values.size() > 0) {
+                new UpdateBookmarkTask(getApplicationContext(), id).execute(values);
+            }
+            setResult(RESULT_OK);
         } else {
             Bitmap thumbnail;
             Bitmap favicon;
@@ -794,5 +835,4 @@
         }
         return true;
     }
-
 }
diff --git a/src/com/android/browser/BookmarkUtils.java b/src/com/android/browser/BookmarkUtils.java
index 751c0b3..a63b90f 100644
--- a/src/com/android/browser/BookmarkUtils.java
+++ b/src/com/android/browser/BookmarkUtils.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
@@ -29,8 +30,10 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.net.Uri;
+import android.preference.PreferenceManager;
 import android.provider.Browser;
-import android.util.Log;
+import android.provider.BrowserContract;
+import android.text.TextUtils;
 
 class BookmarkUtils {
     private final static String LOGTAG = "BookmarkUtils";
@@ -165,4 +168,17 @@
         canvas.drawBitmap(favicon, null, r, p);
     }
 
+    /* package */ static Uri getBookmarksUri(Context context) {
+        Uri uri = BrowserContract.Bookmarks.CONTENT_URI;
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        String accountType = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_TYPE, null);
+        String accountName = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_NAME, null);
+        if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
+            uri = uri.buildUpon()
+                    .appendQueryParameter(BrowserContract.Bookmarks.PARAM_ACCOUNT_NAME, accountName)
+                    .appendQueryParameter(BrowserContract.Bookmarks.PARAM_ACCOUNT_TYPE, accountType)
+                    .build();
+        }
+        return uri;
+    }
 };
diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java
index 4370885..4887f3f 100644
--- a/src/com/android/browser/BrowserBookmarksPage.java
+++ b/src/com/android/browser/BrowserBookmarksPage.java
@@ -24,7 +24,6 @@
 import android.content.ClipboardManager;
 import android.content.ContentResolver;
 import android.content.ContentUris;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.CursorLoader;
 import android.content.DialogInterface;
@@ -36,6 +35,7 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
 import android.provider.BrowserContract;
@@ -75,7 +75,6 @@
         LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener, IconListener,
         OnItemSelectedListener, BreadCrumbView.Controller, OnClickListener, OnMenuItemClickListener {
 
-    static final int BOOKMARKS_SAVE = 1;
     static final String LOGTAG = "browser";
 
     static final int LOADER_BOOKMARKS = 1;
@@ -98,7 +97,6 @@
     ListView mList;
     BrowserBookmarksAdapter mAdapter;
     boolean mDisableNewWindow;
-    BookmarkItem mContextHeader;
     boolean mCanceled = false;
     boolean mEnableContextMenu = true;
     boolean mShowRootFolder = false;
@@ -331,42 +329,60 @@
         return BitmapFactory.decodeByteArray(data, 0, data.length);
     }
 
+    private MenuItem.OnMenuItemClickListener mContextItemClickListener =
+            new MenuItem.OnMenuItemClickListener() {
+        @Override
+        public boolean onMenuItemClick(MenuItem item) {
+            return onContextItemSelected(item);
+        }
+    };
+
     @Override
     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
         AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
         Cursor cursor = mAdapter.getItem(info.position);
         boolean isFolder
                 = cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) != 0;
-        if (isFolder) return;
 
         final Activity activity = getActivity();
         MenuInflater inflater = activity.getMenuInflater();
         inflater.inflate(R.menu.bookmarkscontext, menu);
-
-        if (mDisableNewWindow) {
-            menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
+        if (isFolder) {
+            menu.setGroupVisible(R.id.FOLDER_CONTEXT_MENU, true);
+        } else {
+            menu.setGroupVisible(R.id.BOOKMARK_CONTEXT_MENU, true);
+            if (mDisableNewWindow) {
+                menu.findItem(R.id.new_window_context_menu_id).setVisible(false);
+            }
         }
+        BookmarkItem header = new BookmarkItem(activity);
+        populateBookmarkItem(cursor, header, isFolder);
+        new LookupBookmarkCount(getActivity(), header)
+                .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
+        menu.setHeaderView(header);
 
-        if (mContextHeader == null) {
-            mContextHeader = new BookmarkItem(activity);
-        } else if (mContextHeader.getParent() != null) {
-            ((ViewGroup) mContextHeader.getParent()).removeView(mContextHeader);
+        int count = menu.size();
+        for (int i = 0; i < count; i++) {
+            menu.getItem(i).setOnMenuItemClickListener(mContextItemClickListener);
         }
-
-        populateBookmarkItem(cursor, mContextHeader);
-
-        menu.setHeaderView(mContextHeader);
     }
 
-    private void populateBookmarkItem(Cursor cursor, BookmarkItem item) {
-        String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
-        item.setUrl(url);
+    private void populateBookmarkItem(Cursor cursor, BookmarkItem item, boolean isFolder) {
         item.setName(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE));
-        Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
-        if (bitmap == null) {
-            bitmap = CombinedBookmarkHistoryView.getIconListenerSet().getFavicon(url);
+        if (isFolder) {
+            item.setUrl(null);
+            Bitmap bitmap =
+                BitmapFactory.decodeResource(getResources(), R.drawable.ic_folder);
+            item.setFavicon(bitmap);
+        } else {
+            String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
+            item.setUrl(url);
+            Bitmap bitmap = getBitmap(cursor, BookmarksLoader.COLUMN_INDEX_FAVICON);
+            if (bitmap == null) {
+                bitmap = CombinedBookmarkHistoryView.getIconListenerSet().getFavicon(url);
+            }
+            item.setFavicon(bitmap);
         }
-        item.setFavicon(bitmap);
     }
 
     /**
@@ -566,73 +582,14 @@
             item.putParcelable(BrowserContract.Bookmarks.FAVICON,
                     BitmapFactory.decodeByteArray(data, 0, data.length));
         }
-        item.putInt("id", cursor.getInt(BookmarksLoader.COLUMN_INDEX_ID));
+        item.putLong(BrowserContract.Bookmarks._ID,
+                cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
         item.putLong(BrowserContract.Bookmarks.PARENT,
                 cursor.getLong(BookmarksLoader.COLUMN_INDEX_PARENT));
-        intent.putExtra("bookmark", item);
-        startActivityForResult(intent, BOOKMARKS_SAVE);
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        switch(requestCode) {
-            case BOOKMARKS_SAVE:
-                if (resultCode == Activity.RESULT_OK) {
-                    Bundle extras;
-                    if (data != null && (extras = data.getExtras()) != null) {
-                        // If there are extras, then we need to save
-                        // the edited bookmark. This is done in updateRow()
-                        String title = extras.getString(BrowserContract.Bookmarks.TITLE);
-                        String url = extras.getString(BrowserContract.Bookmarks.URL);
-                        if (title != null && url != null) {
-                            updateRow(extras);
-                        }
-                    }
-                }
-                break;
-        }
-    }
-
-    /**
-     *  Update a row in the database with new information.
-     *  @param map  Bundle storing id, title and url of new information
-     */
-    public void updateRow(Bundle map) {
-
-        // Find the record
-        int id = map.getInt("id");
-        int position = -1;
-        Cursor cursor = mAdapter.getCursor();
-        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
-            if (cursor.getInt(BookmarksLoader.COLUMN_INDEX_ID) == id) {
-                position = cursor.getPosition();
-                break;
-            }
-        }
-        if (position < 0) {
-            return;
-        }
-
-        cursor.moveToPosition(position);
-        ContentValues values = new ContentValues();
-        String title = map.getString(BrowserContract.Bookmarks.TITLE);
-        if (!title.equals(cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE))) {
-            values.put(BrowserContract.Bookmarks.TITLE, title);
-        }
-        String url = map.getString(BrowserContract.Bookmarks.URL);
-        if (!url.equals(cursor.getString(BookmarksLoader.COLUMN_INDEX_URL))) {
-            values.put(BrowserContract.Bookmarks.URL, url);
-        }
-
-        if (map.getBoolean(AddBookmarkPage.REMOVE_THUMBNAIL)) {
-            values.putNull(BrowserContract.Bookmarks.THUMBNAIL);
-        }
-
-        if (values.size() > 0) {
-            getActivity().getContentResolver().update(
-                    ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id),
-                    values, null, null);
-        }
+        intent.putExtra(AddBookmarkPage.EXTRA_EDIT_BOOKMARK, item);
+        intent.putExtra(AddBookmarkPage.EXTRA_IS_FOLDER,
+                cursor.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1);
+        startActivity(intent);
     }
 
     private void displayRemoveBookmarkDialog(final int position) {
@@ -779,4 +736,36 @@
             }
         }
     }
+
+    private static class LookupBookmarkCount extends AsyncTask<Long, Void, Integer> {
+        Context mContext;
+        BookmarkItem mHeader;
+
+        public LookupBookmarkCount(Context context, BookmarkItem header) {
+            mContext = context;
+            mHeader = header;
+        }
+
+        @Override
+        protected Integer doInBackground(Long... params) {
+            if (params.length != 1) {
+                throw new IllegalArgumentException("Missing folder id!");
+            }
+            Uri uri = BookmarkUtils.getBookmarksUri(mContext);
+            Cursor c = mContext.getContentResolver().query(uri,
+                    null, BrowserContract.Bookmarks.PARENT + "=?",
+                    new String[] {params[0].toString()}, null);
+            return c.getCount();
+        }
+
+        @Override
+        protected void onPostExecute(Integer result) {
+            if (result > 0) {
+                mHeader.setUrl(mContext.getString(R.string.contextheader_folder_bookmarkcount,
+                        result));
+            } else if (result == 0) {
+                mHeader.setUrl(mContext.getString(R.string.contextheader_folder_empty));
+            }
+        }
+    }
 }