de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter.java Source code

Java tutorial

Introduction

Here is the source code for de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter.java

Source

/**
* Android ownCloud News
*
* @author David Luhmer
* @copyright 2013 David Luhmer david-dev@live.de
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
*
*/

package de.luhmer.owncloudnewsreader.ListView;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.ViewUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

import org.apache.commons.lang3.time.StopWatch;

import java.util.ArrayList;
import java.util.List;

import butterknife.Bind;
import butterknife.ButterKnife;
import de.luhmer.owncloudnewsreader.R;
import de.luhmer.owncloudnewsreader.SettingsActivity;
import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm;
import de.luhmer.owncloudnewsreader.database.model.Feed;
import de.luhmer.owncloudnewsreader.database.model.Folder;
import de.luhmer.owncloudnewsreader.helper.FavIconHandler;
import de.luhmer.owncloudnewsreader.helper.ThemeChooser;
import de.luhmer.owncloudnewsreader.interfaces.ExpListTextClicked;
import de.luhmer.owncloudnewsreader.model.AbstractItem;
import de.luhmer.owncloudnewsreader.model.ConcreteFeedItem;
import de.luhmer.owncloudnewsreader.model.FolderSubscribtionItem;
import de.luhmer.owncloudnewsreader.model.Tuple;

import static de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter.SPECIAL_FOLDERS.ALL_STARRED_ITEMS;
import static de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter.SPECIAL_FOLDERS.ALL_UNREAD_ITEMS;
import static de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter.SPECIAL_FOLDERS.ITEMS_WITHOUT_FOLDER;

public class SubscriptionExpandableListAdapter extends BaseExpandableListAdapter {
    private final String TAG = getClass().getCanonicalName();

    private Context mContext;
    private DatabaseConnectionOrm dbConn;

    ListView listView;

    ExpListTextClicked eListTextClickHandler;

    private ArrayList<AbstractItem> mCategoriesArrayList;
    private SparseArray<ArrayList<ConcreteFeedItem>> mItemsArrayList;
    private boolean showOnlyUnread = false;

    public enum SPECIAL_FOLDERS {
        ALL_UNREAD_ITEMS(-10), ALL_STARRED_ITEMS(-11), ALL_ITEMS(-12), ITEMS_WITHOUT_FOLDER(-22);

        private int id;

        SPECIAL_FOLDERS(int id) {
            this.id = id;
        }

        public int getValue() {
            return id;
        }

        public String getValueString() {
            return String.valueOf(id);
        }

        @Override
        public String toString() {
            return getValueString();
        }
    }

    private int mTextColorLightTheme;
    private FavIconHandler favIconHandler;

    LayoutInflater inflater;

    public SubscriptionExpandableListAdapter(Context mContext, DatabaseConnectionOrm dbConn, ListView listView) {
        favIconHandler = new FavIconHandler(mContext);

        this.inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.mContext = mContext;
        this.dbConn = dbConn;

        mTextColorLightTheme = ContextCompat.getColor(mContext, R.color.slider_listview_text_color_light_theme);

        unreadCountFeeds = new SparseArray<>();
        unreadCountFolders = new SparseArray<>();
        starredCountFeeds = new SparseArray<>();

        mCategoriesArrayList = new ArrayList<>();
        mItemsArrayList = new SparseArray<>();

        this.listView = listView;
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        int parent_id = (int) getGroupId(groupPosition);
        return mItemsArrayList.get(parent_id).get(childPosition);
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return ((ConcreteFeedItem) (getChild(groupPosition, childPosition))).id_database;
    }

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView,
            ViewGroup parent) {
        final ConcreteFeedItem item = (ConcreteFeedItem) getChild(groupPosition, childPosition);
        final ChildHolder viewHolder;

        if (convertView != null) {
            viewHolder = (ChildHolder) convertView.getTag();
        } else {
            LinearLayout view = new LinearLayout(mContext);
            convertView = inflater.inflate(R.layout.subscription_list_sub_item, view, true);
            viewHolder = new ChildHolder(convertView);
            convertView.setTag(viewHolder);
        }

        if (item != null) {
            String headerText = (item.header != null) ? item.header : "";
            viewHolder.tV_HeaderText.setText(headerText);

            String unreadCount;
            if (item.idFolder == ALL_STARRED_ITEMS.getValue()) {
                unreadCount = starredCountFeeds.get((int) item.id_database);
            } else {
                unreadCount = unreadCountFeeds.get((int) item.id_database);
            }

            if (unreadCount != null)
                viewHolder.tV_UnreadCount.setText(unreadCount);
            else
                viewHolder.tV_UnreadCount.setText("");

            favIconHandler.loadFavIconForFeed(item.favIcon, viewHolder.imgView_FavIcon);
        } else {
            viewHolder.tV_HeaderText.setText(mContext.getString(R.string.login_dialog_text_something_went_wrong));
            viewHolder.tV_UnreadCount.setText("");
            viewHolder.imgView_FavIcon.setImageDrawable(null);
        }

        return convertView;
    }

    static class ChildHolder {
        @Bind(R.id.list_item_layout)
        View listItemLayout;
        @Bind(R.id.summary)
        TextView tV_HeaderText;
        @Bind(R.id.tv_unreadCount)
        TextView tV_UnreadCount;
        @Bind(R.id.iVFavicon)
        ImageView imgView_FavIcon;

        public ChildHolder(View view) {
            ButterKnife.bind(this, view);
        }
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        int parent_id = (int) getGroupId(groupPosition);
        return (mItemsArrayList.get(parent_id) != null) ? mItemsArrayList.get(parent_id).size() : 0;
    }

    @Override
    public Object getGroup(int groupPosition) {
        return mCategoriesArrayList.get(groupPosition);
    }

    @Override
    public int getGroupCount() {
        return mCategoriesArrayList.size();
    }

    @Override
    public long getGroupId(int groupPosition) {
        return ((AbstractItem) getGroup(groupPosition)).id_database;
    }

    private enum GroupViewType {
        FOLDER, FEED
    }

    @Override
    public int getGroupType(int groupPosition) {
        AbstractItem ai = mCategoriesArrayList.get(groupPosition);

        if (ai instanceof FolderSubscribtionItem)
            return GroupViewType.FOLDER.ordinal();
        else
            return GroupViewType.FEED.ordinal();
    }

    @Override
    public int getGroupTypeCount() {
        return GroupViewType.values().length;
    }

    @Override
    public View getGroupView(final int groupPosition, final boolean isExpanded, View convertView,
            ViewGroup parent) {

        GroupHolder viewHolder;
        final AbstractItem group = (AbstractItem) getGroup(groupPosition);

        if (convertView == null) {
            LinearLayout view = new LinearLayout(mContext);
            convertView = inflater.inflate(R.layout.subscription_list_item, view, true);
            viewHolder = new GroupHolder(convertView);
            view.setTag(viewHolder);
        } else {
            viewHolder = (GroupHolder) convertView.getTag();
        }

        viewHolder.txt_Summary.setText(group.header);
        viewHolder.listItemLayout.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

                long idFeed = group.id_database;
                boolean skipFireEvent = false;

                if (group instanceof ConcreteFeedItem) {
                    fireListTextClicked(idFeed, false, (long) ITEMS_WITHOUT_FOLDER.getValue());
                    skipFireEvent = true;
                }

                if (!skipFireEvent)
                    fireListTextClicked(idFeed, true, ((FolderSubscribtionItem) group).idFolder);
            }
        });

        viewHolder.listItemLayout.setOnLongClickListener(new View.OnLongClickListener() {

            @Override
            public boolean onLongClick(View v) {

                long idFeed = group.id_database;

                if (group instanceof ConcreteFeedItem) {
                    fireListTextLongClicked(idFeed, false, (long) ITEMS_WITHOUT_FOLDER.getValue());
                } else {
                    fireListTextLongClicked(idFeed, true, ((FolderSubscribtionItem) group).idFolder);
                }
                return true; //consume event
            }
        });

        viewHolder.txt_UnreadCount.setText("");
        boolean skipGetUnread = false;
        if (group.idFolder != null && group.idFolder == ITEMS_WITHOUT_FOLDER.getValue()) {
            String unreadCount = unreadCountFeeds.get((int) group.id_database);
            if (unreadCount != null) {
                viewHolder.txt_UnreadCount.setText(unreadCount);
            }

            skipGetUnread = true;
        }

        if (!skipGetUnread) {
            String unreadCount = unreadCountFolders.get((int) group.id_database);
            if (unreadCount != null)
                viewHolder.txt_UnreadCount.setText(unreadCount);
        }

        int rotation = 0;
        int contentDescriptionId = R.string.content_desc_none;

        if (group.idFolder != null) {
            viewHolder.imgView.setVisibility(View.GONE);
            if (group.idFolder == ITEMS_WITHOUT_FOLDER.getValue()) {
                ConcreteFeedItem concreteFeedItem = ((ConcreteFeedItem) group);
                favIconHandler.loadFavIconForFeed(concreteFeedItem.favIcon, viewHolder.faviconView);
            }
        } else {
            if (group.id_database == ALL_STARRED_ITEMS.getValue()) {
                viewHolder.imgView.setVisibility(View.GONE);
                viewHolder.faviconView.setVisibility(View.VISIBLE);
                rotation = 0;
                viewHolder.faviconView.setImageResource(getBtn_rating_star_off_normal_holo_light());
            } else if (getChildrenCount(groupPosition) == 0) {
                viewHolder.imgView.setVisibility(View.GONE);
                viewHolder.faviconView.setVisibility(View.INVISIBLE);
            } else {
                viewHolder.imgView.setVisibility(View.VISIBLE);
                viewHolder.faviconView.setVisibility(View.INVISIBLE);
                viewHolder.imgView.setImageResource(R.drawable.ic_action_expand_less);

                if (isExpanded) {
                    rotation = 180;
                    contentDescriptionId = R.string.content_desc_collapse;
                } else {
                    if (ViewUtils.isLayoutRtl(listView)) {
                        rotation = -90; // mirror for rtl layout
                    } else {
                        rotation = 90;
                    }
                    contentDescriptionId = R.string.content_desc_expand;
                }

                viewHolder.imgView.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (isExpanded)
                            ((ExpandableListView) listView).collapseGroup(groupPosition);
                        else
                            ((ExpandableListView) listView).expandGroup(groupPosition);
                    }
                });
            }
        }

        viewHolder.imgView.setRotation(rotation);
        viewHolder.imgView.setContentDescription(viewHolder.imgView.getContext().getString(contentDescriptionId));

        return convertView;
    }

    private Integer btn_rating_star_off_normal_holo_light;

    private int getBtn_rating_star_off_normal_holo_light() {
        if (btn_rating_star_off_normal_holo_light == null) {
            if (ThemeChooser.isDarkTheme(mContext)) {
                btn_rating_star_off_normal_holo_light = R.drawable.ic_action_star_border_dark;
            } else {
                btn_rating_star_off_normal_holo_light = R.drawable.ic_action_star_border_light;
            }
        }
        return btn_rating_star_off_normal_holo_light;
    }

    static class GroupHolder {
        @Bind(R.id.list_item_layout)
        View listItemLayout;
        @Bind(R.id.summary)
        TextView txt_Summary;
        @Bind(R.id.tV_feedsCount)
        TextView txt_UnreadCount;
        @Bind(R.id.img_View_expandable_indicator)
        ImageButton imgView;
        @Bind(R.id.img_view_favicon)
        ImageView faviconView;

        public GroupHolder(View view) {
            ButterKnife.bind(this, view);
        }
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }

    SparseArray<String> starredCountFeeds;
    SparseArray<String> unreadCountFolders;
    SparseArray<String> unreadCountFeeds;
    SparseArray<String> urlsToFavIcons;

    public void NotifyDataSetChangedAsync() {
        new NotifyDataSetChangedAsyncTask().execute((Void) null);
    }

    private class NotifyDataSetChangedAsyncTask extends AsyncTask<Void, Void, Void> {
        SparseArray<String> starredCountFeedsTemp;
        SparseArray<String> unreadCountFoldersTemp;
        SparseArray<String> unreadCountFeedsTemp;
        SparseArray<String> urlsToFavIconsTemp;

        @Override
        protected Void doInBackground(Void... voids) {
            StopWatch stopwatch = new StopWatch();
            stopwatch.start();

            SparseArray<String>[] temp = dbConn.getUnreadItemCountFeedFolder();

            unreadCountFoldersTemp = temp[0];// dbConn.getUnreadItemCountForFolder();
            unreadCountFeedsTemp = temp[1]; // dbConn.getUnreadItemCountForFeed();

            starredCountFeedsTemp = dbConn.getStarredItemCount();
            urlsToFavIconsTemp = dbConn.getUrlsToFavIcons();

            stopwatch.stop();
            Log.v(TAG, "Fetched folder/feed counts in " + stopwatch.toString());
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            if (showOnlyUnread) {
                for (int i = 0; i < mCategoriesArrayList.size(); i++) {
                    AbstractItem item = mCategoriesArrayList.get(i);

                    if (item instanceof FolderSubscribtionItem
                            && unreadCountFoldersTemp.get(((Long) item.id_database).intValue()) == null) {
                        Log.v(TAG, "Remove folder item!!!");
                        mCategoriesArrayList.remove(i);
                        i--;
                    } else if (item instanceof ConcreteFeedItem
                            && unreadCountFeedsTemp.get(((Long) item.id_database).intValue()) == null) {
                        Log.v(TAG, "Remove feed item!!!");
                        mCategoriesArrayList.remove(i);
                        i--;
                    } else {
                        //Log.v(TAG, "Keep.. " + unreadCountFoldersTemp.get(((Long) item.id_database).intValue()));
                    }
                }

                for (int i = 0; i < mItemsArrayList.size(); i++) {
                    ArrayList<ConcreteFeedItem> item = mItemsArrayList.valueAt(i);
                    for (int x = 0; x < item.size(); x++) {
                        if (unreadCountFeedsTemp.get((int) item.get(x).id_database) == null) {
                            item.remove(x);
                            x--;
                            Log.v(TAG, "Remove sub feed!!");
                        }
                    }
                }
            }

            notifyCountDataSetChanged(unreadCountFoldersTemp, unreadCountFeedsTemp, urlsToFavIconsTemp,
                    starredCountFeedsTemp);
            super.onPostExecute(aVoid);
        }
    }

    public void ReloadAdapterAsync() {
        new ReloadAdapterAsyncTask().execute((Void) null);
    }

    private class ReloadAdapterAsyncTask extends
            AsyncTask<Void, Void, Tuple<ArrayList<AbstractItem>, SparseArray<ArrayList<ConcreteFeedItem>>>> {

        public ReloadAdapterAsyncTask() {

        }

        @Override
        protected Tuple<ArrayList<AbstractItem>, SparseArray<ArrayList<ConcreteFeedItem>>> doInBackground(
                Void... voids) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();

            Tuple<ArrayList<AbstractItem>, SparseArray<ArrayList<ConcreteFeedItem>>> ad = ReloadAdapter();
            //return ReloadAdapter();

            stopWatch.stop();
            Log.v(TAG, "Reload Adapter - time taken: " + stopWatch.toString());

            return ad;
        }

        @Override
        protected void onPostExecute(
                Tuple<ArrayList<AbstractItem>, SparseArray<ArrayList<ConcreteFeedItem>>> arrayListSparseArrayTuple) {
            mCategoriesArrayList = arrayListSparseArrayTuple.key;
            mItemsArrayList = arrayListSparseArrayTuple.value;

            notifyDataSetChanged();

            NotifyDataSetChangedAsync();

            super.onPostExecute(arrayListSparseArrayTuple);
        }

    }

    public Tuple<ArrayList<AbstractItem>, SparseArray<ArrayList<ConcreteFeedItem>>> ReloadAdapter() {
        SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
        showOnlyUnread = mPrefs.getBoolean(SettingsActivity.CB_SHOWONLYUNREAD_STRING, false);

        ArrayList<AbstractItem> mCategoriesArrayListAsync = new ArrayList<>();
        mCategoriesArrayListAsync.add(new FolderSubscribtionItem(mContext.getString(R.string.allUnreadFeeds), null,
                ALL_UNREAD_ITEMS.getValue()));
        mCategoriesArrayListAsync.add(new FolderSubscribtionItem(mContext.getString(R.string.starredFeeds), null,
                ALL_STARRED_ITEMS.getValue()));

        StopWatch sw = new StopWatch();
        sw.start();

        List<Folder> folderList;
        //if(showOnlyUnread) {
        //    folderList = dbConn.getListOfFoldersWithUnreadItems();
        //} else {
        folderList = dbConn.getListOfFolders();
        //}

        sw.stop();
        Log.v(TAG, "Time needed (fetch folder list): " + sw.toString());

        for (Folder folder : folderList) {
            mCategoriesArrayListAsync.add(new FolderSubscribtionItem(folder.getLabel(), null, folder.getId()));
        }

        for (Feed feed : dbConn.getListOfFeedsWithoutFolders(showOnlyUnread)) {
            mCategoriesArrayListAsync.add(new ConcreteFeedItem(feed.getFeedTitle(),
                    (long) ITEMS_WITHOUT_FOLDER.getValue(), feed.getId(), feed.getFaviconUrl(), feed.getId()));
        }

        SparseArray<ArrayList<ConcreteFeedItem>> mItemsArrayListAsync = new SparseArray<>();

        for (int groupPosition = 0; groupPosition < mCategoriesArrayListAsync.size(); groupPosition++) {
            //int parent_id = (int)getGroupId(groupPosition);
            int parent_id = (int) mCategoriesArrayListAsync.get(groupPosition).id_database;
            mItemsArrayListAsync.append(parent_id, new ArrayList<ConcreteFeedItem>());

            List<Feed> feedItemList = null;

            if (parent_id == ALL_UNREAD_ITEMS.getValue()) {
                feedItemList = dbConn.getAllFeedsWithUnreadRssItems();
            } else if (parent_id == ALL_STARRED_ITEMS.getValue()) {
                feedItemList = dbConn.getAllFeedsWithStarredRssItems();
            } else {
                for (Folder folder : folderList) {//Find the current selected folder
                    if (folder.getId() == parent_id) {//Current item
                        feedItemList = dbConn.getAllFeedsWithUnreadRssItemsForFolder(folder.getId());
                        break;
                    }
                }
            }

            if (feedItemList != null) {
                for (Feed feed : feedItemList) {
                    ConcreteFeedItem newItem = new ConcreteFeedItem(feed.getFeedTitle(), (long) parent_id,
                            feed.getId(), feed.getFaviconUrl(), feed.getId());
                    mItemsArrayListAsync.get(parent_id).add(newItem);
                }
            }
        }

        return new Tuple<>(mCategoriesArrayListAsync, mItemsArrayListAsync);
    }

    @SuppressLint("NewApi") // wrongly reports setSelectionFromTop is only available in lollipop
    public void notifyCountDataSetChanged(SparseArray<String> unreadCountFolders,
            SparseArray<String> unreadCountFeeds, SparseArray<String> urlsToFavIcons,
            SparseArray<String> starredCountFeeds) {
        this.unreadCountFolders = unreadCountFolders;
        this.unreadCountFeeds = unreadCountFeeds;
        this.urlsToFavIcons = urlsToFavIcons;
        this.starredCountFeeds = starredCountFeeds;

        BlockingExpandableListView bView = (BlockingExpandableListView) listView;

        int firstVisPos = bView.getFirstVisiblePosition();
        View firstVisView = bView.getChildAt(0);
        int top = firstVisView != null ? firstVisView.getTop() : 0;

        // Number of items added before the first visible item
        int itemsAddedBeforeFirstVisible = 0;

        bView.setBlockLayoutChildren(true);
        notifyDataSetChanged();
        bView.setBlockLayoutChildren(false);

        // Call setSelectionFromTop to change the ListView position
        if (bView.getCount() >= firstVisPos + itemsAddedBeforeFirstVisible)
            bView.setSelectionFromTop(firstVisPos + itemsAddedBeforeFirstVisible, top);
    }

    public void setHandlerListener(ExpListTextClicked listener) {
        eListTextClickHandler = listener;
    }

    protected void fireListTextClicked(long idFeed, boolean isFolder, Long optional_folder_id) {
        if (eListTextClickHandler != null)
            eListTextClickHandler.onTextClicked(idFeed, isFolder, optional_folder_id);
    }

    protected void fireListTextLongClicked(long idFeed, boolean isFolder, Long optional_folder_id) {
        if (eListTextClickHandler != null)
            eListTextClickHandler.onTextLongClicked(idFeed, isFolder, optional_folder_id);
    }
}