/* * 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.google.samples.apps.iosched.model; import android.content.Context; import android.database.Cursor; import android.os.AsyncTask; import android.text.TextUtils; import android.util.Log; import com.google.samples.apps.iosched.BuildConfig; import com.google.samples.apps.iosched.R; import com.google.samples.apps.iosched.provider.ScheduleContract; import com.google.samples.apps.iosched.provider.ScheduleContract.Blocks; import com.google.samples.apps.iosched.provider.ScheduleContract.Sessions; import com.google.samples.apps.iosched.ui.MyScheduleAdapter; import com.google.samples.apps.iosched.util.AccountUtils; import com.google.samples.apps.iosched.util.PrefUtils; import com.google.samples.apps.iosched.util.UIUtils; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import static com.google.samples.apps.iosched.util.LogUtils.LOGD; import static com.google.samples.apps.iosched.util.LogUtils.makeLogTag; public class ScheduleHelper { private static final String TAG = makeLogTag(ScheduleHelper.class); private Context mContext; public ScheduleHelper(Context context) { this.mContext = context; } public ArrayList<ScheduleItem> getScheduleData(long start, long end) { // get sessions in my schedule and blocks, starting anytime in the conference day ArrayList<ScheduleItem> mutableItems = new ArrayList<ScheduleItem>(); ArrayList<ScheduleItem> immutableItems = new ArrayList<ScheduleItem>(); addBlocks(start, end, mutableItems, immutableItems); addSessions(start, end, mutableItems, immutableItems); ArrayList<ScheduleItem> result = ScheduleItemHelper.processItems(mutableItems, immutableItems); if (BuildConfig.DEBUG || Log.isLoggable(TAG, Log.DEBUG)) { ScheduleItem previous = null; for (ScheduleItem item: result) { if ((item.flags & ScheduleItem.FLAG_CONFLICTS_WITH_PREVIOUS) != 0) { Log.d(TAG, "Schedule Item conflicts with previous. item="+item+" previous="+previous); } previous = item; } } setSessionCounters(result, start, end); return result; } /** * Fill the number of sessions for FREE blocks: */ protected void setSessionCounters(ArrayList<ScheduleItem> items, long dayStart, long dayEnd) { ArrayList<ScheduleItem> free = new ArrayList<ScheduleItem>(); for (ScheduleItem item: items) { if (item.type == ScheduleItem.FREE) { free.add(item); } } if (free.isEmpty()) { return; } // Count number of start/end pairs for sessions that are between dayStart and dayEnd and // are not in my schedule: String liveStreamedOnlySelection = UIUtils.shouldShowLiveSessionsOnly(mContext) ? "AND IFNULL(" + ScheduleContract.Sessions.SESSION_LIVESTREAM_URL + ",'')!=''" : ""; Cursor cursor = mContext.getContentResolver().query( ScheduleContract.Sessions.buildCounterByIntervalUri(), SessionsCounterQuery.PROJECTION, Sessions.SESSION_START + ">=? AND "+Sessions.SESSION_START + "<=? AND "+ Sessions.SESSION_IN_MY_SCHEDULE + " = 0 "+liveStreamedOnlySelection, new String[]{String.valueOf(dayStart), String.valueOf(dayEnd)}, null); while (cursor.moveToNext()) { long start = cursor.getLong(SessionsCounterQuery.SESSION_INTERVAL_START); int counter = cursor.getInt(SessionsCounterQuery.SESSION_INTERVAL_COUNT); // Find blocks that this interval applies. for (ScheduleItem item: free) { // If grouped sessions starts and ends inside the free block, it is considered in it: if (item.startTime <= start && start < item.endTime) { item.numOfSessions += counter; } } } cursor.close(); // remove free blocks that have no available sessions or that are in the past long now = UIUtils.getCurrentTime(mContext); Iterator<ScheduleItem> it = items.iterator(); while (it.hasNext()) { ScheduleItem i = it.next(); if (i.type == ScheduleItem.FREE) { if (i.endTime < now) { LOGD(TAG, "Removing empty block in the past."); it.remove(); } else if (i.numOfSessions == 0) { LOGD(TAG, "Removing block with zero sessions: " + new Date(i.startTime) + "-" + new Date(i.endTime)); it.remove(); } else { i.subtitle = mContext.getResources().getQuantityString( R.plurals.schedule_block_subtitle, i.numOfSessions, i.numOfSessions); } } } } public void getScheduleDataAsync(final MyScheduleAdapter adapter, long start, long end) { new AsyncTask<Long, Void, ArrayList<ScheduleItem>>() { @Override protected ArrayList<ScheduleItem> doInBackground(Long... params) { Long start = params[0]; Long end = params[1]; return getScheduleData(start, end); } @Override protected void onPostExecute(ArrayList<ScheduleItem> scheduleItems) { adapter.updateItems(scheduleItems); } }.execute(start, end); } protected void addSessions(long start, long end, ArrayList<ScheduleItem> mutableItems, ArrayList<ScheduleItem> immutableItems) { Cursor cursor = mContext.getContentResolver().query( ScheduleContract.addOverrideAccountName(Sessions.CONTENT_MY_SCHEDULE_URI, AccountUtils.getActiveAccountName(mContext)), SessionsQuery.PROJECTION, // filter sessions to the specified day Sessions.STARTING_AT_TIME_INTERVAL_SELECTION, new String[]{String.valueOf(start), String.valueOf(end)}, // order by session start Sessions.SESSION_START); while (cursor.moveToNext()) { ScheduleItem item = new ScheduleItem(); item.type = ScheduleItem.SESSION; item.sessionId = cursor.getString(SessionsQuery.SESSION_ID); item.title = cursor.getString(SessionsQuery.SESSION_TITLE); item.startTime = cursor.getLong(SessionsQuery.SESSION_START); item.endTime = cursor.getLong(SessionsQuery.SESSION_END); if (!TextUtils.isEmpty(cursor.getString(SessionsQuery.SESSION_LIVESTREAM_URL))) { item.flags |= ScheduleItem.FLAG_HAS_LIVESTREAM; } item.subtitle = UIUtils.formatSessionSubtitle( cursor.getString(SessionsQuery.ROOM_ROOM_NAME), cursor.getString(SessionsQuery.SESSION_SPEAKER_NAMES), mContext); item.backgroundImageUrl = cursor.getString(SessionsQuery.SESSION_PHOTO_URL); item.backgroundColor = cursor.getInt(SessionsQuery.SESSION_COLOR); item.hasGivenFeedback = (cursor.getInt(SessionsQuery.HAS_GIVEN_FEEDBACK) > 0); immutableItems.add(item); } cursor.close();; } protected void addBlocks(long start, long end, ArrayList<ScheduleItem> mutableItems, ArrayList<ScheduleItem> immutableItems) { Cursor cursor = mContext.getContentResolver().query( Blocks.CONTENT_URI, BlocksQuery.PROJECTION, // filter sessions on the specified day Blocks.BLOCK_START + " >= ? and " + Blocks.BLOCK_START + " <= ?", new String[]{String.valueOf(start), String.valueOf(end)}, // order by session start Blocks.BLOCK_START); while (cursor.moveToNext()) { ScheduleItem item = new ScheduleItem(); item.setTypeFromBlockType(cursor.getString(BlocksQuery.BLOCK_TYPE)); item.title = cursor.getString(BlocksQuery.BLOCK_TITLE); item.subtitle = cursor.getString(BlocksQuery.BLOCK_SUBTITLE); item.startTime = cursor.getLong(BlocksQuery.BLOCK_START); item.endTime = cursor.getLong(BlocksQuery.BLOCK_END); // Hide BREAK blocks to remote attendees (b/14666391): if (item.type == ScheduleItem.BREAK && !PrefUtils.isAttendeeAtVenue(mContext)) { continue; } // Currently, only type=FREE is mutable if (item.type == ScheduleItem.FREE) { mutableItems.add(item); } else { immutableItems.add(item); item.flags |= ScheduleItem.FLAG_NOT_REMOVABLE; } } } private interface SessionsQuery { String[] PROJECTION = { Sessions.SESSION_ID, Sessions.SESSION_TITLE, Sessions.SESSION_START, Sessions.SESSION_END, ScheduleContract.Rooms.ROOM_NAME, Sessions.SESSION_IN_MY_SCHEDULE, Sessions.SESSION_LIVESTREAM_URL, Sessions.SESSION_SPEAKER_NAMES, Sessions.SESSION_PHOTO_URL, Sessions.SESSION_COLOR, Sessions.HAS_GIVEN_FEEDBACK, }; int SESSION_ID = 0; int SESSION_TITLE = 1; int SESSION_START = 2; int SESSION_END = 3; int ROOM_ROOM_NAME = 4; int SESSION_LIVESTREAM_URL = 6; int SESSION_SPEAKER_NAMES = 7; int SESSION_PHOTO_URL = 8; int SESSION_COLOR = 9; int HAS_GIVEN_FEEDBACK = 10; } private interface BlocksQuery { String[] PROJECTION = { Blocks.BLOCK_TITLE, Blocks.BLOCK_TYPE, Blocks.BLOCK_START, Blocks.BLOCK_END, Blocks.BLOCK_SUBTITLE }; int BLOCK_TITLE = 0; int BLOCK_TYPE= 1; int BLOCK_START = 2; int BLOCK_END = 3; int BLOCK_SUBTITLE = 4; } private interface SessionsCounterQuery { String[] PROJECTION = { Sessions.SESSION_START, Sessions.SESSION_END, Sessions.SESSION_INTERVAL_COUNT, Sessions.SESSION_IN_MY_SCHEDULE, }; int SESSION_INTERVAL_START = 0; int SESSION_INTERVAL_END = 1; int SESSION_INTERVAL_COUNT = 2; } }