Merge "Cleanup/ refactor apply/remove folder dialogs."
diff --git a/res/layout/folders_view.xml b/res/layout/multi_folders_view.xml
similarity index 100%
rename from res/layout/folders_view.xml
rename to res/layout/multi_folders_view.xml
diff --git a/res/layout/single_folders_view.xml b/res/layout/single_folders_view.xml
new file mode 100644
index 0000000..003f5fa
--- /dev/null
+++ b/res/layout/single_folders_view.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2010 Google Inc.
+ *
+ * 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.
+ */
+-->
+
+<!-- Describes an individual toggleable label entry to be displayed in a list of labels in
+ a label selection UI. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/labels"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:height="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:padding="3dip">
+
+ <!-- Note: the checkbox is not focusable because the parent list item itself handles
+ the toggling -->
+ <RadioButton android:id="@+id/checkbox"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_margin="4dip"
+ android:singleLine="false"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:drawablePadding="20dip"
+ android:focusable="false"
+ android:focusableInTouchMode="false"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+ <View
+ android:id="@+id/color_block"
+ android:layout_height="36dip"
+ android:layout_width="36dip"
+ android:layout_gravity="right|top"
+ android:layout_margin="4dip" />
+
+</LinearLayout>
diff --git a/src/com/android/mail/ui/ApplyRemoveFolderDialog.java b/src/com/android/mail/ui/ApplyRemoveFolderDialog.java
deleted file mode 100644
index 0eb2423..0000000
--- a/src/com/android/mail/ui/ApplyRemoveFolderDialog.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2012 Google Inc.
- * Licensed to 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.mail.ui;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.database.Cursor;
-import android.net.Uri;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ListView;
-
-import com.android.mail.R;
-import com.android.mail.providers.Account;
-import com.android.mail.providers.Conversation;
-import com.android.mail.providers.UIProvider;
-import com.android.mail.ui.FoldersSelectionDialog.CommitListener;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Apply labels to a conversation.
- *
- * Invoked by ConversationActivity and ConversationListActivity to display a
- * list of available labels and allow the user to add or remove them from the
- * current conversation. This class doesn't do any cursor manipulation.
- */
-public class ApplyRemoveFolderDialog extends AlertDialog
- implements OnCancelListener, DialogInterface.OnClickListener {
-
- // All the labels available on this account
- public static final String EXTRA_ALL_LABELS = "all-labels";
-
- // All the labels applied to the current conversation
- public static final String EXTRA_CURRENT_LABELS = "current-labels";
-
- // The following two extras are set on the result and they contain
- // all the labels that were added and removed by the user on this screen.
- public static final String EXTRA_ADDED_LABELS = "added-labels";
- public static final String EXTRA_REMOVED_LABELS = "removed-labels";
-
- private Context mContext;
- private ListView mListView;
-
- private FolderSelectorAdapter mAdapter;
-
- private CommitListener mCommitListener;
-
- private ConversationsLabelHandler mLabelHandler;
-
- public ApplyRemoveFolderDialog(Context context, CommitListener commitListener, Account account) {
- super(context);
- mContext = context;
- setTitle("change label");
- setOnCancelListener(this);
- setButton(DialogInterface.BUTTON_POSITIVE,
- mContext.getString(android.R.string.ok), this);
- setButton(DialogInterface.BUTTON_NEGATIVE,
- mContext.getString(android.R.string.cancel), this);
- setInverseBackgroundForced(true);
-
- LayoutInflater inflater = LayoutInflater.from(mContext);
- mListView = (ListView) inflater.inflate(R.layout.apply_remove_folder_dialog, null);
- setView(mListView, 0, 0, 0, 0);
- mCommitListener = commitListener;
- onPrepare(account);
- }
-
- /**
- * Invoked before showing the dialog. This method resets the change list and initializes
- * the list adapter to reflect the labels found on the current conversations.
- * @param account the account
- * @param currentLabels the labels on this/these conversations.
- */
- public void onPrepare(Account account) {
- Cursor folders = getContext().getContentResolver().query(Uri.parse(account.folderListUri),
- UIProvider.FOLDERS_PROJECTION, null, null, null);
- mAdapter = new FolderSelectorAdapter(getContext(), folders, new HashSet<String>());
-
- // Handle toggling of labels on the list item itself.
- // The clicks on the checkboxes are suppressed so that there is a consistent experience
- // regardless on whether or not the touch area was the checkbox, or elsewhere in the item.
- // This also allows the list items to be navigable and toggled using the trackball.
- mListView.setItemsCanFocus(true);
- mListView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- onSelectLabel(position);
- }
- });
-
- mListView.setAdapter(mAdapter);
- mLabelHandler = new ConversationsLabelHandler(mAdapter);
- }
-
- @SuppressWarnings("unchecked")
- private void onSelectLabel(int position) {
- // Update the UI
- mLabelHandler.update(mAdapter.getItem(position));
- }
-
- /////
- // implements DialogInterface.OnClickListener
- //
-
- @Override
- public void onCancel(DialogInterface dialog) {
- // Nothing to do
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // If the user clicked the OK button, apply the change list
- if (which == DialogInterface.BUTTON_POSITIVE) {
- mCommitListener.onCommit(mLabelHandler.getUris());
- }
- }
-}
diff --git a/src/com/android/mail/ui/ConversationsLabelHandler.java b/src/com/android/mail/ui/ConversationsLabelHandler.java
deleted file mode 100644
index 618a6de..0000000
--- a/src/com/android/mail/ui/ConversationsLabelHandler.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2012 Google Inc.
- * Licensed to 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.mail.ui;
-
-import com.android.mail.providers.Folder;
-
-import java.util.ArrayList;
-
-/**
- * Utility class that handles all changes to labels associated with a set of
- * conversations.
- *
- * @author mindyp@google.com
- */
-public class ConversationsLabelHandler {
-
- private final FolderSelectorAdapter mAdapter;
-
- private final ArrayList<String> mChangeList;
-
- public ConversationsLabelHandler(FolderSelectorAdapter adapter) {
- mAdapter = adapter;
- mChangeList = new ArrayList<String>();
- }
-
- /**
- * Call this to update the state of labels as a result of them being
- * selected / de-selected.
- *
- * @param row The item being updated.
- */
- public void update(FolderSelectorAdapter.FolderRow row) {
- // Update the UI
- final boolean add = !row.isPresent();
- final Folder folder = row.getFolder();
-
- row.setIsPresent(add);
- mAdapter.notifyDataSetChanged();
-
- // Update the label
-
- // Always add the change to our change list since this dialog could
- // be used to apply labels to several selected conversations and the
- // user might have to click on the same label (first + and then -) to
- // remove a label on the set. The previous implementation turned this
- // operation into a no-op but we can no longer do this now. The downside
- // is that we might emit label changes to the provider that cancel each
- // other out but the provider might be already smart enough not to emit
- // a no-op label change anyway.
- if (add) {
- mChangeList.add(folder.uri);
- } else {
- int pos = mChangeList.indexOf(folder.uri);
- if (pos >= 0) {
- mChangeList.remove(pos);
- }
- }
- }
-
- /**
- * Clear the state of the handler.
- */
- public void reset() {
- mChangeList.clear();
- }
-
- public String getUris() {
- StringBuilder folderUris = new StringBuilder();
- boolean first = true;
- for (String folderUri : mChangeList) {
- if (first) {
- first = false;
- } else {
- folderUris.append(',');
- }
- folderUris.append(folderUri);
- }
- return folderUris.toString();
- }
-}
diff --git a/src/com/android/mail/ui/FolderSelectorAdapter.java b/src/com/android/mail/ui/FolderSelectorAdapter.java
index 094677e..c0be05d 100644
--- a/src/com/android/mail/ui/FolderSelectorAdapter.java
+++ b/src/com/android/mail/ui/FolderSelectorAdapter.java
@@ -30,6 +30,8 @@
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.RadioButton;
import java.util.Collections;
import java.util.List;
@@ -78,15 +80,15 @@
private List<FolderRow> mFolderRows = Lists.newArrayList();
private LayoutInflater mInflater;
+ private int mLayout;
+ private boolean mSingle;
- private final Map<Integer, PaintDrawable> mColorBlockCache = Maps.newHashMap();
-
- private static int DEFAULT_LABEL_BACKGROUND_COLOR = android.R.color.white;
public FolderSelectorAdapter(Context context, Cursor folders,
- Set<String> initiallySelected) {
+ Set<String> initiallySelected, boolean single) {
mInflater = LayoutInflater.from(context);
-
+ mSingle = single;
+ mLayout = single? R.layout.single_folders_view : R.layout.multi_folders_view;
processLists(folders, initiallySelected);
}
@@ -118,27 +120,26 @@
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
- CheckBox checkBox;
+ CompoundButton checkBox = null;
View colorBlock;
if (view == null) {
- view = mInflater.inflate(R.layout.folders_view, parent, false);
- checkBox = (CheckBox) view.findViewById(R.id.checkbox);
- // Suppress the checkbox selection, and handle the toggling of the label
- // on the parent list item's click handler.
+ view = mInflater.inflate(mLayout, parent, false);
+ checkBox = (CompoundButton) view.findViewById(R.id.checkbox);
+ // Suppress the checkbox selection, and handle the toggling of the
+ // folder on the parent list item's click handler.
checkBox.setClickable(false);
- colorBlock = view.findViewById(R.id.color_block);
+ checkBox.setClickable(false);
view.setTag(R.id.checkbox, checkBox);
+ colorBlock = view.findViewById(R.id.color_block);
view.setTag(R.id.color_block, colorBlock);
} else {
- checkBox = (CheckBox) view.getTag(R.id.checkbox);
+ checkBox = (CompoundButton) view.getTag(R.id.checkbox);
colorBlock = (View) view.getTag(R.id.color_block);
}
FolderRow row = getItem(position);
- Folder folder = row.getFolder();
-
- checkBox.setText(folder.name);
+ checkBox.setText(row.getFolder().name);
checkBox.setChecked(row.isPresent());
return view;
diff --git a/src/com/android/mail/ui/FoldersSelectionDialog.java b/src/com/android/mail/ui/FoldersSelectionDialog.java
index a6488a8..561fec8 100644
--- a/src/com/android/mail/ui/FoldersSelectionDialog.java
+++ b/src/com/android/mail/ui/FoldersSelectionDialog.java
@@ -22,37 +22,27 @@
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.database.Cursor;
-import android.database.MatrixCursor;
import android.net.Uri;
-import android.provider.BaseColumns;
+import android.view.View;
+import android.widget.AdapterView;
import com.android.mail.R;
import com.android.mail.providers.Account;
import com.android.mail.providers.UIProvider;
+import com.android.mail.ui.FolderSelectorAdapter.FolderRow;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Set;
import java.util.Map.Entry;
public class FoldersSelectionDialog implements OnClickListener, OnMultiChoiceClickListener {
- private static final String CHECKED_COLUMN_NAME = "checked";
- // We only need _id because MatrixCursor insists
- private static final String[] FOLDER_DIALOG_PROJECTION = new String[] {
- BaseColumns._ID, UIProvider.FolderColumns.URI, UIProvider.FolderColumns.NAME,
- CHECKED_COLUMN_NAME
- };
- private static final int FOLDERS_CURSOR_ID = 0;
- private static final int FOLDERS_CURSOR_URI = 1;
- private static final int FOLDERS_CURSOR_NAME = 2;
- private static final int FOLDERS_CURSOR_CHECKED = 3;
-
- private int mCheckedItem;
private AlertDialog mDialog;
private CommitListener mCommitListener;
private HashMap<String, Boolean> mCheckedState;
- private MatrixCursor mFolderDialogCursor;
private boolean mSingle = false;
+ private FolderSelectorAdapter mAdapter;
public interface CommitListener {
public void onCommit(String uris);
@@ -63,50 +53,55 @@
mCommitListener = commitListener;
// Mapping of a folder's uri to its checked state
mCheckedState = new HashMap<String, Boolean>();
-
- if (!account.supportsCapability(UIProvider.AccountCapabilities.MULTIPLE_FOLDERS_PER_CONV)) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle("Change folders");
- builder.setPositiveButton(R.string.ok, this);
- builder.setNegativeButton(R.string.cancel, this);
-
- // Get all of our folders
- // TODO: Should only be folders that allow messages to be moved
- // there!!
- Cursor foldersCursor = context.getContentResolver().query(
- Uri.parse(account.folderListUri), UIProvider.FOLDERS_PROJECTION, null, null,
- null);
- // Get the id, name, and a placeholder for check information
- Object[] columnValues = new Object[FOLDER_DIALOG_PROJECTION.length];
- mFolderDialogCursor = new MatrixCursor(FOLDER_DIALOG_PROJECTION);
- int i = 0;
- while (foldersCursor.moveToNext()) {
- int flags = foldersCursor.getInt(UIProvider.FOLDER_CAPABILITIES_COLUMN);
- if ((flags & UIProvider.FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES) == 0) {
- continue;
- }
- String uri = foldersCursor.getString(UIProvider.FOLDER_URI_COLUMN);
- columnValues[FOLDERS_CURSOR_ID] = i++;
- columnValues[FOLDERS_CURSOR_URI] = uri;
- columnValues[FOLDERS_CURSOR_NAME] = foldersCursor
- .getString(UIProvider.FOLDER_NAME_COLUMN);
- columnValues[FOLDERS_CURSOR_CHECKED] = 1; // 0 = unchecked
- mFolderDialogCursor.addRow(columnValues);
- mCheckedState.put(uri, true);
- }
- foldersCursor.close();
- mSingle = true;
- builder.setSingleChoiceItems(mFolderDialogCursor, mCheckedItem,
- UIProvider.FolderColumns.NAME, this);
- mDialog = builder.create();
- } else {
- mSingle = false;
- mDialog = new ApplyRemoveFolderDialog(context, commitListener, account);
- }
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle("Change folders");
+ builder.setPositiveButton(R.string.ok, this);
+ builder.setNegativeButton(R.string.cancel, this);
+ mSingle = !account
+ .supportsCapability(UIProvider.AccountCapabilities.MULTIPLE_FOLDERS_PER_CONV);
+ // TODO: (mindyp) make async
+ Cursor foldersCursor = context.getContentResolver().query(Uri.parse(account.folderListUri),
+ UIProvider.FOLDERS_PROJECTION, null, null, null);
+ mAdapter = new FolderSelectorAdapter(context, foldersCursor, new HashSet<String>(), mSingle);
+ builder.setAdapter(mAdapter, this);
+ mDialog = builder.create();
}
public void show() {
mDialog.show();
+ mDialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ update(mAdapter.getItem(position));
+ }
+ });
+ }
+
+ /**
+ * Call this to update the state of labels as a result of them being
+ * selected / de-selected.
+ *
+ * @param row The item being updated.
+ */
+ public void update(FolderSelectorAdapter.FolderRow row) {
+ // Update the UI
+ boolean add = !row.isPresent();
+ if (mSingle) {
+ if (!add) {
+ // This would remove the check on a single radio button, so just
+ // return.
+ return;
+ }
+ // Clear any other checked items.
+ mAdapter.getCount();
+ for (int i = 0; i < mAdapter.getCount(); i++) {
+ mAdapter.getItem(i).setIsPresent(false);
+ }
+ mCheckedState.clear();
+ }
+ row.setIsPresent(add);
+ mAdapter.notifyDataSetChanged();
+ mCheckedState.put(row.getFolder().uri, add);
}
@Override
@@ -144,13 +139,13 @@
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- mFolderDialogCursor.moveToPosition(which);
+ FolderRow row = mAdapter.getItem(which);
if (mSingle) {
// Clear any other checked items.
mCheckedState.clear();
isChecked = true;
}
- mCheckedState.put(mFolderDialogCursor.getString(FOLDERS_CURSOR_URI), isChecked);
+ mCheckedState.put(row.getFolder().uri, isChecked);
mDialog.getListView().setItemChecked(which, false);
}
}