diff --git a/TODO b/TODO index f120a53..598ef8f 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,6 @@ Fixes: -- Figure out how to highlight email backgrounds when selected in list +- Fix clicking pictures to select emails +- Fix tick over selected emails - Refine view email page -- Delete/read/unread/move actions that don't break everything - Improve network status page diff --git a/app/src/main/java/i2p/bote/android/EmailListAdapter.java b/app/src/main/java/i2p/bote/android/EmailListAdapter.java index 27aebfd..d55b157 100644 --- a/app/src/main/java/i2p/bote/android/EmailListAdapter.java +++ b/app/src/main/java/i2p/bote/android/EmailListAdapter.java @@ -1,5 +1,15 @@ package i2p.bote.android; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Typeface; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + import java.io.IOException; import java.security.GeneralSecurityException; import java.text.DateFormat; @@ -11,31 +21,19 @@ import javax.mail.Part; import i2p.bote.android.util.BoteHelper; import i2p.bote.email.Email; import i2p.bote.fileencryption.PasswordException; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Typeface; -import android.util.SparseBooleanArray; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.TextView; public class EmailListAdapter extends ArrayAdapter { private final LayoutInflater mInflater; - private SparseBooleanArray mSelectedEmails; private EmailSelector mSelector; private boolean mIsOutbox; public interface EmailSelector { - public void select(int position); + public void select(View view); } public EmailListAdapter(Context context, EmailSelector selector, boolean isOutbox) { super(context, android.R.layout.simple_list_item_2); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mSelectedEmails = new SparseBooleanArray(); mSelector = selector; mIsOutbox = isOutbox; } @@ -51,7 +49,7 @@ public class EmailListAdapter extends ArrayAdapter { @Override public View getView(int position, View convertView, ViewGroup parent) { - View v = mInflater.inflate(R.layout.listitem_email, parent, false); + final View v = mInflater.inflate(R.layout.listitem_email, parent, false); final Email email = getItem(position); ImageView picture = (ImageView) v.findViewById(R.id.contact_picture); @@ -62,13 +60,14 @@ public class EmailListAdapter extends ArrayAdapter { picture.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { - mSelector.select(getPosition(email)); + mSelector.select(v); } }); - if (mSelectedEmails.get(position)) { - ((ImageView) v.findViewById(R.id.email_selected)).setVisibility(View.VISIBLE); - } + // TODO fix + //if (mSelectedEmails.get(position)) { + // ((ImageView) v.findViewById(R.id.email_selected)).setVisibility(View.VISIBLE); + //} try { String fromAddress = email.getOneFromAddress(); @@ -127,29 +126,4 @@ public class EmailListAdapter extends ArrayAdapter { return v; } - - public void toggleSelection(int position) { - selectView(position, !mSelectedEmails.get(position)); - } - - public void removeSelection() { - mSelectedEmails = new SparseBooleanArray(); - notifyDataSetChanged(); - } - - public void selectView(int position, boolean value) { - if (value) - mSelectedEmails.put(position, value); - else - mSelectedEmails.delete(position); - notifyDataSetChanged(); - } - - public int getSelectedCount() { - return mSelectedEmails.size(); - } - - public SparseBooleanArray getSelectedIds() { - return mSelectedEmails; - } } diff --git a/app/src/main/java/i2p/bote/android/EmailListFragment.java b/app/src/main/java/i2p/bote/android/EmailListFragment.java index e7fb1c2..00d2af4 100644 --- a/app/src/main/java/i2p/bote/android/EmailListFragment.java +++ b/app/src/main/java/i2p/bote/android/EmailListFragment.java @@ -1,26 +1,5 @@ package i2p.bote.android; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.util.List; - -import javax.mail.Flags.Flag; -import javax.mail.MessagingException; - -import uk.co.senab.actionbarpulltorefresh.extras.actionbarcompat.PullToRefreshLayout; -import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh; -import uk.co.senab.actionbarpulltorefresh.library.Options; -import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener; -import net.i2p.I2PAppContext; -import net.i2p.util.Log; -import i2p.bote.I2PBote; -import i2p.bote.android.util.BetterAsyncTaskLoader; -import i2p.bote.android.util.BoteHelper; -import i2p.bote.android.util.MoveToDialogFragment; -import i2p.bote.email.Email; -import i2p.bote.fileencryption.PasswordException; -import i2p.bote.folder.EmailFolder; -import i2p.bote.folder.FolderListener; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; @@ -28,6 +7,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.ListFragment; @@ -43,11 +23,34 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; +import net.i2p.I2PAppContext; +import net.i2p.util.Log; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.List; + +import javax.mail.Flags.Flag; +import javax.mail.MessagingException; + +import i2p.bote.I2PBote; +import i2p.bote.android.util.BetterAsyncTaskLoader; +import i2p.bote.android.util.BoteHelper; +import i2p.bote.android.util.MoveToDialogFragment; +import i2p.bote.android.util.MultiSelectionUtil; +import i2p.bote.email.Email; +import i2p.bote.fileencryption.PasswordException; +import i2p.bote.folder.EmailFolder; +import i2p.bote.folder.FolderListener; +import uk.co.senab.actionbarpulltorefresh.extras.actionbarcompat.PullToRefreshLayout; +import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh; +import uk.co.senab.actionbarpulltorefresh.library.Options; +import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener; + public class EmailListFragment extends ListFragment implements LoaderManager.LoaderCallbacks>, MoveToDialogFragment.MoveToDialogListener, @@ -63,7 +66,10 @@ public class EmailListFragment extends ListFragment implements private EmailListAdapter mAdapter; private EmailFolder mFolder; - private ActionMode mMode; + + // The Controller which provides CHOICE_MODE_MULTIPLE_MODAL-like functionality + private MultiSelectionUtil.Controller mMultiSelectController; + private ModalChoiceListener mModalChoiceListener; private EditText mPasswordInput; private TextView mPasswordError; @@ -103,7 +109,7 @@ public class EmailListFragment extends ListFragment implements @Override public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view,savedInstanceState); + super.onViewCreated(view, savedInstanceState); String folderName = getArguments().getString(FOLDER_NAME); mFolder = BoteHelper.getMailFolder(folderName); @@ -138,6 +144,7 @@ public class EmailListFragment extends ListFragment implements mNumIncompleteEmails = new TextView(getActivity()); mNumIncompleteEmails.setText(getResources().getString(R.string.incomplete_emails, numIncompleteEmails)); + mNumIncompleteEmails.setPadding(16, 5, 16, 5); getListView().addHeaderView(mNumIncompleteEmails); } } @@ -151,16 +158,15 @@ public class EmailListFragment extends ListFragment implements setListAdapter(mAdapter); - // Set up CAB - mMode = null; - getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView parent, View view, - int position, long id) { - onListItemSelect(position); - return true; - } - }); + // Attach a MultiSelectionUtil.Controller to the ListView, giving it an instance of + // ModalChoiceListener (see below) + mModalChoiceListener = new ModalChoiceListener(); + mMultiSelectController = MultiSelectionUtil + .attachMultiSelectionController(getListView(), (ActionBarActivity) getActivity(), + mModalChoiceListener); + + // Allow the Controller to restore itself + mMultiSelectController.restoreInstanceState(savedInstanceState); if (mFolder == null) { setEmptyText(getResources().getString( @@ -267,6 +273,16 @@ public class EmailListFragment extends ListFragment implements getLoaderManager().initLoader(EMAIL_LIST_LOADER, null, this); } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + // Allow the Controller to save it's instance state so that any checked items are + // stored + if (mMultiSelectController != null) + mMultiSelectController.saveInstanceState(outState); + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.email_list, menu); @@ -275,94 +291,97 @@ public class EmailListFragment extends ListFragment implements @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.action_new_email: - Intent nei = new Intent(getActivity(), NewEmailActivity.class); - startActivity(nei); - return true; + case R.id.action_new_email: + Intent nei = new Intent(getActivity(), NewEmailActivity.class); + startActivity(nei); + return true; - default: - return super.onOptionsItemSelected(item); + default: + return super.onOptionsItemSelected(item); } } @Override public void onListItemClick(ListView parent, View view, int pos, long id) { super.onListItemClick(parent, view, pos, id); - int position = mNumIncompleteEmails == null ? pos : pos - 1; - if (position < 0) return; - if (mMode == null) { - mCallback.onEmailSelected( - mFolder.getName(), mAdapter.getItem(position).getMessageID()); - } else - onListItemSelect(position); + final Email email = (Email) getListView().getItemAtPosition(pos); + if (email != null) + mCallback.onEmailSelected(mFolder.getName(), email.getMessageID()); } - private void onListItemSelect(int position) { - mAdapter.toggleSelection(position); - boolean hasCheckedElement = mAdapter.getSelectedCount() > 0; - - if (hasCheckedElement && mMode == null) { - boolean unread = mAdapter.getItem(position).isUnread(); - mMode = ((ActionBarActivity) getActivity()).startSupportActionMode(new ModeCallback(unread)); - } else if (!hasCheckedElement && mMode != null) { - mMode.finish(); - } - - if (mMode != null) - mMode.setTitle(getResources().getString( - R.string.items_selected, mAdapter.getSelectedCount())); - } - - private final class ModeCallback implements ActionMode.Callback { + private class ModalChoiceListener implements MultiSelectionUtil.MultiChoiceModeListener { private boolean areUnread; - public ModeCallback(boolean unread) { - super(); - this.areUnread = unread; + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { + final ListView listView = getListView(); + int numChecked = 0; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + final SparseBooleanArray items = listView.getCheckedItemPositions(); + for (int i = 0; i < items.size(); i++) + if (items.valueAt(i)) + numChecked++; + } else + numChecked = listView.getCheckedItemCount(); + + mode.setTitle(getResources().getString(R.string.items_selected, numChecked)); + + if (checked && numChecked == 1) { // This is the first checked item + Email email = (Email) listView.getItemAtPosition(position); + areUnread = email.isUnread(); + mode.invalidate(); + } } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + final ListView listView = getListView(); // Respond to clicks on the actions in the CAB switch (item.getItemId()) { - case R.id.action_delete: - SparseBooleanArray toDelete = mAdapter.getSelectedIds(); - for (int i = (toDelete.size() - 1); i >= 0; i--) { - if (toDelete.valueAt(i)) { - Email email = mAdapter.getItem(toDelete.keyAt(i)); - // The Loader will update mAdapter - I2PBote.getInstance().deleteEmail(mFolder, email.getMessageID()); - } - } - mode.finish(); - return true; - case R.id.action_mark_read: - case R.id.action_mark_unread: - SparseBooleanArray selected = mAdapter.getSelectedIds(); - for (int i = (selected.size() - 1); i >= 0; i--) { - if (selected.valueAt(i)) { - Email email = mAdapter.getItem(selected.keyAt(i)); - try { + case R.id.action_delete: + SparseBooleanArray toDelete = listView.getCheckedItemPositions(); + if (toDelete.size() == 0) + return false; + + for (int i = (toDelete.size() - 1); i >= 0; i--) { + if (toDelete.valueAt(i)) { + Email email = (Email) listView.getItemAtPosition(toDelete.keyAt(i)); // The Loader will update mAdapter - mFolder.setNew(email, !areUnread); - } catch (PasswordException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (GeneralSecurityException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + I2PBote.getInstance().deleteEmail(mFolder, email.getMessageID()); } } - } - areUnread = !areUnread; - mMode.invalidate(); - return true; - case R.id.action_move_to: - DialogFragment f = MoveToDialogFragment.newInstance(mFolder); - f.show(getFragmentManager(), "moveTo"); - return true; - default: - return false; + mode.finish(); + return true; + + case R.id.action_mark_read: + case R.id.action_mark_unread: + SparseBooleanArray selected = listView.getCheckedItemPositions(); + for (int i = (selected.size() - 1); i >= 0; i--) { + if (selected.valueAt(i)) { + Email email = (Email) listView.getItemAtPosition(selected.keyAt(i)); + try { + // The Loader will update mAdapter + mFolder.setNew(email, !areUnread); + } catch (PasswordException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (GeneralSecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + areUnread = !areUnread; + mode.invalidate(); + return true; + + case R.id.action_move_to: + DialogFragment f = MoveToDialogFragment.newInstance(mFolder); + f.show(getFragmentManager(), "moveTo"); + return true; + + default: + return false; } } @@ -382,16 +401,6 @@ public class EmailListFragment extends ListFragment implements return true; } - @Override - public void onDestroyActionMode(ActionMode mode) { - // Here you can make any necessary updates to the activity when - // the CAB is removed. - mAdapter.removeSelection(); - - if (mode == mMode) - mMode = null; - } - @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { // Here you can perform updates to the CAB due to @@ -402,19 +411,24 @@ public class EmailListFragment extends ListFragment implements } return true; } + + @Override + public void onDestroyActionMode(ActionMode mode) { + } } // Called by EmailListActivity.onFolderSelected() public void onFolderSelected(EmailFolder newFolder) { - SparseBooleanArray toMove = mAdapter.getSelectedIds(); + final ListView listView = getListView(); + SparseBooleanArray toMove = listView.getCheckedItemPositions(); for (int i = (toMove.size() - 1); i >= 0; i--) { if (toMove.valueAt(i)) { - Email email = mAdapter.getItem(toMove.keyAt(i)); + Email email = (Email) listView.getItemAtPosition(toMove.keyAt(i)); mFolder.move(email, newFolder); } } - mMode.finish(); + mMultiSelectController.finish(); } // LoaderManager.LoaderCallbacks> @@ -473,7 +487,7 @@ public class EmailListFragment extends ListFragment implements } public void onLoadFinished(Loader> loader, - List data) { + List data) { // Clear recent flags for (Email email : data) try { @@ -508,8 +522,12 @@ public class EmailListFragment extends ListFragment implements // EmailListAdapter.EmailSelector - public void select(int position) { - onListItemSelect(position); + public void select(View view) { + // TODO temporarily disabled while broken, need to fix + //final ListView listView = getListView(); + //final int position = listView.getPositionForView(view); + //listView.setItemChecked(position, !listView.isItemChecked(position)); + //view.performLongClick(); } // OnRefreshListener @@ -527,7 +545,8 @@ public class EmailListFragment extends ListFragment implements while (I2PBote.getInstance().isCheckingForMail()) { try { Thread.sleep(100); - } catch (InterruptedException e) {} + } catch (InterruptedException e) { + } } return null; } diff --git a/app/src/main/java/i2p/bote/android/util/MultiSelectionUtil.java b/app/src/main/java/i2p/bote/android/util/MultiSelectionUtil.java new file mode 100644 index 0000000..d02504a --- /dev/null +++ b/app/src/main/java/i2p/bote/android/util/MultiSelectionUtil.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2013 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 i2p.bote.android.util; + +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.view.ActionMode; +import android.util.Pair; +import android.util.SparseBooleanArray; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AbsListView; +import android.widget.Adapter; +import android.widget.AdapterView; +import android.widget.ListView; + +import java.util.HashSet; + +/** + * Utilities for handling multiple selection in list views. Contains functionality similar to {@link + * AbsListView#CHOICE_MODE_MULTIPLE_MODAL} which works with {@link ActionBarActivity} and + * backward-compatible action bars. + */ +public class MultiSelectionUtil { + + /** + * Attach a Controller to the given listView, activity + * and listener. + * + * @param listView ListView which displays {@link android.widget.Checkable} items. + * @param activity Activity which contains the ListView. + * @param listener Listener that will manage the selection mode. + * @return the attached Controller instance. + */ + public static Controller attachMultiSelectionController(final ListView listView, + final ActionBarActivity activity, final MultiChoiceModeListener listener) { + return new Controller(listView, activity, listener); + } + + /** + * Class which provides functionality similar to {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL} + * for the {@link ListView} provided to it. A + * {@link android.widget.AdapterView.OnItemLongClickListener} is set on the ListView so that + * when an item is long-clicked an ActionBarCompat Action Mode is started. Once started, a + * {@link android.widget.AdapterView.OnItemClickListener} is set so that an item click toggles + * that item's checked state. + */ + public static class Controller { + + private final ListView mListView; + private final ActionBarActivity mActivity; + private final MultiChoiceModeListener mListener; + private final Callbacks mCallbacks; + + // Current Action Mode (if there is one) + private ActionMode mActionMode; + + // Keeps record of any items that should be checked on the next action mode creation + private HashSet> mItemsToCheck; + + // Reference to the replace OnItemClickListener (so it can be restored later) + private AdapterView.OnItemClickListener mOldItemClickListener; + + private final Runnable mSetChoiceModeNoneRunnable = new Runnable() { + @Override + public void run() { + mListView.setChoiceMode(AbsListView.CHOICE_MODE_NONE); + } + }; + + private Controller(ListView listView, ActionBarActivity activity, + MultiChoiceModeListener listener) { + mListView = listView; + mActivity = activity; + mListener = listener; + mCallbacks = new Callbacks(); + + // We set ourselves as the OnItemLongClickListener so we know when to start + // an Action Mode + listView.setOnItemLongClickListener(mCallbacks); + } + + /** + * Finish the current Action Mode (if there is one). + */ + public void finish() { + if (mActionMode != null) { + mActionMode.finish(); + } + } + + /** + * This method should be called from your {@link ActionBarActivity} or + * {@link android.support.v4.app.Fragment Fragment} to allow the controller to restore any + * instance state. + * + * @param savedInstanceState - The state passed to your Activity or Fragment. + */ + public void restoreInstanceState(Bundle savedInstanceState) { + if (savedInstanceState != null) { + long[] checkedIds = savedInstanceState.getLongArray(getStateKey()); + if (checkedIds != null && checkedIds.length > 0) { + HashSet idsToCheckOnRestore = new HashSet(); + for (long id : checkedIds) { + idsToCheckOnRestore.add(id); + } + tryRestoreInstanceState(idsToCheckOnRestore); + } + } + } + + /** + * This method should be called from + * {@link ActionBarActivity#onSaveInstanceState(android.os.Bundle)} or + * {@link android.support.v4.app.Fragment#onSaveInstanceState(android.os.Bundle) + * Fragment.onSaveInstanceState(Bundle)} to allow the controller to save its instance + * state. + * + * @param outState - The state passed to your Activity or Fragment. + */ + public void saveInstanceState(Bundle outState) { + if (mActionMode != null && mListView.getAdapter().hasStableIds()) { + outState.putLongArray(getStateKey(), mListView.getCheckedItemIds()); + } + } + + // Internal utility methods + + private String getStateKey() { + return MultiSelectionUtil.class.getSimpleName() + "_" + mListView.getId(); + } + + private void tryRestoreInstanceState(HashSet idsToCheckOnRestore) { + if (idsToCheckOnRestore == null || mListView.getAdapter() == null) { + return; + } + + boolean idsFound = false; + Adapter adapter = mListView.getAdapter(); + for (int pos = adapter.getCount() - 1; pos >= 0; pos--) { + if (idsToCheckOnRestore.contains(adapter.getItemId(pos))) { + idsFound = true; + if (mItemsToCheck == null) { + mItemsToCheck = new HashSet>(); + } + mItemsToCheck.add(new Pair(pos, adapter.getItemId(pos))); + } + } + + if (idsFound) { + // We found some IDs that were checked. Let's now restore the multi-selection + // state. + mActionMode = mActivity.startSupportActionMode(mCallbacks); + } + } + + /** + * This class encapsulates all of the callbacks necessary for the controller class. + */ + final class Callbacks implements ActionMode.Callback, AdapterView.OnItemClickListener, + AdapterView.OnItemLongClickListener { + + @Override + public final boolean onCreateActionMode(ActionMode actionMode, Menu menu) { + if (mListener.onCreateActionMode(actionMode, menu)) { + mActionMode = actionMode; + // Keep a reference to the existing OnItemClickListener so we can restore it + mOldItemClickListener = mListView.getOnItemClickListener(); + + // Set-up the ListView to emulate CHOICE_MODE_MULTIPLE_MODAL + mListView.setOnItemClickListener(this); + mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE); + mListView.removeCallbacks(mSetChoiceModeNoneRunnable); + + // If there are some items to check, do it now + if (mItemsToCheck != null) { + for (Pair posAndId : mItemsToCheck) { + mListView.setItemChecked(posAndId.first, true); + // Notify the listener that the item has been checked + mListener.onItemCheckedStateChanged(mActionMode, posAndId.first, + posAndId.second, true); + } + } + return true; + } + return false; + } + + @Override + public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { + // Proxy listener + return mListener.onPrepareActionMode(actionMode, menu); + } + + @Override + public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { + // Proxy listener + return mListener.onActionItemClicked(actionMode, menuItem); + } + + @Override + public void onDestroyActionMode(ActionMode actionMode) { + mListener.onDestroyActionMode(actionMode); + + // Clear all the checked items + SparseBooleanArray checkedPositions = mListView.getCheckedItemPositions(); + if (checkedPositions != null) { + for (int i = 0; i < checkedPositions.size(); i++) { + mListView.setItemChecked(checkedPositions.keyAt(i), false); + } + } + + // Restore the original onItemClickListener + mListView.setOnItemClickListener(mOldItemClickListener); + + // Clear the Action Mode + mActionMode = null; + + // Reset the ListView's Choice Mode + mListView.post(mSetChoiceModeNoneRunnable); + } + + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long id) { + // Check to see what the new checked state is, and then notify the listener + final boolean checked = mListView.isItemChecked(position); + mListener.onItemCheckedStateChanged(mActionMode, position, id, checked); + + boolean hasCheckedItem = checked; + + // Check to see if we have any checked items + if (!hasCheckedItem) { + SparseBooleanArray checkedItemPositions = mListView.getCheckedItemPositions(); + if (checkedItemPositions != null) { + // Iterate through the SparseBooleanArray to see if there is a checked item + int i = 0; + while (!hasCheckedItem && i < checkedItemPositions.size()) { + hasCheckedItem = checkedItemPositions.valueAt(i++); + } + } + } + + // If we don't have any checked items, finish the action mode + if (!hasCheckedItem) { + mActionMode.finish(); + } + } + + @Override + public boolean onItemLongClick(AdapterView adapterView, View view, int position, + long id) { + // If we already have an action mode started return false + // (onItemClick will be called anyway) + if (mActionMode != null) { + return false; + } + + mItemsToCheck = new HashSet>(); + mItemsToCheck.add(new Pair(position, id)); + mActionMode = mActivity.startSupportActionMode(this); + return true; + } + } + } + + /** + * @see android.widget.AbsListView.MultiChoiceModeListener + */ + public static interface MultiChoiceModeListener extends ActionMode.Callback { + + /** + * @see android.widget.AbsListView.MultiChoiceModeListener#onItemCheckedStateChanged( + *android.view.ActionMode, int, long, boolean) + */ + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, + boolean checked); + } +} diff --git a/app/src/main/res/drawable/listitem_checked.xml b/app/src/main/res/drawable/listitem_checked.xml new file mode 100644 index 0000000..18b16eb --- /dev/null +++ b/app/src/main/res/drawable/listitem_checked.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/listitem_email.xml b/app/src/main/res/layout/listitem_email.xml index 51c46a5..494e643 100644 --- a/app/src/main/res/layout/listitem_email.xml +++ b/app/src/main/res/layout/listitem_email.xml @@ -2,6 +2,7 @@ + + #9033B5E5 #c31756 #111 #f00