Implementing group renaming and deletion

Change-Id: I5d55d0d45f1e275e1ebf4f607ef09aba1c6bedc3
diff --git a/res/layout/group_name_dialog.xml b/res/layout/group_name_dialog.xml
new file mode 100644
index 0000000..b22c8e0
--- /dev/null
+++ b/res/layout/group_name_dialog.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="25dip"
+    android:paddingRight="25dip"
+    android:paddingBottom="25dip"
+    android:paddingLeft="25dip">
+    <EditText
+        android:id="@+id/group_label"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+</FrameLayout>
diff --git a/res/menu/actions.xml b/res/menu/actions.xml
index 8afd8c2..a53967f 100644
--- a/res/menu/actions.xml
+++ b/res/menu/actions.xml
@@ -33,4 +33,13 @@
         android:icon="@drawable/ic_menu_import_export_holo_light"
         android:title="@string/menu_import_export" />
 
+    <item
+        android:id="@+id/menu_rename_group"
+        android:icon="@drawable/ic_menu_settings_holo_light"
+        android:title="@string/menu_renameGroup" />
+
+    <item
+        android:id="@+id/menu_delete_group"
+        android:icon="@drawable/ic_menu_trash_holo_light"
+        android:title="@string/menu_deleteGroup" />
 </menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index eda0d74..1374ba9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -125,6 +125,12 @@
     <!-- Menu item that splits an item from the contact detail into a separate aggregate -->
     <string name="menu_splitAggregate">Separate</string>
 
+    <!-- Menu item that renames the currently selected group -->
+    <string name="menu_renameGroup">Rename group</string>
+
+    <!-- Menu item that deletes the currently selected group -->
+    <string name="menu_deleteGroup">Delete group</string>
+
     <!-- Title of the confirmation dialog for separating contacts into multiple instances  -->
     <string name="splitConfirmation_title">Separate Contact</string>
 
@@ -942,7 +948,7 @@
     <string name="dialog_import_export">Import/Export contacts</string>
 
     <!-- The menu item to share the currently viewed contact -->
-    <string name="menu_share">Share</string>
+    <string name="menu_share">Share contact</string>
 
     <!-- Dialog title when picking the application to share a contact with. -->
     <string name="share_via">Share contact via</string>
@@ -1401,4 +1407,16 @@
     Initiates a contact import dialog [CHAR LIMIT=128] -->
     <string name="contacts_unavailable_import_contacts">Import contacts from a file</string>
 
+    <!-- Title of the dialog that allows renaming of a contact group [CHAR LIMIT=128] -->
+    <string name="rename_group_dialog_title">Rename group</string>
+
+    <!-- Title of the dialog that allows deletion of a contact group [CHAR LIMIT=128] -->
+    <string name="delete_group_dialog_title">Delete group</string>
+
+    <!-- Confirmation message of the dialog that allows deletion of a contact group  [CHAR LIMIT=256] -->
+    <string name="delete_group_dialog_message">Are you sure you want to delete the group 
+      \'<xliff:g id="group_label" example="Friends">%1$s</xliff:g>\'?
+      (Contacts themselves will not be deleted.)
+    </string>
+
 </resources>
diff --git a/src/com/android/contacts/activities/ContactBrowserActivity.java b/src/com/android/contacts/activities/ContactBrowserActivity.java
index 7232d63..b47c494 100644
--- a/src/com/android/contacts/activities/ContactBrowserActivity.java
+++ b/src/com/android/contacts/activities/ContactBrowserActivity.java
@@ -18,6 +18,8 @@
 
 import com.android.contacts.R;
 import com.android.contacts.interactions.ContactDeletionInteraction;
+import com.android.contacts.interactions.GroupDeletionDialogFragment;
+import com.android.contacts.interactions.GroupRenamingDialogFragment;
 import com.android.contacts.interactions.ImportExportInteraction;
 import com.android.contacts.interactions.PhoneNumberInteraction;
 import com.android.contacts.list.ContactBrowseListContextMenuAdapter;
@@ -401,6 +403,8 @@
         if (mContactContentDisplayed) {
             setupContactDetailFragment(mListFragment.getSelectedContactUri());
         }
+
+        invalidateOptionsMenu();
     }
 
     /**
@@ -431,6 +435,8 @@
         } else if (mContactContentDisplayed) {
             setupContactDetailFragment(mListFragment.getSelectedContactUri());
         }
+
+        invalidateOptionsMenu();
     }
 
     /**
@@ -478,6 +484,8 @@
         if (mContactContentDisplayed) {
             setupContactDetailFragment(requestedContactUri);
         }
+
+        invalidateOptionsMenu();
     }
 
     /**
@@ -883,6 +891,26 @@
             displayGroups.setVisible(
                     mRequest.getActionCode() == ContactsRequest.ACTION_DEFAULT);
         }
+
+        boolean groupSelected = false;
+        if (mListFragment instanceof DefaultContactBrowseListFragment) {
+            ContactListFilter filter =
+                    ((DefaultContactBrowseListFragment)mListFragment).getFilter();
+            if (filter != null && filter.filterType == ContactListFilter.FILTER_TYPE_GROUP) {
+                groupSelected = true;
+            }
+        }
+
+        MenuItem renameGroup = menu.findItem(R.id.menu_rename_group);
+        if (renameGroup != null) {
+            renameGroup.setVisible(groupSelected);
+        }
+
+        MenuItem deleteGroup = menu.findItem(R.id.menu_delete_group);
+        if (deleteGroup != null) {
+            deleteGroup.setVisible(groupSelected);
+        }
+
         return true;
     }
 
@@ -915,6 +943,20 @@
                 startActivity(intent);
                 return true;
             }
+            case R.id.menu_rename_group: {
+                ContactListFilter filter =
+                        ((DefaultContactBrowseListFragment)mListFragment).getFilter();
+                GroupRenamingDialogFragment.show(getFragmentManager(), filter.groupId,
+                        filter.title);
+                return true;
+            }
+            case R.id.menu_delete_group: {
+                ContactListFilter filter =
+                        ((DefaultContactBrowseListFragment)mListFragment).getFilter();
+                GroupDeletionDialogFragment.show(getFragmentManager(), filter.groupId,
+                        filter.title);
+                return true;
+            }
         }
         return false;
     }
diff --git a/src/com/android/contacts/interactions/GroupDeletionDialogFragment.java b/src/com/android/contacts/interactions/GroupDeletionDialogFragment.java
new file mode 100644
index 0000000..2693dfa
--- /dev/null
+++ b/src/com/android/contacts/interactions/GroupDeletionDialogFragment.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.interactions;
+
+import com.android.contacts.R;
+import com.android.contacts.views.ContactSaveService;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+/**
+ * A dialog for deleting a group.
+ */
+public class GroupDeletionDialogFragment extends DialogFragment {
+
+    private static final String ARG_GROUP_ID = "groupId";
+    private static final String ARG_LABEL = "label";
+
+    public static void show(FragmentManager fragmentManager, long groupId, String label) {
+        GroupDeletionDialogFragment dialog = new GroupDeletionDialogFragment();
+        Bundle args = new Bundle();
+        args.putLong(ARG_GROUP_ID, groupId);
+        args.putString(ARG_LABEL, label);
+        dialog.setArguments(args);
+        dialog.show(fragmentManager, "deleteGroup");
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        String label = getArguments().getString(ARG_LABEL);
+        String message = getActivity().getString(R.string.delete_group_dialog_message, label);
+
+        return new AlertDialog.Builder(getActivity())
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setTitle(R.string.delete_group_dialog_title)
+                .setMessage(message)
+                .setPositiveButton(android.R.string.ok,
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int whichButton) {
+                            deleteGroup();
+                        }
+                    }
+                )
+                .setNegativeButton(android.R.string.cancel, null)
+                .create();
+    }
+
+    protected void deleteGroup() {
+        Bundle arguments = getArguments();
+        long groupId = arguments.getLong(ARG_GROUP_ID);
+
+        getActivity().startService(ContactSaveService.createGroupDeletionIntent(
+                getActivity(), groupId));
+    }
+}
diff --git a/src/com/android/contacts/interactions/GroupRenamingDialogFragment.java b/src/com/android/contacts/interactions/GroupRenamingDialogFragment.java
new file mode 100644
index 0000000..6339e79
--- /dev/null
+++ b/src/com/android/contacts/interactions/GroupRenamingDialogFragment.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.interactions;
+
+import com.android.contacts.R;
+import com.android.contacts.views.ContactSaveService;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+
+/**
+ * A dialog for renaming a group.
+ */
+public class GroupRenamingDialogFragment extends DialogFragment {
+
+    private static final String ARG_GROUP_ID = "groupId";
+    private static final String ARG_LABEL = "label";
+
+    private EditText mEdit;
+
+    public static void show(FragmentManager fragmentManager, long groupId, String label) {
+        GroupRenamingDialogFragment dialog = new GroupRenamingDialogFragment();
+        Bundle args = new Bundle();
+        args.putLong(ARG_GROUP_ID, groupId);
+        args.putString(ARG_LABEL, label);
+        dialog.setArguments(args);
+        dialog.show(fragmentManager, "renameGroup");
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        View view = LayoutInflater.from(getActivity()).inflate(R.layout.group_name_dialog, null);
+        mEdit = (EditText) view.findViewById(R.id.group_label);
+        String label = getArguments().getString(ARG_LABEL);
+        mEdit.setText(label);
+        if (label != null) {
+            mEdit.setSelection(label.length());
+        }
+
+        return new AlertDialog.Builder(getActivity())
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setTitle(R.string.rename_group_dialog_title)
+                .setView(view)
+                .setPositiveButton(android.R.string.ok,
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int whichButton) {
+                            saveGroupLabel();
+                        }
+                    }
+                )
+                .setNegativeButton(android.R.string.cancel, null)
+                .create();
+    }
+
+    protected void saveGroupLabel() {
+        String newLabel = mEdit.getText().toString();
+
+        Bundle arguments = getArguments();
+        long groupId = arguments.getLong(ARG_GROUP_ID);
+
+        getActivity().startService(ContactSaveService.createGroupRenameIntent(
+                getActivity(), groupId, newLabel));
+    }
+}
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 68f0607..e1100f6 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -58,6 +58,10 @@
         mFilter = filter;
     }
 
+    public ContactListFilter getFilter() {
+        return mFilter;
+    }
+
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
diff --git a/src/com/android/contacts/views/ContactSaveService.java b/src/com/android/contacts/views/ContactSaveService.java
index 91d2413..04ab43a 100644
--- a/src/com/android/contacts/views/ContactSaveService.java
+++ b/src/com/android/contacts/views/ContactSaveService.java
@@ -16,7 +16,6 @@
 
 package com.android.contacts.views;
 
-import com.android.contacts.activities.ContactBrowserActivity;
 import com.google.android.collect.Sets;
 
 import android.accounts.Account;
@@ -25,6 +24,7 @@
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Intent;
 import android.content.OperationApplicationException;
@@ -33,6 +33,7 @@
 import android.os.RemoteException;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Groups;
 import android.provider.ContactsContract.RawContacts;
 import android.util.Log;
 
@@ -52,6 +53,11 @@
 
     public static final String EXTRA_OPERATIONS = "Operations";
 
+    public static final String ACTION_RENAME_GROUP = "renameGroup";
+    public static final String ACTION_DELETE_GROUP = "deleteGroup";
+    public static final String EXTRA_GROUP_ID = "groupId";
+    public static final String EXTRA_GROUP_LABEL = "groupLabel";
+
     private static final HashSet<String> ALLOWED_DATA_COLUMNS = Sets.newHashSet(
         Data.MIMETYPE,
         Data.IS_PRIMARY,
@@ -82,6 +88,10 @@
         String action = intent.getAction();
         if (ACTION_NEW_RAW_CONTACT.equals(action)) {
             createRawContact(intent);
+        } else if (ACTION_RENAME_GROUP.equals(action)) {
+            renameGroup(intent);
+        } else if (ACTION_DELETE_GROUP.equals(action)) {
+            deleteGroup(intent);
         } else {
             performContentProviderOperations(intent);
         }
@@ -169,4 +179,51 @@
         serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
         return serviceIntent;
     }
+
+    private void renameGroup(Intent intent) {
+        long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1);
+        String label = intent.getStringExtra(EXTRA_GROUP_LABEL);
+
+        if (groupId == -1) {
+            Log.e(TAG, "Invalid arguments for renameGroup request");
+            return;
+        }
+
+        ContentValues values = new ContentValues();
+        values.put(Groups.TITLE, label);
+        getContentResolver().update(
+                ContentUris.withAppendedId(Groups.CONTENT_URI, groupId), values, null, null);
+    }
+
+    /**
+     * Creates an intent that can be sent to this service to rename a group.
+     */
+    public static Intent createGroupRenameIntent(Activity activity, long groupId, String newLabel) {
+        Intent serviceIntent = new Intent(activity, ContactSaveService.class);
+        serviceIntent.setAction(ContactSaveService.ACTION_RENAME_GROUP);
+        serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId);
+        serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, newLabel);
+        return serviceIntent;
+    }
+
+    private void deleteGroup(Intent intent) {
+        long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1);
+        if (groupId == -1) {
+            Log.e(TAG, "Invalid arguments for deleteGroup request");
+            return;
+        }
+
+        getContentResolver().delete(
+                ContentUris.withAppendedId(Groups.CONTENT_URI, groupId), null, null);
+    }
+
+    /**
+     * Creates an intent that can be sent to this service to delete a group.
+     */
+    public static Intent createGroupDeletionIntent(Activity activity, long groupId) {
+        Intent serviceIntent = new Intent(activity, ContactSaveService.class);
+        serviceIntent.setAction(ContactSaveService.ACTION_DELETE_GROUP);
+        serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId);
+        return serviceIntent;
+    }
 }