summaryrefslogtreecommitdiffstats
path: root/src/com/cyanogenmod/eleven/sectionadapter
diff options
context:
space:
mode:
authorlinus_lee <llee@cyngn.com>2014-11-20 16:39:38 -0800
committerlinus_lee <llee@cyngn.com>2014-12-09 12:23:20 -0800
commit71810ebb2bf8fd792c92487fe87f9dbebefc8541 (patch)
tree42a4d11ba03a4c7af843edc0b45375b17c64053c /src/com/cyanogenmod/eleven/sectionadapter
parentf199f983c9a5e2f4434b85273d1da0d609c33228 (diff)
downloadandroid_packages_apps_Eleven-71810ebb2bf8fd792c92487fe87f9dbebefc8541.tar.gz
android_packages_apps_Eleven-71810ebb2bf8fd792c92487fe87f9dbebefc8541.tar.bz2
android_packages_apps_Eleven-71810ebb2bf8fd792c92487fe87f9dbebefc8541.zip
Update Eleven headers and namespace for open source
Change-Id: I82caf2ebf991998e67f546ff2ac7eaf2b30dc6be
Diffstat (limited to 'src/com/cyanogenmod/eleven/sectionadapter')
-rw-r--r--src/com/cyanogenmod/eleven/sectionadapter/SectionAdapter.java419
-rw-r--r--src/com/cyanogenmod/eleven/sectionadapter/SectionCreator.java78
-rw-r--r--src/com/cyanogenmod/eleven/sectionadapter/SectionListContainer.java36
3 files changed, 533 insertions, 0 deletions
diff --git a/src/com/cyanogenmod/eleven/sectionadapter/SectionAdapter.java b/src/com/cyanogenmod/eleven/sectionadapter/SectionAdapter.java
new file mode 100644
index 0000000..870382a
--- /dev/null
+++ b/src/com/cyanogenmod/eleven/sectionadapter/SectionAdapter.java
@@ -0,0 +1,419 @@
+/*
+* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.sectionadapter;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import com.cyanogenmod.eleven.R;
+import com.cyanogenmod.eleven.ui.MusicHolder;
+import com.cyanogenmod.eleven.utils.SectionCreatorUtils.Section;
+import com.cyanogenmod.eleven.utils.SectionCreatorUtils.SectionType;
+import com.cyanogenmod.eleven.widgets.IPopupMenuCallback;
+
+import java.util.TreeMap;
+
+/**
+ * This class wraps an ArrayAdapter that implements BasicAdapter and allows Sections to be inserted
+ * into the list. This wraps the methods for getting the view/indices and returns the section
+ * heads and if it is an underlying item it flows it through the underlying adapter
+ * @param <TItem> The underlying item that is in the array adapter
+ * @param <TArrayAdapter> the arrayadapter that contains TItem and implements BasicAdapter
+ */
+public class SectionAdapter<TItem,
+ TArrayAdapter extends ArrayAdapter<TItem> & SectionAdapter.BasicAdapter & IPopupMenuCallback>
+ extends BaseAdapter implements IPopupMenuCallback, IPopupMenuCallback.IListener {
+ /**
+ * Basic interface that the adapters implement
+ */
+ public interface BasicAdapter {
+ public void unload();
+ public void buildCache();
+ public void flush();
+ public int getItemPosition(long id);
+ }
+
+ /**
+ * The underlying adapter to wrap
+ */
+ protected TArrayAdapter mUnderlyingAdapter;
+
+ /**
+ * A map of external position to the Section type and Identifier
+ */
+ protected TreeMap<Integer, Section> mSections;
+
+ protected int mHeaderLayoutId;
+ protected boolean mHeaderEnabled;
+
+ protected int mFooterLayoutId;
+ protected boolean mFooterEnabled;
+
+ /**
+ * Popup menu click listener
+ */
+ protected IListener mListener;
+
+ /**
+ * {@link Context}
+ */
+ protected final Context mContext;
+
+ /**
+ * Creates a SectionAdapter
+ * @param context The {@link Context} to use.
+ * @param underlyingAdapter the underlying adapter to wrap
+ */
+ public SectionAdapter(final Activity context, final TArrayAdapter underlyingAdapter) {
+ mContext = context;
+ mUnderlyingAdapter = underlyingAdapter;
+ mUnderlyingAdapter.setPopupMenuClickedListener(this);
+ mSections = new TreeMap<Integer, Section>();
+ setupHeaderParameters(R.layout.list_header, false);
+ // since we have no good default footer, just re-use the header layout
+ setupFooterParameters(R.layout.list_header, false);
+ }
+
+ /**
+ * Gets the underlying array adapter
+ * @return the underlying array adapter
+ */
+ public TArrayAdapter getUnderlyingAdapter() {
+ return mUnderlyingAdapter;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View getView(final int position, View convertView, final ViewGroup parent) {
+ if (isSection(position)) {
+ if (convertView == null) {
+ int layoutId = mHeaderLayoutId;
+ if (isSectionFooter(position)) {
+ layoutId = mFooterLayoutId;
+ }
+
+ convertView = LayoutInflater.from(mContext).inflate(layoutId, parent, false);
+ }
+
+ TextView title = (TextView)convertView.findViewById(R.id.title);
+ title.setText(mSections.get(position).mIdentifier);
+ } else {
+ convertView = mUnderlyingAdapter.getView(
+ getInternalPosition(position), convertView, parent);
+
+ Object tag = convertView.getTag();
+ if (tag instanceof MusicHolder) {
+ MusicHolder holder = (MusicHolder)tag;
+ View divider = holder.mDivider.get();
+ if (divider != null) {
+ // if it is the last item in the list, or it is an item before a section divider
+ // then hide the divider, otherwise show it
+ if (position == getCount() - 1 || isSection(position + 1)) {
+ divider.setVisibility(View.INVISIBLE);
+ } else {
+ divider.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+ }
+
+ return convertView;
+ }
+
+ /**
+ * Setup the header parameters
+ * @param layoutId the layout id used to inflate
+ * @param enabled whether clicking is enabled on the header
+ */
+ public void setupHeaderParameters(int layoutId, boolean enabled) {
+ mHeaderLayoutId = layoutId;
+ mHeaderEnabled = enabled;
+ }
+
+ /**
+ * Setup the footer parameters
+ * @param layoutId the layout id used to inflate
+ * @param enabled whether clicking is enabled on the footer
+ */
+ public void setupFooterParameters(int layoutId, boolean enabled) {
+ mFooterLayoutId = layoutId;
+ mFooterEnabled = enabled;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getCount() {
+ return mSections.size() + mUnderlyingAdapter.getCount();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object getItem(int position) {
+ if (isSection(position)) {
+ return mSections.get(position);
+ }
+
+ return mUnderlyingAdapter.getItem(getInternalPosition(position));
+ }
+
+ /**
+ * Gets the underlying adapter's item
+ * @param position position to query for
+ * @return the underlying item or null if a section header is queried
+ */
+ public TItem getTItem(int position) {
+ if (isSection(position)) {
+ return null;
+ }
+
+ return mUnderlyingAdapter.getItem(getInternalPosition(position));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getItemViewType(int position) {
+ if (isSectionHeader(position)) {
+ // use the last view type id as the section header
+ return getViewTypeCount() - 1;
+ } else if (isSectionFooter(position)) {
+ // use the last view type id as the section header
+ return getViewTypeCount() - 2;
+ }
+
+ return mUnderlyingAdapter.getItemViewType(getInternalPosition(position));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getViewTypeCount() {
+ // increment view type count by 2 for section headers and section footers
+ return mUnderlyingAdapter.getViewTypeCount() + 2;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void notifyDataSetChanged() {
+ super.notifyDataSetChanged();
+
+ mUnderlyingAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void notifyDataSetInvalidated() {
+ super.notifyDataSetInvalidated();
+
+ mUnderlyingAdapter.notifyDataSetInvalidated();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isEnabled(int position) {
+ if (isSectionHeader(position)) {
+ return mHeaderEnabled;
+ } else if (isSectionFooter(position)) {
+ return mFooterEnabled;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ /**
+ * Determines whether the item at the position is a section header
+ * @param position position in the overall lis
+ * @return true if a section header
+ */
+ public boolean isSectionHeader(int position) {
+ return mSections.containsKey(position) && mSections.get(position).mType == SectionType.Header;
+ }
+
+ /**
+ * Determines whether the item at the position is a section footer
+ * @param position position in the overall lis
+ * @return true if a section footer
+ */
+ public boolean isSectionFooter(int position) {
+ return mSections.containsKey(position) && mSections.get(position).mType == SectionType.Footer;
+ }
+
+ /**
+ * Determines whether the item at the position is a section of some type
+ * @param position position in the overall lis
+ * @return true if the item is a section
+ */
+ public boolean isSection(int position) {
+ return mSections.containsKey(position);
+ }
+
+ /**
+ * Converts the external position to the internal position. This is needed to determine
+ * the position to pass into the underlying adapter
+ * @param position external position
+ * @return the internal position
+ */
+ public int getInternalPosition(int position) {
+ if (isSection(position)) {
+ return -1;
+ }
+
+ int countSectionHeaders = 0;
+
+ for (Integer sectionPosition : mSections.keySet()) {
+ if (sectionPosition <= position) {
+ countSectionHeaders++;
+ } else {
+ break;
+ }
+ }
+
+ return position - countSectionHeaders;
+ }
+
+ /**
+ * Converts the underlaying adapter position to wrapped adapter position
+ * @param internalPosition the position of the underlying adapter
+ * @return the position of the wrapped adapter
+ */
+ public int getExternalPosition(int internalPosition) {
+ int externalPosition = internalPosition;
+ for (Integer sectionPosition : mSections.keySet()) {
+ // because the section headers are tracking the 'merged' lists, we need to keep bumping
+ // our position for each found section header
+ if (sectionPosition <= externalPosition) {
+ externalPosition++;
+ } else {
+ break;
+ }
+ }
+
+ return externalPosition;
+ }
+
+ /**
+ * Sets the data on the adapter
+ * @param data data to set
+ */
+ public void setData(SectionListContainer<TItem> data) {
+ mUnderlyingAdapter.unload();
+
+ if (data.mSections == null) {
+ mSections.clear();
+ } else {
+ mSections = data.mSections;
+ }
+
+ mUnderlyingAdapter.addAll(data.mListResults);
+
+ mUnderlyingAdapter.buildCache();
+
+ notifyDataSetChanged();
+ }
+
+ /**
+ * unloads the underlying adapter
+ */
+ public void unload() {
+ mSections.clear();
+ mUnderlyingAdapter.unload();
+ notifyDataSetChanged();
+ }
+
+ /**
+ * flushes the underlying adapter
+ */
+ public void flush() {
+ mUnderlyingAdapter.flush();
+ notifyDataSetChanged();
+ }
+
+ public void clear() {
+ mSections.clear();
+ mUnderlyingAdapter.clear();
+ mSections.clear();
+ }
+
+ /**
+ * Gets the item position for the given identifier
+ * @param identifier used to identify the object
+ * @return item position, or -1 if not found
+ */
+ public int getItemPosition(long identifier) {
+ int internalPosition = mUnderlyingAdapter.getItemPosition(identifier);
+ if (internalPosition >= 0) {
+ return getExternalPosition(internalPosition);
+ }
+
+ return -1;
+ }
+
+ @Override
+ public void setPopupMenuClickedListener(IListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onPopupMenuClicked(View v, int position) {
+ if (mListener != null) {
+ mListener.onPopupMenuClicked(v, getExternalPosition(position));
+ }
+ }
+}
diff --git a/src/com/cyanogenmod/eleven/sectionadapter/SectionCreator.java b/src/com/cyanogenmod/eleven/sectionadapter/SectionCreator.java
new file mode 100644
index 0000000..16e7e09
--- /dev/null
+++ b/src/com/cyanogenmod/eleven/sectionadapter/SectionCreator.java
@@ -0,0 +1,78 @@
+/*
+* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.sectionadapter;
+
+import android.content.Context;
+
+import com.cyanogenmod.eleven.loaders.WrappedAsyncTaskLoader;
+import com.cyanogenmod.eleven.utils.SectionCreatorUtils;
+
+import java.util.List;
+import java.util.TreeMap;
+
+/**
+ * This class wraps a SimpleListLoader and creates header sections for the sections
+ * @param <T> The type of item that is loaded
+ */
+public class SectionCreator<T> extends WrappedAsyncTaskLoader<SectionListContainer<T>> {
+ /**
+ * Simple list loader class that exposes a load method
+ * @param <T> type of item to load
+ */
+ public static abstract class SimpleListLoader<T> extends WrappedAsyncTaskLoader<List<T>> {
+ protected Context mContext;
+
+ public SimpleListLoader(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+ }
+
+ private SimpleListLoader<T> mLoader;
+ private SectionCreatorUtils.IItemCompare<T> mComparator;
+
+ /**
+ * Creates a SectionCreator object which loads @loader
+ * @param context The {@link Context} to use.
+ * @param loader loader to wrap
+ * @param comparator the comparison object to run to create the sections
+ */
+ public SectionCreator(Context context, SimpleListLoader<T> loader,
+ SectionCreatorUtils.IItemCompare<T> comparator) {
+ super(context);
+ mLoader = loader;
+ mComparator = comparator;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SectionListContainer<T> loadInBackground() {
+ List<T> results = mLoader.loadInBackground();
+ TreeMap<Integer, SectionCreatorUtils.Section> sections = null;
+
+ if (mComparator != null) {
+ sections = SectionCreatorUtils.createSections(results, mComparator);
+ }
+
+ return new SectionListContainer<T>(sections, results);
+ }
+}
diff --git a/src/com/cyanogenmod/eleven/sectionadapter/SectionListContainer.java b/src/com/cyanogenmod/eleven/sectionadapter/SectionListContainer.java
new file mode 100644
index 0000000..486bf84
--- /dev/null
+++ b/src/com/cyanogenmod/eleven/sectionadapter/SectionListContainer.java
@@ -0,0 +1,36 @@
+/*
+* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.sectionadapter;
+
+import com.cyanogenmod.eleven.utils.SectionCreatorUtils;
+
+import java.util.List;
+import java.util.TreeMap;
+
+/**
+ * Simple Container that contains a list of T items as well as the map of section information
+ * @param <T> the type of item that the list contains
+ */
+public class SectionListContainer<T> {
+ public TreeMap<Integer, SectionCreatorUtils.Section> mSections;
+ public List<T> mListResults;
+
+ public SectionListContainer(final TreeMap<Integer, SectionCreatorUtils.Section> sections,
+ final List<T> results) {
+ mSections = sections;
+ mListResults = results;
+ }
+}