diff --git a/app/ui/src/main/java/com/fsck/k9/activity/ChooseFolder.java b/app/ui/src/main/java/com/fsck/k9/activity/ChooseFolder.java index 2baf85439..213989c78 100644 --- a/app/ui/src/main/java/com/fsck/k9/activity/ChooseFolder.java +++ b/app/ui/src/main/java/com/fsck/k9/activity/ChooseFolder.java @@ -9,19 +9,27 @@ import java.util.List; import android.content.Intent; import android.os.Bundle; import android.os.Handler; +import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.widget.AdapterView; -import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; import android.widget.Filter; +import android.widget.Filterable; +import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.ListView; import android.widget.SearchView; +import android.widget.TextView; import com.fsck.k9.Account; import com.fsck.k9.Account.FolderMode; +import com.fsck.k9.K9; import com.fsck.k9.Preferences; +import com.fsck.k9.activity.FolderListFilter.FolderAdapter; import com.fsck.k9.controller.MessageReference; import com.fsck.k9.ui.R; import com.fsck.k9.controller.MessagingController; @@ -29,6 +37,10 @@ import com.fsck.k9.controller.MessagingListener; import com.fsck.k9.controller.SimpleMessagingListener; import com.fsck.k9.mail.Folder; import com.fsck.k9.mailstore.LocalFolder; +import com.fsck.k9.ui.folders.FolderIconProvider; +import timber.log.Timber; + +import static java.util.Collections.emptyList; public class ChooseFolder extends K9ListActivity { @@ -42,14 +54,14 @@ public class ChooseFolder extends K9ListActivity { public static final String RESULT_FOLDER_DISPLAY_NAME = "folderDisplayName"; - String currentFolder; - String mSelectFolder; - Account mAccount; - MessageReference mMessageReference; - ArrayAdapter mAdapter; + private String currentFolder; + private String mSelectFolder; + private Account mAccount; + private MessageReference mMessageReference; + private FolderListAdapter mAdapter; private ChooseFolderHandler mHandler = new ChooseFolderHandler(); - boolean mHideCurrentFolder = true; - boolean mShowDisplayableOnly = false; + private boolean mHideCurrentFolder = true; + private boolean mShowDisplayableOnly = false; /** * What folders to display.
@@ -59,13 +71,6 @@ public class ChooseFolder extends K9ListActivity { */ private Account.FolderMode mMode; - /** - * Current filter used by our ArrayAdapter.
- * Created on the fly and invalidated if a new - * set of folders is chosen via {@link #onOptionsItemSelected(MenuItem)} - */ - private FolderListFilter mMyFilter = null; - @Override public void onCreate(Bundle savedInstanceState) { @@ -95,18 +100,7 @@ public class ChooseFolder extends K9ListActivity { if (currentFolder == null) currentFolder = ""; - mAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1) { - private Filter myFilter = null; - - @Override - public Filter getFilter() { - if (myFilter == null) { - myFilter = new FolderListFilter<>(this); - } - return myFilter; - } - }; - + mAdapter = new FolderListAdapter(); setListAdapter(mAdapter); mMode = mAccount.getFolderTargetMode(); @@ -115,7 +109,7 @@ public class ChooseFolder extends K9ListActivity { this.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - FolderDisplayData folder = mAdapter.getItem(position); + FolderInfoHolder folder = mAdapter.getItem(position); if (folder == null) { throw new AssertionError("Couldn't get item at adapter position " + position); } @@ -225,10 +219,6 @@ public class ChooseFolder extends K9ListActivity { private void setDisplayMode(FolderMode aMode) { mMode = aMode; - // invalidate the current filter as it is working on an inval - if (mMyFilter != null) { - mMyFilter.invalidate(); - } //re-populate the list MessagingController.getInstance(getApplication()).listFolders(mAccount, false, mListener); } @@ -264,8 +254,8 @@ public class ChooseFolder extends K9ListActivity { } Account.FolderMode aMode = mMode; - List newFolders = new ArrayList<>(); - List topFolders = new ArrayList<>(); + List newFolders = new ArrayList<>(); + List topFolders = new ArrayList<>(); for (LocalFolder folder : folders) { String serverId = folder.getServerId(); @@ -289,10 +279,7 @@ public class ChooseFolder extends K9ListActivity { continue; } - long id = folder.getDatabaseId(); - String name = folder.getName(); - String displayName = buildDisplayName(account, serverId, name); - FolderDisplayData folderDisplayData = new FolderDisplayData(id, serverId, displayName); + FolderInfoHolder folderDisplayData = new FolderInfoHolder(folder, account); if (folder.isInTopGroup()) { topFolders.add(folderDisplayData); @@ -301,9 +288,9 @@ public class ChooseFolder extends K9ListActivity { } } - final Comparator comparator = new Comparator() { + final Comparator comparator = new Comparator() { @Override - public int compare(FolderDisplayData lhs, FolderDisplayData rhs) { + public int compare(FolderInfoHolder lhs, FolderInfoHolder rhs) { int result = lhs.displayName.compareToIgnoreCase(rhs.displayName); return (result != 0) ? result : lhs.displayName.compareTo(rhs.displayName); } @@ -312,7 +299,7 @@ public class ChooseFolder extends K9ListActivity { Collections.sort(topFolders, comparator); Collections.sort(newFolders, comparator); - final List folderList = new ArrayList<>(newFolders.size() + topFolders.size()); + final List folderList = new ArrayList<>(newFolders.size() + topFolders.size()); folderList.addAll(topFolders); folderList.addAll(newFolders); @@ -325,7 +312,7 @@ public class ChooseFolder extends K9ListActivity { */ try { int position = 0; - for (FolderDisplayData folder : folderList) { + for (FolderInfoHolder folder : folderList) { if (mSelectFolder != null) { /* * Never select EXTRA_CUR_FOLDER (mFolder) if EXTRA_SEL_FOLDER @@ -347,16 +334,7 @@ public class ChooseFolder extends K9ListActivity { @Override public void run() { // Now we're in the UI-thread, we can safely change the contents of the adapter. - mAdapter.clear(); - mAdapter.addAll(folderList); - mAdapter.notifyDataSetChanged(); - - /* - * Only enable the text filter after the list has been - * populated to avoid possible race conditions because our - * FolderListFilter isn't really thread-safe. - */ - getListView().setTextFilterEnabled(true); + mAdapter.setFolders(folderList); } }); } @@ -367,29 +345,117 @@ public class ChooseFolder extends K9ListActivity { } }; - private String buildDisplayName(Account account, String serverId, String name) { - if (account.getInboxFolder().equals(serverId)) { - return getString(R.string.special_mailbox_name_inbox); - } else { - return name; - } - } + class FolderListAdapter extends BaseAdapter implements Filterable, FolderAdapter { + private List mFolders = emptyList(); + private List mFilteredFolders = emptyList(); + private Filter mFilter = new FolderListFilter(this, mFolders); + private FolderIconProvider folderIconProvider = new FolderIconProvider(getTheme()); + private CharSequence filterText; - - static class FolderDisplayData { - final long id; - final String serverId; - final String displayName; - - FolderDisplayData(long id, String serverId, String displayName) { - this.id = id; - this.serverId = serverId; - this.displayName = displayName; + public FolderInfoHolder getItem(long position) { + return getItem((int)position); } @Override - public String toString() { - return displayName; + public FolderInfoHolder getItem(int position) { + return mFilteredFolders.get(position); + } + + @Override + public long getItemId(int position) { + return mFilteredFolders.get(position).folder.getDatabaseId(); + } + + @Override + public int getCount() { + return mFilteredFolders.size(); + } + + @Override + public boolean isEnabled(int item) { + return true; + } + + @Override + public boolean areAllItemsEnabled() { + return true; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (position <= getCount()) { + return getItemView(position, convertView, parent); + } else { + Timber.e("getView with illegal position=%d called! count is only %d", position, getCount()); + return null; + } + } + + public View getItemView(int itemPosition, View convertView, ViewGroup parent) { + FolderInfoHolder folder = getItem(itemPosition); + View view; + if (convertView != null) { + view = convertView; + } else { + view = View.inflate(ChooseFolder.this, R.layout.choose_folder_list_item, null); + } + + FolderViewHolder holder = (FolderViewHolder) view.getTag(); + + if (holder == null) { + holder = new FolderViewHolder(); + holder.folderName = view.findViewById(R.id.folder_name); + holder.folderIcon = view.findViewById(R.id.folder_icon); + holder.folderListItemLayout = view.findViewById(R.id.folder_list_item_layout); + + view.setTag(holder); + } + + if (folder == null) { + return view; + } + + holder.folderName.setText(folder.displayName); + holder.folderIcon.setImageResource(folderIconProvider.getFolderIcon(folder.folder.getType())); + + if (K9.isWrapFolderNames()) { + holder.folderName.setEllipsize(null); + holder.folderName.setSingleLine(false); + } + else { + holder.folderName.setEllipsize(TextUtils.TruncateAt.START); + holder.folderName.setSingleLine(true); + } + + return view; + } + + @Override + public boolean hasStableIds() { + return true; + } + + public Filter getFilter() { + return mFilter; + } + + @Override + public void setFilteredFolders(CharSequence filterText, List folders) { + this.filterText = filterText; + mFilteredFolders = folders; + notifyDataSetChanged(); + } + + void setFolders(List folders) { + mFolders = folders; + mFilter = new FolderListFilter(this, folders); + mFilter.filter(filterText); } } + + static class FolderViewHolder { + TextView folderName; + ImageView folderIcon; + LinearLayout folderListItemLayout; + } } diff --git a/app/ui/src/main/java/com/fsck/k9/activity/FolderListFilter.java b/app/ui/src/main/java/com/fsck/k9/activity/FolderListFilter.java index 871985967..e2df35e9c 100644 --- a/app/ui/src/main/java/com/fsck/k9/activity/FolderListFilter.java +++ b/app/ui/src/main/java/com/fsck/k9/activity/FolderListFilter.java @@ -4,80 +4,41 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -import timber.log.Timber; -import android.widget.ArrayAdapter; import android.widget.Filter; /** - * Filter to search for occurrences of the search-expression in any place of the - * folder-name instead of doing just a prefix-search. - * - * @author Marcus@Wolschon.biz + * Filter to search for occurrences of the search-expression in any place of the folder name. */ -public class FolderListFilter extends Filter { - /** - * ArrayAdapter that contains the list of folders displayed in the - * ListView. - * This object is modified by {@link #publishResults} to reflect the - * changes due to the filtering performed by {@link #performFiltering}. - * This in turn will change the folders displayed in the ListView. - */ - private ArrayAdapter mFolders; +public class FolderListFilter extends Filter { + private final FolderAdapter adapter; + private final List folders; - /** - * All folders. - */ - private List mOriginalValues = null; - /** - * Create a filter for a list of folders. - * - * @param folders - */ - public FolderListFilter(final ArrayAdapter folders) { - this.mFolders = folders; + public FolderListFilter(FolderAdapter adapter, List folders) { + this.adapter = adapter; + this.folders = folders; } - /** - * Do the actual search. - * {@inheritDoc} - * - * @see #publishResults(CharSequence, FilterResults) - */ @Override protected FilterResults performFiltering(CharSequence searchTerm) { FilterResults results = new FilterResults(); - // Copy the values from mFolders to mOriginalValues if this is the - // first time this method is called. - if (mOriginalValues == null) { - int count = mFolders.getCount(); - mOriginalValues = new ArrayList<>(count); - for (int i = 0; i < count; i++) { - mOriginalValues.add(mFolders.getItem(i)); - } - } - Locale locale = Locale.getDefault(); - if ((searchTerm == null) || (searchTerm.length() == 0)) { - List list = new ArrayList<>(mOriginalValues); + if (searchTerm == null || searchTerm.length() == 0) { + List list = new ArrayList<>(folders); results.values = list; results.count = list.size(); } else { - final String searchTermString = searchTerm.toString().toLowerCase(locale); - final String[] words = searchTermString.split(" "); - final int wordCount = words.length; + String searchTermString = searchTerm.toString().toLowerCase(locale); + String[] words = searchTermString.split(" "); - final List values = mOriginalValues; + List newValues = new ArrayList<>(); + for (FolderInfoHolder folderInfoHolder : folders) { + String valueText = folderInfoHolder.displayName.toLowerCase(locale); - final List newValues = new ArrayList<>(); - - for (final T value : values) { - final String valueText = value.toString().toLowerCase(locale); - - for (int k = 0; k < wordCount; k++) { - if (valueText.contains(words[k])) { - newValues.add(value); + for (String word : words) { + if (valueText.contains(word)) { + newValues.add(folderInfoHolder); break; } } @@ -90,39 +51,15 @@ public class FolderListFilter extends Filter { return results; } - /** - * Publish the results to the user-interface. - * {@inheritDoc} - */ @SuppressWarnings("unchecked") @Override protected void publishResults(CharSequence constraint, FilterResults results) { - // Don't notify for every change - mFolders.setNotifyOnChange(false); - try { - - //noinspection unchecked - final List folders = (List) results.values; - mFolders.clear(); - if (folders != null) { - for (T folder : folders) { - if (folder != null) { - mFolders.add(folder); - } - } - } else { - Timber.w("FolderListFilter.publishResults - null search-result "); - } - - // Send notification that the data set changed now - mFolders.notifyDataSetChanged(); - } finally { - // restore notification status - mFolders.setNotifyOnChange(true); - } + List folders = (List) results.values; + adapter.setFilteredFolders(constraint, folders); } - public void invalidate() { - mOriginalValues = null; + + public interface FolderAdapter { + void setFilteredFolders(CharSequence filterText, List folders); } } diff --git a/app/ui/src/main/res/layout/choose_folder_list_item.xml b/app/ui/src/main/res/layout/choose_folder_list_item.xml new file mode 100644 index 000000000..fe6fe00e9 --- /dev/null +++ b/app/ui/src/main/res/layout/choose_folder_list_item.xml @@ -0,0 +1,32 @@ + + + + + + + +