com.ptapp.activity.SessionsFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.ptapp.activity.SessionsFragment.java

Source

/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * 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.ptapp.activity;

import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.provider.BaseColumns;
import android.support.v4.view.ViewCompat;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.GenericRequestBuilder;
import com.bumptech.glide.ListPreloader;

import com.ptapp.provider.PTAppContract;
import com.ptapp.provider.PTAppDatabase;
import com.ptapp.app.R;

//import com.ptapp.provider.ScheduleContract;

import com.ptapp.widget.CollectionView;
import com.ptapp.widget.CollectionViewCallbacks;

import com.ptapp.widget.MessageCardView;

import com.ptapp.utils.*;
import com.squareup.picasso.Picasso;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.TimeZone;

import static com.ptapp.utils.LogUtils.*;

/**
 * A {@link android.app.ListFragment} showing a list of sessions. The fragment arguments
 * indicate what is the list of sessions to show. It may be a set of tag
 * filters or a search query.
 */
public class SessionsFragment extends Fragment
        implements LoaderManager.LoaderCallbacks<Cursor>, CollectionViewCallbacks {

    private static final String TAG = makeLogTag(SessionsFragment.class);

    private static final String STATE_SESSION_QUERY_TOKEN = "session_query_token";
    private static final String STATE_ARGUMENTS = "arguments";

    /**
     * The handler message for updating the search query.
     */
    private static final int MESSAGE_QUERY_UPDATE = 1;
    /**
     * The delay before actual requerying in millisecs.
     */
    private static final int QUERY_UPDATE_DELAY_MILLIS = 100;
    /**
     * The number of rows ahead to preload images for
     */
    private static final int ROWS_TO_PRELOAD = 2;

    private static final int ANIM_DURATION = 250;
    private static final int CARD_DISMISS_ACTION_DELAY = MessageCardView.ANIM_DURATION - 50;

    private Context mAppContext;

    // the cursor whose data we are currently displaying
    private int mSessionQueryToken;
    private Uri mCurrentUri = PTAppContract.ClassSubject.CONTENT_URI;
    private Cursor mCursor;
    private boolean mIsSearchCursor;

    // this variable is relevant when we start the sessions loader, and indicates the desired
    // behavior when load finishes: if true, this is a full reload (for example, because filters
    // have been changed); if not, it's just a refresh because data has changed.
    private boolean mSessionDataIsFullReload = false;

    private ImageLoader mImageLoader;
    private int mDefaultSessionColor;

    private CollectionView mCollectionView;
    private TextView mEmptyView;
    private View mLoadingView;
    //private TagMetadata mTagMetadata = null;

    private boolean mWasPaused = false;

    private static final int HERO_GROUP_ID = 123;

    private Bundle mArguments;

    private DateFormat mDateFormat = DateFormat.getDateInstance(DateFormat.SHORT);
    private DateFormat mTimeFormat = DateFormat.getTimeInstance(DateFormat.SHORT);

    private static final String CARD_ANSWER_ATTENDING_REMOTELY = "CARD_ANSWER_ATTENDING_REMOTELY";
    private static final String CARD_ANSWER_ATTENDING_IN_PERSON = "CARD_ANSWER_ATTENDING_IN_PERSON";
    private static final String CARD_ANSWER_YES = "CARD_ANSWER_YES";
    private static final String CARD_ANSWER_NO = "CARD_ANSWER_NO";

    private ThrottledContentObserver mSessionsObserver;//, mTagsObserver;

    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MESSAGE_QUERY_UPDATE) {
                /*String query = (String) msg.obj;
                reloadFromArguments(BaseActivity.intentToFragmentArguments(
                    new Intent(Intent.ACTION_SEARCH, PTAppContract.Courses.buildCourseUri(query))));*/
            }
        }

    };

    private Preloader mPreloader;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (mImageLoader == null) {
            mImageLoader = new ImageLoader(this.getActivity());
        }

        mDefaultSessionColor = getResources().getColor(R.color.default_session_color);

        final TimeZone tz = SharedPrefUtil.getDisplayTimeZone(getActivity());
        mDateFormat.setTimeZone(tz);
        mTimeFormat.setTimeZone(tz);

        if (savedInstanceState != null) {
            mSessionQueryToken = savedInstanceState.getInt(STATE_SESSION_QUERY_TOKEN);
            mArguments = savedInstanceState.getParcelable(STATE_ARGUMENTS);
            if (mArguments != null) {
                //mCurrentUri = mArguments.getParcelable("_uri");
            }

            if (mSessionQueryToken > 0) {
                // Only if this is a config change should we initLoader(), to reconnect with an
                // existing loader. Otherwise, the loader will be init'd when reloadFromArguments
                // is called.
                getLoaderManager().initLoader(mSessionQueryToken, null, SessionsFragment.this);
            }
        }
    }

    public boolean canCollectionViewScrollUp() {
        return ViewCompat.canScrollVertically(mCollectionView, -1);
    }

    public void setContentTopClearance(int topClearance) {
        mCollectionView.setContentTopClearance(topClearance);
    }

    // Called when there is a change on sessions in the content provider
    private void onSessionsContentChanged() {
        LOGD(TAG, "ThrottledContentObserver fired (sessions). Content changed.");
        if (!isAdded()) {
            LOGD(TAG, "Ignoring ContentObserver event (Fragment not added).");
            return;
        }

        LOGD(TAG, "Requesting sessions cursor reload as a result of ContentObserver firing.");
        reloadSessionData(false);
    }

    private void reloadSessionData(boolean fullReload) {
        LOGD(TAG, "Reloading session data: " + (fullReload ? "FULL RELOAD" : "light refresh"));
        mSessionDataIsFullReload = fullReload;
        getLoaderManager().restartLoader(mSessionQueryToken, mArguments, SessionsFragment.this);
    }

    @Override
    public void onPause() {
        super.onPause();
        mWasPaused = true;
    }

    @Override
    public void onResume() {
        super.onResume();
        if (mWasPaused) {
            mWasPaused = false;
            LOGD(TAG, "Reloading data as a result of onResume()");
            mSessionsObserver.cancelPendingCallback();
            reloadSessionData(false);
        }
    }

    public interface Callbacks {
        public void onSessionSelected(String sessionId, String courseName, String groupJid, int groupId,
                View clickedView);

        // public void onTagMetadataLoaded(TagMetadata metadata);
    }

    private static Callbacks sDummyCallbacks = new Callbacks() {
        @Override
        public void onSessionSelected(String sessionId, String courseName, String groupJid, int groupId,
                View clickedView) {
        }

        /* @Override
         public void onTagMetadataLoaded(TagMetadata metadata) {
         }*/
    };

    private Callbacks mCallbacks = sDummyCallbacks;

    private boolean useExpandedMode() {
        /*if (mCurrentUri != null && ScheduleContract.Sessions.CONTENT_URI.equals(mCurrentUri)) {
        // If showing all sessions (landing page) do not use expanded mode,
        // show info as condensed as possible
        return false;
        }*/
        if (mCurrentUri != null && PTAppContract.ClassSubject.CONTENT_URI.equals(mCurrentUri)) {
            // If showing all sessions (landing page) do not use expanded mode,
            // show info as condensed as possible
            return false;
        }
        return true;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mAppContext = getActivity().getApplicationContext();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_sessions, container, false);
        mCollectionView = (CollectionView) root.findViewById(R.id.sessions_collection_view);
        mPreloader = new Preloader(ROWS_TO_PRELOAD);
        mCollectionView.setOnScrollListener(mPreloader);
        mEmptyView = (TextView) root.findViewById(R.id.empty_text);
        mLoadingView = root.findViewById(R.id.loading);
        return root;
    }

    void reloadFromArguments(Bundle arguments) {
        // Load new arguments
        if (arguments == null) {
            arguments = new Bundle();
        } else {
            // since we might make changes, don't meddle with caller's copy
            arguments = (Bundle) arguments.clone();
        }

        // save arguments so we can reuse it when reloading from content observer events
        mArguments = arguments;

        LOGD(TAG, "SessionsFragment reloading from arguments: " + arguments);
        mCurrentUri = arguments.getParcelable("_uri");
        if (mCurrentUri == null) {
            // if no URI, default to all sessions URI
            /*LOGD(TAG, "SessionsFragment did not get a URL, defaulting to all sessions.");*/
            LOGD(TAG, "Setting uri to teacher's all groups...");
            arguments.putParcelable("_uri", PTAppContract.StaffEngagement.CONTENT_STAFF_GROUPS_URI);
            mCurrentUri = PTAppContract.StaffEngagement.CONTENT_STAFF_GROUPS_URI;
        }

        /*if (ScheduleContract.Sessions.isSearchUri(mCurrentUri)) {
        mSessionQueryToken = SessionsQuery.SEARCH_TOKEN;
        } else {*/
        mSessionQueryToken = SessionsQuery.NORMAL_TOKEN;
        /*}*/

        //LOGD(TAG, "SessionsFragment reloading, uri=" + mCurrentUri + ", expanded=" + useExpandedMode());

        reloadSessionData(true); // full reload
    }

    void requestQueryUpdate(String query) {
        mHandler.removeMessages(MESSAGE_QUERY_UPDATE);
        mHandler.sendMessageDelayed(Message.obtain(mHandler, MESSAGE_QUERY_UPDATE, query),
                QUERY_UPDATE_DELAY_MILLIS);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (!(activity instanceof Callbacks)) {
            throw new ClassCastException("Activity must implement fragment's callbacks.");
        }

        mAppContext = getActivity().getApplicationContext();
        mCallbacks = (Callbacks) activity;
        mSessionsObserver = new ThrottledContentObserver(new ThrottledContentObserver.Callbacks() {
            @Override
            public void onThrottledContentObserverFired() {
                onSessionsContentChanged();
            }
        });

        /*activity.getContentResolver().registerContentObserver(
            PTAppContract.Courses.CONTENT_URI, true, mSessionsObserver);*/

        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(activity);
        sp.registerOnSharedPreferenceChangeListener(mPrefChangeListener);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCallbacks = sDummyCallbacks;
        getActivity().getContentResolver().unregisterContentObserver(mSessionsObserver);

        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
        sp.unregisterOnSharedPreferenceChangeListener(mPrefChangeListener);
    }

    public void animateReload() {
        //int curTop = mCollectionView.getTop();
        mCollectionView.setAlpha(0);
        //mCollectionView.setTop(getResources().getDimensionPixelSize(R.dimen.browse_sessions_anim_amount));
        //mCollectionView.animate().y(curTop).alpha(1).setDuration(ANIM_DURATION).setInterpolator(new DecelerateInterpolator());
        mCollectionView.animate().alpha(1).setDuration(ANIM_DURATION).setInterpolator(new DecelerateInterpolator());
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(STATE_SESSION_QUERY_TOKEN, mSessionQueryToken);
        outState.putParcelable(STATE_ARGUMENTS, mArguments);
    }

    // LoaderCallbacks interface
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle data) {
        LOGD(TAG, "onCreateLoader, id=" + id + ", data=" + data);
        final Intent intent = BaseActivity.fragmentArgumentsToIntent(data);
        Uri sessionsUri = intent.getData();
        if ((id == SessionsQuery.NORMAL_TOKEN || id == SessionsQuery.SEARCH_TOKEN) && sessionsUri == null) {
            LOGD(TAG, "intent.getData() is null, setting to default sessions search");
            /*sessionsUri = ScheduleContract.Sessions.CONTENT_URI;*/
            /*sessionsUri = PTAppContract.ClassSubject.CONTENT_URI;*/
            sessionsUri = PTAppContract.StaffEngagement.CONTENT_STAFF_GROUPS_URI;
        }
        Loader<Cursor> loader = null;

        //Log.d(TAG, "teacherId onCreateLoader: " + SharedPrefUtil.getPrefTeacherUserId(getActivity()));
        String selectionWhere = PTAppContract.StaffEngagement.STAFF_ID + " = ?";
        /*String[] selectionArgs = new String[]{SharedPrefUtil.getPrefTeacherUserId(getActivity())};*/
        //TODO:get the staff ID from the sharedPref.
        String[] selectionArgs = new String[] { String.valueOf(2) };

        if (id == SessionsQuery.NORMAL_TOKEN) {
            LOGD(TAG, "Creating educator loader for " + sessionsUri + ", selectionWhere " + selectionWhere);

            loader = new CursorLoader(getActivity(), sessionsUri, SessionsQuery.NORMAL_PROJECTION, selectionWhere,
                    selectionArgs, null);
            /*loader = new CursorLoader(getActivity(), sessionsUri, SessionsQuery.NORMAL_PROJECTION,
                selectionWhere, selectionArgs, PTAppContract.Courses.SORT_BY_COURSE_NAME);*/
        }
        return loader;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        if (getActivity() == null) {
            return;

        }

        int token = loader.getId();
        LOGD(TAG, "Loader finished: " + (token == SessionsQuery.NORMAL_TOKEN ? "educator"
                : token == SessionsQuery.SEARCH_TOKEN ? "search" : ""));
        if (token == SessionsQuery.NORMAL_TOKEN || token == SessionsQuery.SEARCH_TOKEN) {
            if (mCursor != null && mCursor != cursor) {
                mCursor.close();
            }
            mCursor = cursor;
            mIsSearchCursor = token == SessionsQuery.SEARCH_TOKEN;
            LOGD(TAG, "Cursor has " + mCursor.getCount() + " items. Will now update collection view.");
            updateCollectionView();
        } else {
            LOGD(TAG, "Query complete, Not Actionable: " + token);
            cursor.close();
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
    }

    private final SharedPreferences.OnSharedPreferenceChangeListener mPrefChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
        @Override
        public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
            if (isAdded()) {
                if (SharedPrefUtil.PREF_LOCAL_TIMES.equals(key)) {
                    updateCollectionView();
                } else if (SharedPrefUtil.PREF_ATTENDEE_AT_VENUE.equals(key)) {
                    if (mCursor != null) {
                        reloadSessionData(true);
                    }
                }
            }
        }
    };

    private void updateCollectionView() {
        if (mCursor == null) {
            LOGD(TAG, "updateCollectionView: not ready yet... no cursor.");
            // not ready!
            return;
        }
        LOGD(TAG, "SessionsFragment updating CollectionView... "
                + (mSessionDataIsFullReload ? "(FULL RELOAD)" : "(light refresh)"));
        mCursor.moveToPosition(-1);
        int itemCount = mCursor.getCount();

        mMaxDataIndexAnimated = 0;

        CollectionView.Inventory inv;
        if (itemCount == 0) {
            showEmptyView();
            inv = new CollectionView.Inventory();
        } else {
            hideEmptyView();
            inv = prepareInventory();
        }

        Parcelable state = null;
        if (!mSessionDataIsFullReload) {
            // it's not a full reload, so we want to keep scroll position, etc
            state = mCollectionView.onSaveInstanceState();
        }
        LOGD(TAG, "Updating CollectionView with inventory, # groups = " + inv.getGroupCount() + " total items = "
                + inv.getTotalItemCount());
        mCollectionView.setCollectionAdapter(this);
        mCollectionView.updateInventory(inv, mSessionDataIsFullReload);
        if (state != null) {
            mCollectionView.onRestoreInstanceState(state);
        }
        mSessionDataIsFullReload = false;
    }

    private void hideEmptyView() {
        mEmptyView.setVisibility(View.GONE);
        mLoadingView.setVisibility(View.GONE);
    }

    private void showEmptyView() {
        /*final String searchQuery = PTAppContract.Courses.isSearchUri(mCurrentUri) ?
            PTAppContract.Courses.getSearchQuery(mCurrentUri) : null;*/

        if (mCurrentUri.equals(PTAppContract.ClassSubject.CONTENT_URI)) {
            // if showing all sessions, the empty view should say "loading..." because
            // the only reason we would have no sessions at all is if we are currently
            // preparing the database from the bootstrap data, which should only take a few
            // seconds.
            mEmptyView.setVisibility(View.GONE);
            mLoadingView.setVisibility(View.VISIBLE);
        }
        /*else if (ScheduleContract.Sessions.isUnscheduledSessionsInInterval(mCurrentUri)) {
        // Showing sessions in a given interval, so say "No sessions in this time slot."
        mEmptyView.setText(R.string.no_matching_sessions_in_interval);
        mEmptyView.setVisibility(View.VISIBLE);
        mLoadingView.setVisibility(View.GONE);
        } else if (ScheduleContract.Sessions.isSearchUri(mCurrentUri)
            && (TextUtils.isEmpty(searchQuery) || "*".equals(searchQuery))) {
        // Empty search query (for example, user hasn't started to type the query yet),
        // so don't show an empty view.
        mEmptyView.setText("");
        mEmptyView.setVisibility(View.VISIBLE);
        mLoadingView.setVisibility(View.GONE);
        }*/ /*else {
             // Showing sessions as a result of search or filter, so say "No matching sessions."
             mEmptyView.setText(R.string.no_matching_sessions);
             mEmptyView.setVisibility(View.VISIBLE);
             mLoadingView.setVisibility(View.GONE);
             }*/
    }

    // Creates the CollectionView groups based on the cursor data.
    private CollectionView.Inventory prepareInventory() {
        LOGD(TAG, "Preparing collection view inventory.");
        ArrayList<CollectionView.InventoryGroup> pastGroups = new ArrayList<CollectionView.InventoryGroup>();
        ArrayList<CollectionView.InventoryGroup> futureGroups = new ArrayList<CollectionView.InventoryGroup>();
        HashMap<String, CollectionView.InventoryGroup> pastGroupsByName = new HashMap<String, CollectionView.InventoryGroup>();
        HashMap<String, CollectionView.InventoryGroup> futureGroupsByName = new HashMap<String, CollectionView.InventoryGroup>();
        CollectionView.InventoryGroup heroGroup = null;

        mCursor.moveToPosition(-1);
        int nextGroupId = HERO_GROUP_ID + 1000; // to avoid conflict with the special hero group ID
        LOGD(TAG, "Total cursor data items: " + mCursor.getCount());
        int dataIndex = -1;
        final long now = UIUtils.getCurrentTime(mAppContext);
        final boolean conferenceEnded = TimeUtils.hasConferenceEnded(mAppContext);
        LOGD(TAG, "conferenceEnded=" + conferenceEnded);

        final boolean expandedMode = useExpandedMode();
        /*final int displayCols = getResources().getInteger(expandedMode ?
            R.integer.explore_2nd_level_grid_columns : R.integer.explore_1st_level_grid_columns);*/
        final int displayCols = 2;
        LOGD(TAG, "Using " + displayCols + " columns.");
        mPreloader.setDisplayCols(displayCols);

        while (mCursor.moveToNext()) {
            // For each data item, we decide what group it should appear under, then
            // we add it to that group (and create the group if it doesn't exist yet).

            /*long sessionEnd = mCursor.getLong(mCursor.getColumnIndex(
                ScheduleContract.Sessions.SESSION_END));*/
            long sessionEnd = 0;

            ++dataIndex;
            /*boolean showAsPast = !conferenceEnded && sessionEnd < now;*/
            boolean showAsPast = false;
            //for PTApp, it's a teacher status text/tagline
            String groupLabel = "Placeholder to show teacher's status text/tagline...";

            /*if (expandedMode) {
            String tags = mCursor.getString(mCursor.getColumnIndex(ScheduleContract.Sessions.SESSION_TAGS));
            TagMetadata.Tag groupTag = tags == null ? null
                    : mTagMetadata.getSessionGroupTag(tags.split(","));
            if (groupTag != null) {
                groupLabel = groupTag.getName();
            } else {
                groupLabel = getString(R.string.others);
            }
            groupLabel += (showAsPast ? " (" + getString(R.string.session_finished) + ")" : "");
            } else {
            groupLabel = showAsPast ? getString(R.string.ended_sessions) : "";
            }*/

            /*LOGV(TAG, "Data item #" + dataIndex + ", sessionEnd=" + sessionEnd + ", groupLabel="
                + groupLabel + " showAsPast=" + showAsPast);*/

            CollectionView.InventoryGroup group;

            // should this item be the hero group?
            //In this PTApp we dont need to show the top-most single column(full width), so
            /*if (!useExpandedMode() && !showAsPast && heroGroup == null) {
            // yes, this item is the hero
            LOGV(TAG, "This item is the hero.");
            group = heroGroup = new CollectionView.InventoryGroup(HERO_GROUP_ID)
                    .setDisplayCols(1)  // hero item spans all columns
                    .setShowHeader(false).setHeaderLabel("");
            } else {*/
            // "list" and "map" are just shorthand variables pointing to the right list and map
            ArrayList<CollectionView.InventoryGroup> list = showAsPast ? pastGroups : futureGroups;
            HashMap<String, CollectionView.InventoryGroup> map = showAsPast ? pastGroupsByName : futureGroupsByName;

            // Create group, if it doesn't exist yet
            if (!map.containsKey(groupLabel)) {
                LOGV(TAG, "Creating new group: " + groupLabel);
                group = new CollectionView.InventoryGroup(nextGroupId++).setDisplayCols(displayCols)
                        .setShowHeader(false).setHeaderLabel(groupLabel);
                map.put(groupLabel, group);
                list.add(group);
            } else {
                LOGV(TAG, "Adding to existing group: " + groupLabel);
                group = map.get(groupLabel);
            }
            //}

            // add this item to the group
            LOGV(TAG, "...adding to group '" + groupLabel + "' with custom data index " + dataIndex);
            group.addItemWithCustomDataIndex(dataIndex);
        }

        // prepare the final groups list
        ArrayList<CollectionView.InventoryGroup> groups = new ArrayList<CollectionView.InventoryGroup>();
        if (heroGroup != null) {
            groups.add(heroGroup); // start with the hero
        }
        groups.addAll(futureGroups); // then all future events
        groups.addAll(pastGroups); // then all past events
        LOGD(TAG, "Total: hero " + (heroGroup == null ? "absent" : "present") + " " + futureGroups.size()
                + " future groups, " + " " + pastGroups.size() + " past groups, total " + groups.size());

        // the first group doesn't need a header label, because it's the "default group"
        //if (groups.size() > 0) {
        //    groups.get(0).setHeaderLabel("").setShowHeader(false);
        //}

        // finally, assemble the inventory and we're done
        CollectionView.Inventory inventory = new CollectionView.Inventory();
        for (CollectionView.InventoryGroup g : groups) {
            inventory.addGroup(g);
        }
        return inventory;
    }

    @Override
    public View newCollectionHeaderView(Context context, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        return inflater.inflate(R.layout.list_item_explore_header, parent, false);
    }

    @Override
    public void bindCollectionHeaderView(Context context, View view, int groupId, String groupLabel) {
        TextView tv = (TextView) view.findViewById(android.R.id.text1);
        if (tv != null) {
            tv.setText(groupLabel);
        }
    }

    @Override
    public View newCollectionItemView(Context context, int groupId, ViewGroup parent) {
        final LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        int layoutId;

        /*if (useExpandedMode()) {
        layoutId = R.layout.list_item_session;
        } else {
        // Group HERO_GROUP_ID is the hero -- use a larger layout
        layoutId = (groupId == HERO_GROUP_ID) ? R.layout.list_item_session_hero : R.layout.list_item_session_summarized;
        }*/

        layoutId = R.layout.list_item_session_summarized;

        return inflater.inflate(layoutId, parent, false);
    }

    private StringBuilder mBuffer = new StringBuilder();

    private int mMaxDataIndexAnimated = 0;

    @Override
    public void bindCollectionItemView(Context context, View view, final int groupId, int indexInGroup,
            int dataIndex, Object tag) {
        if (mCursor == null || !mCursor.moveToPosition(dataIndex)) {
            LOGW(TAG, "Can't bind collection view item, dataIndex=" + dataIndex
                    + (mCursor == null ? ": cursor is null" : ": bad data index."));
            return;
        }

        Log.v(TAG, "test collection view cursor data: " + mCursor.getString(0) + ", " + mCursor.getString(1) + ", "
                + mCursor.getString(2) + ", " + mCursor.getString(3) + ", ");

        final String groupJid = mCursor.getString(SessionsQuery.GROUP_JID);
        final String classSubjectId = mCursor.getString(SessionsQuery.CLASS_SUBJECT_ID);

        if (classSubjectId == null) {
            return;
        }

        // first, read session info from cursor and put it in convenience variables
        final String courseTitle = mCursor.getString(SessionsQuery.GROUP_NAME);
        /*final String courseTitle = mCursor.getString(SessionsQuery.SUBJECT_TITLE);
        final String className = mCursor.getString(SessionsQuery.CLASS_TITLE)
            + "-" + mCursor.getString(SessionsQuery.SECTION_TITLE);*/
        /*final String educatorId = mCursor.getString(SessionsQuery.EDUCATOR_ID);
        final String classId = mCursor.getString(SessionsQuery.CLASS_ID);
            
        final String studentId = mCursor.getString(SessionsQuery.STUDENT_ID);*/
        /*final String sessionAbstract = mCursor.getString(SessionsQuery.ABSTRACT);
        final long sessionStart = mCursor.getLong(SessionsQuery.SESSION_START);
        final long sessionEnd = mCursor.getLong(SessionsQuery.SESSION_END);
        final String roomName = mCursor.getString(SessionsQuery.ROOM_NAME);
        int sessionColor = mCursor.getInt(SessionsQuery.COLOR);*/
        int sessionColor = 0;
        sessionColor = sessionColor == 0 ? getResources().getColor(R.color.default_session_color) : sessionColor;
        /*final String snippet = mIsSearchCursor ? mCursor.getString(SessionsQuery.SNIPPET) : null;
        final Spannable styledSnippet = mIsSearchCursor ? buildStyledSnippet(snippet) : null;
        final boolean starred = mCursor.getInt(SessionsQuery.IN_MY_SCHEDULE) != 0;
        final String[] tags = mCursor.getString(SessionsQuery.TAGS).split(",");*/

        // now let's compute a few pieces of information from the data, which we will use
        // later to decide what to render where
        /*final boolean hasLivestream = !TextUtils.isEmpty(mCursor.getString(
            SessionsQuery.LIVESTREAM_URL));*/
        final long now = UIUtils.getCurrentTime(context);
        /*final boolean happeningNow = now >= sessionStart && now <= sessionEnd;*/

        // text that says "LIVE" if session is live, or empty if session is not live
        /*final String liveNowText = hasLivestream ? " " + UIUtils.getLiveBadgeText(context,
            sessionStart, sessionEnd) : "";*/
        final String liveNowText = "";

        // get reference to all the views in the layout we will need
        final TextView titleView = (TextView) view.findViewById(R.id.session_title);
        final TextView subtitleView = (TextView) view.findViewById(R.id.session_subtitle);
        final TextView shortSubtitleView = (TextView) view.findViewById(R.id.session_subtitle_short);
        /*final TextView snippetView = (TextView) view.findViewById(R.id.session_snippet);*/
        final TextView abstractView = (TextView) view.findViewById(R.id.session_abstract);
        final TextView categoryView = (TextView) view.findViewById(R.id.session_category);
        final View boxView = view.findViewById(R.id.info_box);
        final View sessionTargetView = view.findViewById(R.id.session_target);
        final View grpmsgView = (ImageView) view.findViewById(R.id.session_grp_msg);

        if (sessionColor == 0) {
            // use default
            sessionColor = mDefaultSessionColor;
        }
        sessionColor = UIUtils.scaleSessionColorToDefaultBG(sessionColor);

        ImageView photoView = (ImageView) view.findViewById(R.id.session_photo_colored);
        if (photoView != null) {
            if (!mPreloader.isDimensSet()) {
                final ImageView finalPhotoView = photoView;
                photoView.post(new Runnable() {
                    @Override
                    public void run() {
                        mPreloader.setDimens(finalPhotoView.getWidth(), finalPhotoView.getHeight());
                    }
                });
            }
            // colored
            photoView.setColorFilter(UIUtils.setColorAlpha(sessionColor, UIUtils.SESSION_PHOTO_SCRIM_ALPHA));
        } else {
            photoView = (ImageView) view.findViewById(R.id.session_photo);
        }
        ((BaseActivity) getActivity()).getLPreviewUtils().setViewName(photoView, "photo_" + classSubjectId);

        // when we load a photo, it will fade in from transparent so the
        // background of the container must be the session color to avoid a white flash
        ViewParent parent = photoView.getParent();
        if (parent != null && parent instanceof View) {
            ((View) parent).setBackgroundColor(sessionColor);
        } else {
            photoView.setBackgroundColor(sessionColor);
        }

        //String photo = mCursor.getString(SessionsQuery.PHOTO_URL);
        int subjPath = R.drawable.nophotoavailable;
        //TODO:Temporary task to generate screenshots
        if (courseTitle != null) {
            if (courseTitle.contains("English")) {
                subjPath = R.drawable.logo_english;
            } else if (courseTitle.contains("Math")) {
                subjPath = R.drawable.logo_math;
            } else if (courseTitle.contains("Punjabi")) {
                subjPath = R.drawable.course_punjabi;
            } else if (courseTitle.contains("Hindi")) {
                subjPath = R.drawable.course_hindi;
            } else if (courseTitle.contains("German")) {
                subjPath = R.drawable.course_german;
            } else if (courseTitle.contains("Dutch")) {
                subjPath = R.drawable.course_dutch;
            } else if (courseTitle.contains("Science")) {
                subjPath = R.drawable.course_science;
            } else if (courseTitle.contains("French")) {
                subjPath = R.drawable.course_french;
            }
        }

        /*if (!TextUtils.isEmpty(photo)) {*/
        //mImageLoader.loadImage(photo, photoView, true /*crop*/);
        Picasso.with(context) //
                .load(subjPath) //
                .placeholder(CommonConstants.LOADING) //
                .error(CommonConstants.ERROR_IMAGE) //
                .fit() //
                .into(photoView);

        /*} else {
        // cleaning the (potentially) recycled photoView, in case this session has no photo:
        photoView.setImageDrawable(null);
        }*/

        // render title
        /*titleView.setText(courseTitle == null ? "?" : courseTitle);*/
        titleView.setText(courseTitle == null ? "?" : courseTitle);

        // render subtitle into either the subtitle view, or the short subtitle view, as available
        if (subtitleView != null) {
            /*subtitleView.setText(UIUtils.formatSessionSubtitle(
                sessionStart, sessionEnd, roomName, mBuffer, context) + liveNowText);*/
            //subtitleView.setText(className == null ? "?" : className);
        } else if (shortSubtitleView != null) {
            //Dummy data
            /*shortSubtitleView.setText("25");*/
            shortSubtitleView.setText(mCursor.getString(SessionsQuery.MEMBER_COUNT));
            shortSubtitleView.setGravity(Gravity.RIGHT);
            /*shortSubtitleView.setText(UIUtils.formatSessionSubtitle(
                sessionStart, sessionEnd, roomName, mBuffer, context, true) + liveNowText);*/
            //shortSubtitleView.setText(className == null ? "?" : className);
        }

        // render category
        if (categoryView != null) {
            /*categoryView.setText(className == null ? "?" : className);*/
        }

        // if a snippet view is available, render the session snippet there.
        /*if (snippetView != null) {
        *//*if (mIsSearchCursor) {
             // render the search snippet into the snippet view
             snippetView.setText(styledSnippet);
           } else {
             // render speaker names and abstracts into the snippet view
             mBuffer.setLength(0);
             if (!TextUtils.isEmpty(speakerNames)) {
                 mBuffer.append(speakerNames).append(". ");
             }
             if (!TextUtils.isEmpty(sessionAbstract)) {
                 mBuffer.append(sessionAbstract);
             }
             snippetView.setText(mBuffer.toString());
           }*//*
               }*/

        if (abstractView != null && !mIsSearchCursor) {
            // render speaker names and abstracts into the abstract view
            mBuffer.setLength(0);
            /*if (!TextUtils.isEmpty(speakerNames)) {
            mBuffer.append(speakerNames).append("\n\n");
            }
            if (!TextUtils.isEmpty(sessionAbstract)) {
            mBuffer.append(sessionAbstract);
            }*/
            abstractView.setText(mBuffer.toString());
        }

        // in expanded mode, the box background color follows the session color
        if (useExpandedMode()) {
            boxView.setBackgroundColor(sessionColor);
        }

        /*// show or hide the "in my schedule" indicator
        view.findViewById(R.id.indicator_in_schedule).setVisibility(starred ? View.VISIBLE
            : View.INVISIBLE);*/

        // if we are in condensed mode and this card is the hero card (big card at the top
        // of the screen), set up the message card if necessary.
        if (!useExpandedMode() && groupId == HERO_GROUP_ID) {
            // this is the hero view, so we might want to show a message card
            final boolean cardShown = setupMessageCard(view);

            // if this is the wide hero layout, show or hide the card or the session abstract
            // view, as appropriate (they are mutually exclusive).
            final View cardContainer = view.findViewById(R.id.message_card_container_wide);
            final View abstractContainer = view.findViewById(R.id.session_abstract);
            if (cardContainer != null && abstractContainer != null) {
                cardContainer.setVisibility(cardShown ? View.VISIBLE : View.GONE);
                abstractContainer.setVisibility(cardShown ? View.GONE : View.VISIBLE);
                abstractContainer.setBackgroundColor(sessionColor);
            }
        }

        // if this session is live right now, display the "LIVE NOW" icon on top of it
        View liveNowBadge = view.findViewById(R.id.live_now_badge);
        if (liveNowBadge != null) {
            liveNowBadge.setVisibility(View.INVISIBLE);
            //liveNowBadge.setVisibility(happeningNow && hasLivestream ? View.VISIBLE : View.GONE);
        }

        // if this view is clicked, open the session details view
        final View finalPhotoView = photoView;
        final int studentGroupId = mCursor.getInt(SessionsQuery.GROUP_ID);

        sessionTargetView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCallbacks.onSessionSelected(classSubjectId, courseTitle, groupJid, studentGroupId, finalPhotoView);
            }
        });

        // animate this card
        if (dataIndex > mMaxDataIndexAnimated) {
            mMaxDataIndexAnimated = dataIndex;
        }

        //if this view is clicked, open group messages chatting screen
        if (grpmsgView != null) {
            grpmsgView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.i(TAG, "grp msg image clicked, opening group messages screen");
                    Intent intent = new Intent(getActivity(), EducatorGroupMsgActivity.class);
                    startActivity(intent);
                }
            });
        }
    }

    private boolean setupMessageCard(View hero) {
        MessageCardView card = (MessageCardView) hero.findViewById(R.id.message_card);
        if (card == null) {
            LOGE(TAG, "Message card not found in UI (R.id.message_card).");
            return false;
        }
        /*if (!PrefUtils.hasAnsweredLocalOrRemote(getActivity()) &&
            !TimeUtils.hasConferenceEnded(getActivity())) {
        // show the "in person" vs "remote" card
        setupLocalOrRemoteCard(card);
        return true;
        } else if (WiFiUtils.shouldOfferToSetupWifi(getActivity(), true)) {
        // show wifi setup card
        setupWifiOfferCard(card);
        return true;
        } else if (PrefUtils.shouldOfferIOExtended(getActivity(), true)) {
        // show the I/O extended card
        setupIOExtendedCard(card);
        return true;*/
        //    }
        else {
            card.setVisibility(View.GONE);
            return false;
        }
    }

    /*private void setupLocalOrRemoteCard(final MessageCardView card) {
    card.overrideBackground(R.drawable.card_bg);
    card.setText(getString(R.string.question_local_or_remote));
    card.setButton(0, getString(R.string.attending_remotely), CARD_ANSWER_ATTENDING_REMOTELY,
            false, 0);
    card.setButton(1, getString(R.string.attending_in_person), CARD_ANSWER_ATTENDING_IN_PERSON,
            true, 0);
    final Context context = getActivity().getApplicationContext();
    final Activity activity = getActivity();
    card.setListener(new MessageCardView.OnMessageCardButtonClicked() {
        @Override
        public void onMessageCardButtonClicked(final String tag) {
            final boolean inPerson = CARD_ANSWER_ATTENDING_IN_PERSON.equals(tag);
            card.dismiss(true);
        
            if (activity != null) {
                Toast.makeText(activity, inPerson ? R.string.explore_attending_in_person_toast
                        : R.string.explore_attending_remotely_toast, Toast.LENGTH_LONG).show();
            }
        
            // post delayed to give card time to animate
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    PrefUtils.setAttendeeAtVenue(context, inPerson);
                    PrefUtils.markAnsweredLocalOrRemote(context);
                }
            }, CARD_DISMISS_ACTION_DELAY);
        }
    });
    card.show();
    }*/

    /*private void setupWifiOfferCard(final MessageCardView card) {
    card.overrideBackground(R.drawable.card_bg);
    card.setText(getString(TimeUtils.hasConferenceStarted(getActivity()) ?
            R.string.question_setup_wifi_after_i_o_start :
            R.string.question_setup_wifi_before_i_o_start));
    card.setButton(0, getString(R.string.no_thanks), CARD_ANSWER_NO,
            false, 0);
    card.setButton(1, getString(R.string.setup_wifi_yes), CARD_ANSWER_YES,
            true, 0);
    final Context context = getActivity().getApplicationContext();
    card.setListener(new MessageCardView.OnMessageCardButtonClicked() {
        @Override
        public void onMessageCardButtonClicked(final String tag) {
            card.dismiss(true);
        
            // post delayed to give card time to animate
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (CARD_ANSWER_YES.equals(tag)) {
                        WiFiUtils.showWiFiDialog(SessionsFragment.this.getActivity());
                    } else {
                        PrefUtils.markDeclinedWifiSetup(context);
                    }
                }
            }, CARD_DISMISS_ACTION_DELAY);
        }
    });
    card.show();
    }
    */
    /*  private void setupIOExtendedCard(final MessageCardView card) {
    card.overrideBackground(R.drawable.card_bg);
    card.setText(getString(R.string.question_i_o_extended));
    card.setButton(0, getString(R.string.no_thanks), CARD_ANSWER_NO,
            false, 0);
    card.setButton(1, getString(R.string.browse_events), CARD_ANSWER_YES,
            true, 0);
    final Context context = getActivity().getApplicationContext();
    card.setListener(new MessageCardView.OnMessageCardButtonClicked() {
        @Override
        public void onMessageCardButtonClicked(final String tag) {
            card.dismiss(true);
        
            // post delayed to give card time to animate
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (CARD_ANSWER_YES.equals(tag)) {
                        Intent intent = new Intent(Intent.ACTION_VIEW,
                                Uri.parse(Config.IO_EXTENDED_LINK));
                        startActivity(intent);
                    }
                    PrefUtils.markDismissedIOExtendedCard(SessionsFragment.this.getActivity());
                }
            }, CARD_DISMISS_ACTION_DELAY);
        }
    });
    card.show();
      }
    */
    private void animateSessionAppear(final View view) {
    }

    private class Preloader extends ListPreloader<String> {

        private int[] photoDimens;
        private int displayCols;

        public Preloader(int maxPreload) {
            super(maxPreload);
        }

        public void setDisplayCols(int displayCols) {
            this.displayCols = displayCols;
        }

        public boolean isDimensSet() {
            return photoDimens != null;
        }

        public void setDimens(int width, int height) {
            if (photoDimens == null) {
                photoDimens = new int[] { width, height };
            }
        }

        @Override
        protected int[] getDimensions(String s) {
            return photoDimens;
        }

        @Override
        protected List<String> getItems(int start, int end) {
            // Our start and end are rows, we need to adjust them into data columns
            // The keynote is 1 row with 1 data item, so we need to adjust.
            int keynoteDataOffset = (displayCols - 1);
            int dataStart = start * displayCols - keynoteDataOffset;
            int dataEnd = end * displayCols - keynoteDataOffset;
            List<String> urls = new ArrayList<String>();
            if (mCursor != null) {
                for (int i = dataStart; i < dataEnd; i++) {
                    if (mCursor.moveToPosition(i)) {
                        /*urls.add(mCursor.getString(SessionsQuery.PHOTO_URL));*/
                    }
                }
            }
            return urls;
        }

        @Override
        protected GenericRequestBuilder getRequestBuilder(String url) {
            return mImageLoader.beginImageLoad(url, null, true /*crop*/);
        }
    }

    /**
     * {@lin com.google.samples.apps.iosched.provider.ScheduleContract.Sessions}
     * query parameters.
     */
    private interface SessionsQuery {
        int NORMAL_TOKEN = 0x1;
        int SEARCH_TOKEN = 0x3;

        //Need to specify the table names alongwith column names, to avoid ambiguity error in 'SELECT' statement
        String[] NORMAL_PROJECTION = { PTAppDatabase.Tables.DEFAULT_GROUPS + "." + BaseColumns._ID,
                PTAppContract.DefaultGroups.GROUP_NAME,
                PTAppDatabase.Tables.DEFAULT_GROUPS + "." + PTAppContract.DefaultGroups.JID,
                PTAppDatabase.Tables.DEFAULT_GROUPS + "." + PTAppContract.DefaultGroups.CLASS_SUBJECT_ID,
                PTAppDatabase.Tables.DEFAULT_GROUPS + "." + PTAppContract.DefaultGroups.MEMBER_COUNT
                /*PTAppDatabase.Tables.STAFF_ENGAGEMENT + "." + PTAppContract.StaffEngagement.CLASS_SUBJECT_ID,
                PTAppContract.Classes.CLASS_TYPE_CODE,
                PTAppContract.Classes.SECTION_TYPE_CODE,
                PTAppContract.Subject.DESCRIPTION,
                PTAppDatabase.Tables.CLASS_SUBJECT + "." + PTAppContract.ClassSubject.GROUP_JID*/
        };

        int GROUP_ID = 0;
        int GROUP_NAME = 1;
        int GROUP_JID = 2;
        int CLASS_SUBJECT_ID = 3;
        int MEMBER_COUNT = 4;
        /*int CLASS_SUBJECT_ID = 0;
        int CLASS_TITLE = 1;
        int SECTION_TITLE = 2;
        int SUBJECT_TITLE = 3;
        int GROUP_JID = 4;*/
    }

    //private static final int TAG_METADATA_TOKEN = 0x4;
}