/* * Copyright (c) 2011. WillowTree Apps * * 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 oak.sectionlistview; import android.annotation.TargetApi; import android.os.Build; import android.util.Pair; import android.widget.Filter; import android.widget.Filterable; import java.util.ArrayList; import java.util.List; /** * User: Michael Lake Date: 5/17/11 Time: 11:13 AM */ @TargetApi(Build.VERSION_CODES.ECLAIR) public abstract class SectionAdapter<T extends Sectionable> extends BaseSectionAdapter implements Filterable { private CustomFilter mFilter; private List<Pair<String, List<T>>> mObjects; private ArrayList<Pair<String, List<T>>> mOriginalValues; /** * Lock used to modify the content of {@link #mObjects}. Any write operation performed on the * array should be synchronized on this lock. This lock is also used by the filter (see {@link * #getFilter()} to make a synchronized copy of the original array of data. */ private final Object mLock = new Object(); public void setDataManually(List<Pair<String, List<T>>> data) { mObjects = data; notifyDataSetChanged(); } public void setData(List<T> unSectionedList) { List<Pair<String, List<T>>> res = new ArrayList<Pair<String, List<T>>>(); Pair<String, List<T>> pair; ArrayList<T> subArray = null; for (int i = 0; i < unSectionedList.size(); i++) { T currentSectionable = unSectionedList.get(i); String nextSection = null; if (subArray == null) { subArray = new ArrayList<T>(); } if (i + 1 < unSectionedList.size()) { nextSection = unSectionedList.get(i + 1).getSection(); } String currentSection = currentSectionable.getSection(); subArray.add(currentSectionable); if (!currentSection.equals(nextSection)) { pair = new Pair<String, List<T>>(currentSection, subArray); res.add(pair); subArray = null; } } mObjects = res; notifyDataSetChanged(); } public void clear() { mObjects = null; } public synchronized void replaceDataInSection(String section, List<T> dataForSection) { if (mObjects == null) { return; } for (int i = 0; i < mObjects.size(); i++) { Pair<String, List<T>> pair = mObjects.get(i); if (pair.first.equals(section)) { mObjects.remove(i); Pair<String, List<T>> newPair = new Pair<String, List<T>>(section, dataForSection); mObjects.add(i, newPair); break; } } notifyDataSetChanged(); } @Override public int getCount() { if (mObjects == null) { return 0; } int res = 0; for (int i = 0; i < mObjects.size(); i++) { res += mObjects.get(i).second.size(); } return res; } @Override public T getItem(int position) { int c = 0; for (int i = 0; i < mObjects.size(); i++) { if (position >= c && position < c + mObjects.get(i).second.size()) { return mObjects.get(i).second.get(position - c); } c += mObjects.get(i).second.size(); } return null; } @Override public long getItemId(int position) { return position; } @Override public int getPositionForSection(int section) { if (section < 0) { section = 0; } if (section >= mObjects.size()) { section = mObjects.size() - 1; } int c = 0; for (int i = 0; i < mObjects.size(); i++) { if (section == i) { return c; } c += mObjects.get(i).second.size(); } return 0; } @Override public int getSectionForPosition(int position) { int c = 0; for (int i = 0; i < mObjects.size(); i++) { if (position >= c && position < c + mObjects.get(i).second.size()) { return i; } c += mObjects.get(i).second.size(); } return -1; } // this is used by the fast search widget when scrolling through large lists, shows single letter in box @Override public String[] getSections() { String[] res = new String[mObjects.size()]; String section; for (int i = 0; i < mObjects.size(); i++) { section = mObjects.get(i).first; res[i] = section.length() > 1 ? mObjects.get(i).first.substring(0, 1) : section; } return res; } public String[] getSectionsWithFullName() { String[] res = new String[mObjects.size()]; for (int i = 0; i < mObjects.size(); i++) { res[i] = mObjects.get(i).first; } return res; } public boolean isPositionTopOfSection(int position) { int c = 0; for (int i = 0; i < mObjects.size(); i++) { if (position >= c && position < c + mObjects.get(i).second.size()) { if (position == c) { return true; } } c += mObjects.get(i).second.size(); } return false; } public boolean isPositionBottomOfSection(int position) { int c = 0; for (int i = 0; i < mObjects.size(); i++) { if (position >= c && position < c + mObjects.get(i).second.size()) { if (position == c + mObjects.get(i).second.size() - 1) { return true; } } c += mObjects.get(i).second.size(); } return false; } public Filter getFilter() { if (mFilter == null) { mFilter = new CustomFilter(); } return mFilter; } private class CustomFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence prefix) { FilterResults results = new FilterResults(); if (mOriginalValues == null) { synchronized (mLock) { mOriginalValues = new ArrayList<Pair<String, List<T>>>(mObjects); } } if (prefix == null || prefix.length() == 0) { synchronized (mLock) { List<Pair<String, List<T>>> list = new ArrayList<Pair<String, List<T>>>( mOriginalValues); results.values = list; results.count = list.size(); } } else { String prefixString = prefix.toString().toLowerCase(); final ArrayList<Pair<String, List<T>>> values = mOriginalValues; final int count = values.size(); final ArrayList<Pair<String, List<T>>> newValues = new ArrayList<Pair<String, List<T>>>(count); for (int i = 0; i < count; i++) { String sectionHeader = values.get(i).first; final int sectionCount = values.get(i).second.size(); final List<T> newSectionItems = new ArrayList<T>(sectionCount); for (int k = 0; k < sectionCount; k++) { final T sectionValue = values.get(i).second.get(k); final String sectionValueText = sectionValue.toString().toLowerCase(); if (sectionValue instanceof Queryable && ((Queryable) sectionValue).isQueryMatch(prefix)) { newSectionItems.add(sectionValue); } else { if (sectionValueText.startsWith(prefixString)) { newSectionItems.add(sectionValue); } else { final String[] words = sectionValueText.split(" "); final int wordCount = words.length; for (int j = 0; j < wordCount; j++) { if (words[j].startsWith(prefixString)) { newSectionItems.add(sectionValue); break; } } } } } if (newSectionItems.size() > 0) { newValues.add(new Pair<String, List<T>>(sectionHeader, newSectionItems)); } } results.values = newValues; results.count = newValues.size(); } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { //noinspection unchecked mObjects = (List<Pair<String, List<T>>>) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } } }