/* * Copyright 2015 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.session; import android.app.LoaderManager; import android.content.Context; import android.content.CursorLoader; import android.content.Intent; import android.content.Loader; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v4.content.ContextCompat; import android.text.TextUtils; import android.util.Pair; import com.google.common.annotations.VisibleForTesting; import com.google.samples.apps.iosched.Config; import com.google.samples.apps.iosched.R; import com.google.samples.apps.iosched.archframework.ModelWithLoaderManager; import com.google.samples.apps.iosched.archframework.QueryEnum; import com.google.samples.apps.iosched.archframework.UserActionEnum; import com.google.samples.apps.iosched.feedback.SessionFeedbackActivity; import com.google.samples.apps.iosched.model.TagMetadata; import com.google.samples.apps.iosched.provider.ScheduleContract; import com.google.samples.apps.iosched.service.SessionAlarmService; import com.google.samples.apps.iosched.service.SessionCalendarService; import com.google.samples.apps.iosched.util.AccountUtils; import com.google.samples.apps.iosched.util.AnalyticsHelper; import com.google.samples.apps.iosched.util.ExtendedSessionHelper; import com.google.samples.apps.iosched.util.SessionsHelper; import com.google.samples.apps.iosched.util.TimeUtils; import com.google.samples.apps.iosched.util.UIUtils; import java.util.ArrayList; import java.util.List; import java.util.Locale; import static com.google.samples.apps.iosched.util.LogUtils.LOGD; import static com.google.samples.apps.iosched.util.LogUtils.makeLogTag; public class SessionDetailModel extends ModelWithLoaderManager<SessionDetailModel.SessionDetailQueryEnum, SessionDetailModel.SessionDetailUserActionEnum> { protected final static String TAG = makeLogTag(SessionDetailModel.class); /** * The cursor fields for the links. The corresponding resource ids for the links descriptions * are in {@link #LINKS_DESCRIPTION_RESOURCE_IDS}. */ private static final String[] LINKS_CURSOR_FIELDS = { ScheduleContract.Sessions.SESSION_YOUTUBE_URL, ScheduleContract.Sessions.SESSION_MODERATOR_URL, ScheduleContract.Sessions.SESSION_PDF_URL, ScheduleContract.Sessions.SESSION_NOTES_URL }; /** * The resource ids for the links descriptions. The corresponding cursor fields for the links * are in {@link #LINKS_CURSOR_FIELDS}. */ private static final int[] LINKS_DESCRIPTION_RESOURCE_IDS = { R.string.session_link_youtube, R.string.session_link_moderator, R.string.session_link_pdf, R.string.session_link_notes, }; private final Context mContext; private final SessionsHelper mSessionsHelper; private String mSessionId; private Uri mSessionUri; private boolean mSessionLoaded = false; private String mTitle; private String mSubtitle; private int mSessionColor; private String mMainTag; private boolean mInSchedule; private boolean mInScheduleWhenSessionFirstLoaded; private boolean mIsKeynote; private long mSessionStart; private long mSessionEnd; private String mSessionAbstract; private String mHashTag; private String mUrl = ""; private String mRoomId; private String mRoomName; private String mTagsString; private String mLiveStreamId; private String mYouTubeUrl; private String mPhotoUrl; private boolean mHasLiveStream = false; private boolean mLiveStreamVideoWatched = false; private boolean mHasFeedback = false; private String mRequirements; private String mSpeakersNames; private TagMetadata mTagMetadata; /** * Holds a list of links for the session. The first element of the {@code Pair} is the resource * id for the string describing the link, the second is the {@code Intent} to launch when * selecting the link. */ private List<Pair<Integer, Intent>> mLinks = new ArrayList<Pair<Integer, Intent>>(); private List<Speaker> mSpeakers = new ArrayList<Speaker>(); private StringBuilder mBuffer = new StringBuilder(); public SessionDetailModel(Uri sessionUri, Context context, SessionsHelper sessionsHelper, LoaderManager loaderManager) { super(SessionDetailQueryEnum.values(), SessionDetailUserActionEnum.values(), loaderManager); mContext = context; mSessionsHelper = sessionsHelper; mSessionUri = sessionUri; } public String getSessionId() { return mSessionId; } public String getSessionTitle() { return mTitle; } public String getSessionSubtitle() { return mSubtitle; } public String getSessionUrl() { return mUrl; } public String getRoomId() { return mRoomId; } public String getLiveStreamId() { return mLiveStreamId; } public String getYouTubeUrl() { return mYouTubeUrl; } public int getSessionColor() { return mSessionColor; } public boolean isSessionTrackColorAvailable() { return mTagMetadata != null && mMainTag != null; } public int getSessionTrackColor() { if (isSessionTrackColorAvailable()) { final TagMetadata.Tag tag = mTagMetadata.getTag(mMainTag); if (tag != null) { return tag.getColor(); } } // else default to the session color which is defaulted to the theme primary color return mSessionColor; } public String getSessionAbstract() { return mSessionAbstract; } public boolean getLiveStreamVideoWatched() { return mLiveStreamVideoWatched; } public boolean isSessionOngoing() { long currentTimeMillis = TimeUtils.getCurrentTime(mContext); return currentTimeMillis > mSessionStart && currentTimeMillis <= mSessionEnd; } /** * Live stream should be shown if url is available and the session will start in no more than 10 * minutes, or is ongoing or has ended. */ public boolean showLiveStream() { if (!hasLiveStream()) { return false; } long currentTimeMillis = TimeUtils.getCurrentTime(mContext); return currentTimeMillis > mSessionStart - SessionDetailConstants.LIVESTREAM_BEFORE_SESSION_START_MS; } public boolean hasSessionStarted() { long currentTimeMillis = TimeUtils.getCurrentTime(mContext); return currentTimeMillis > mSessionStart; } public boolean hasSessionEnded() { long currentTimeMillis = TimeUtils.getCurrentTime(mContext); return currentTimeMillis > mSessionEnd; } /** * Returns the number of minutes, rounded down, since session has started, or 0 if not started * yet. */ public long minutesSinceSessionStarted() { if (!hasSessionStarted()) { return 0l; } else { long currentTimeMillis = TimeUtils.getCurrentTime(mContext); // Rounded down number of minutes. return (currentTimeMillis - mSessionStart) / 60000; } } /** * Returns the number of minutes, rounded up, until session stars, or 0 if already started. */ public long minutesUntilSessionStarts() { if (hasSessionStarted()) { return 0l; } else { long currentTimeMillis = TimeUtils.getCurrentTime(mContext); int minutes = (int) ((mSessionStart - currentTimeMillis) / 60000); // Rounded up number of minutes. return minutes * 60000 < (mSessionStart - currentTimeMillis) ? minutes + 1 : minutes; } } public long minutesUntilSessionEnds() { if(hasSessionEnded()) { // If session has ended, return 0 minutes until end of session. return 0l; } else { long currentTimeMillis = TimeUtils.getCurrentTime(mContext); int minutes = (int) ((mSessionEnd - currentTimeMillis) / 60000); // Rounded up number of minutes. return minutes * 60000 < (mSessionEnd - currentTimeMillis) ? minutes + 1 : minutes; } } public boolean shouldShowExtendedSessionLink() { // If display of link is conditional, place conditions here. // For instance if it should only be shown during a session, use isSessionOngoing(). return ExtendedSessionHelper.shouldShowExtendedSessionLink(); } public boolean isSessionReadyForFeedback() { long currentTimeMillis = TimeUtils.getCurrentTime(mContext); return currentTimeMillis > mSessionEnd - SessionDetailConstants.FEEDBACK_MILLIS_BEFORE_SESSION_END_MS; } public boolean hasLiveStream() { return mHasLiveStream || (!TextUtils.isEmpty(mYouTubeUrl) && !mYouTubeUrl.equals("null")); } /** * Show header image if it has a photo url and it is keynote session, or has a youTube * url or has a live stream for a session that is about to start, is ongoing, or has ended. */ public boolean shouldShowHeaderImage() { boolean hasYouTubeUrl = !TextUtils.isEmpty(mYouTubeUrl) && !mYouTubeUrl.equals("null"); return hasPhotoUrl() && (isKeynote() || hasYouTubeUrl || showLiveStream()); } public boolean isInSchedule() { return mInSchedule; } public boolean isInScheduleWhenSessionFirstLoaded() { return mInScheduleWhenSessionFirstLoaded; } public boolean isKeynote() { return mIsKeynote; } public boolean hasFeedback() { return mHasFeedback; } public boolean hasPhotoUrl() { return !TextUtils.isEmpty(mPhotoUrl); } public String getPhotoUrl() { return mPhotoUrl; } public String getRequirements() { return mRequirements; } public String getHashTag() { return mHashTag; } public TagMetadata getTagMetadata() { return mTagMetadata; } public String getTagsString() { return mTagsString; } public List<Pair<Integer, Intent>> getLinks() { return mLinks; } public List<Speaker> getSpeakers() { return mSpeakers; } public boolean hasSummaryContent() { return !TextUtils.isEmpty(mSessionAbstract) || !TextUtils.isEmpty(mRequirements); } @Override public boolean readDataFromCursor(Cursor cursor, SessionDetailQueryEnum query) { boolean success = false; if (cursor != null && cursor.moveToFirst()) { if (SessionDetailQueryEnum.SESSIONS == query) { readDataFromSessionCursor(cursor); mSessionLoaded = true; success = true; } else if (SessionDetailQueryEnum.TAG_METADATA == query) { readDataFromTagMetadataCursor(cursor); success = true; } else if (SessionDetailQueryEnum.FEEDBACK == query) { readDataFromFeedbackCursor(cursor); success = true; } else if (SessionDetailQueryEnum.SPEAKERS == query) { readDataFromSpeakersCursor(cursor); success = true; } else if (SessionDetailQueryEnum.MY_VIEWED_VIDEOS == query) { readDataFromMyViewedVideosCursor(cursor); success = true; } } return success; } private void readDataFromMyViewedVideosCursor(Cursor cursor) { String videoID = cursor.getString(cursor.getColumnIndex( ScheduleContract.MyViewedVideos.VIDEO_ID)); if (videoID != null && videoID.equals(mLiveStreamId)) { mLiveStreamVideoWatched = true; } } private void readDataFromSessionCursor(Cursor cursor) { mTitle = cursor.getString(cursor.getColumnIndex( ScheduleContract.Sessions.SESSION_TITLE)); mInSchedule = cursor.getInt(cursor.getColumnIndex( ScheduleContract.Sessions.SESSION_IN_MY_SCHEDULE)) != 0; if (!mSessionLoaded) { mInScheduleWhenSessionFirstLoaded = mInSchedule; } mTagsString = cursor .getString(cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_TAGS)); mIsKeynote = mTagsString != null && mTagsString.contains(Config.Tags.SPECIAL_KEYNOTE); mSessionColor = cursor.getInt( cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_COLOR)); if (mSessionColor == 0) { mSessionColor = ContextCompat.getColor(mContext, R.color.default_session_color); } else { mSessionColor = UIUtils.setColorOpaque(mSessionColor); } mMainTag = cursor .getString(cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_MAIN_TAG)); mLiveStreamId = cursor .getString(cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_LIVESTREAM_ID)); mHasLiveStream = !TextUtils.isEmpty(mLiveStreamId); mYouTubeUrl = cursor.getString(cursor.getColumnIndex( ScheduleContract.Sessions.SESSION_YOUTUBE_URL)); mSessionStart = cursor .getLong(cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_START)); mSessionEnd = cursor.getLong(cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_END)); mRoomName = cursor.getString(cursor.getColumnIndex(ScheduleContract.Sessions.ROOM_NAME)); mRoomId = cursor.getString(cursor.getColumnIndex(ScheduleContract.Sessions.ROOM_ID)); mHashTag = cursor .getString(cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_HASHTAG)); mPhotoUrl = cursor.getString( cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_PHOTO_URL)); mUrl = cursor.getString(cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_URL)); mSessionAbstract = cursor .getString(cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_ABSTRACT)); mSpeakersNames = cursor .getString(cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_SPEAKER_NAMES)); mRequirements = cursor .getString(cursor.getColumnIndex(ScheduleContract.Sessions.SESSION_REQUIREMENTS)); formatSubtitle(); buildLinks(cursor); } @VisibleForTesting public void formatSubtitle() { mSubtitle = UIUtils.formatSessionSubtitle( mSessionStart, mSessionEnd, mRoomName, mBuffer, mContext); if (mHasLiveStream) { mSubtitle += " " + UIUtils.getLiveBadgeText(mContext, mSessionStart, mSessionEnd); } } @VisibleForTesting public String getExtendedSessionUrl() { return ExtendedSessionHelper.getExtendedSessionUrl(this); } private void buildLinks(Cursor cursor) { mLinks.clear(); if (hasLiveStream() && isSessionOngoing()) { mLinks.add(new Pair<Integer, Intent>( R.string.session_link_livestream, getWatchLiveIntent())); } if (!hasFeedback() && isSessionReadyForFeedback()) { mLinks.add(new Pair<Integer, Intent>( R.string.session_feedback_submitlink, getFeedbackIntent() )); } for (int i = 0; i < LINKS_CURSOR_FIELDS.length; i++) { final String linkUrl = cursor.getString(cursor.getColumnIndex(LINKS_CURSOR_FIELDS[i])); if (TextUtils.isEmpty(linkUrl)) { continue; } mLinks.add(new Pair<Integer, Intent>( LINKS_DESCRIPTION_RESOURCE_IDS[i], new Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl)) .addFlags(getFlagForUrlLink()) )); } } private int getFlagForUrlLink() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { return Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET; } else { return Intent.FLAG_ACTIVITY_NEW_DOCUMENT; } } public Intent getWatchLiveIntent() { final String youtubeLink = String.format( Locale.US, Config.VIDEO_LIBRARY_URL_FMT, TextUtils.isEmpty(mLiveStreamId) ? "" : mLiveStreamId ); return new Intent(Intent.ACTION_VIEW, Uri.parse(youtubeLink)); } public Intent getFeedbackIntent() { return new Intent(Intent.ACTION_VIEW, mSessionUri, mContext, SessionFeedbackActivity.class); } private void readDataFromTagMetadataCursor(Cursor cursor) { mTagMetadata = new TagMetadata(cursor); } private void readDataFromFeedbackCursor(Cursor cursor) { mHasFeedback = cursor.getCount() > 0; } private void readDataFromSpeakersCursor(Cursor cursor) { mSpeakers.clear(); // Not using while(cursor.moveToNext()) because it would lead to issues when writing tests. // Either we would mock cursor.moveToNext() to return true and the test would have infinite // loop, or we would mock cursor.moveToNext() to return false, and the test would be for an // empty cursor. int count = cursor.getCount(); for (int i = 0; i < count; i++) { cursor.moveToPosition(i); final String speakerName = cursor.getString(cursor.getColumnIndex(ScheduleContract.Speakers.SPEAKER_NAME)); if (TextUtils.isEmpty(speakerName)) { continue; } final String speakerImageUrl = cursor.getString( cursor.getColumnIndex(ScheduleContract.Speakers.SPEAKER_IMAGE_URL)); final String speakerCompany = cursor.getString( cursor.getColumnIndex(ScheduleContract.Speakers.SPEAKER_COMPANY)); final String speakerUrl = cursor.getString( cursor.getColumnIndex(ScheduleContract.Speakers.SPEAKER_URL)); final String speakerPlusoneUrl = cursor.getString( cursor.getColumnIndex(ScheduleContract.Speakers.SPEAKER_PLUSONE_URL)); final String speakerTwitterUrl = cursor.getString( cursor.getColumnIndex(ScheduleContract.Speakers.SPEAKER_TWITTER_URL)); final String speakerAbstract = cursor.getString( cursor.getColumnIndex(ScheduleContract.Speakers.SPEAKER_ABSTRACT)); mSpeakers.add(new Speaker(speakerName, speakerImageUrl, speakerCompany, speakerUrl, speakerPlusoneUrl, speakerTwitterUrl, speakerAbstract)); } } @Override public Loader<Cursor> createCursorLoader(SessionDetailQueryEnum query, Bundle args) { CursorLoader loader = null; if (query == null) { return loader; } switch (query) { case SESSIONS: mSessionId = getSessionId(mSessionUri); loader = getCursorLoaderInstance(mContext, mSessionUri, SessionDetailQueryEnum.SESSIONS.getProjection(), null, null, null); break; case SPEAKERS: Uri speakersUri = getSpeakersDirUri(mSessionId); loader = getCursorLoaderInstance(mContext, speakersUri, SessionDetailQueryEnum.SPEAKERS.getProjection(), null, null, ScheduleContract.Speakers.DEFAULT_SORT); break; case FEEDBACK: Uri feedbackUri = getFeedbackUri(mSessionId); loader = getCursorLoaderInstance(mContext, feedbackUri, SessionDetailQueryEnum.FEEDBACK.getProjection(), null, null, null); break; case TAG_METADATA: loader = getTagMetadataLoader(); break; case MY_VIEWED_VIDEOS: Uri myPlayedVideoUri = ScheduleContract.MyViewedVideos.buildMyViewedVideosUri( AccountUtils.getActiveAccountName(mContext)); loader = getCursorLoaderInstance(mContext, myPlayedVideoUri, SessionDetailQueryEnum.MY_VIEWED_VIDEOS.getProjection(), null, null, null); break; } return loader; } @VisibleForTesting public CursorLoader getCursorLoaderInstance(Context context, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return new CursorLoader(context, uri, projection, selection, selectionArgs, sortOrder); } @VisibleForTesting public CursorLoader getTagMetadataLoader() { return TagMetadata.createCursorLoader(mContext); } @VisibleForTesting public Uri getFeedbackUri(String sessionId) { return ScheduleContract.Feedback.buildFeedbackUri(sessionId); } @VisibleForTesting public Uri getSpeakersDirUri(String sessionId) { return ScheduleContract.Sessions.buildSpeakersDirUri(sessionId); } @VisibleForTesting public String getSessionId(Uri uri) { return ScheduleContract.Sessions.getSessionId(uri); } @Override public void processUserAction(SessionDetailUserActionEnum action, @android.support.annotation.Nullable Bundle args, UserActionCallback callback) { switch (action) { case STAR: mInSchedule = true; mSessionsHelper.setSessionStarred(mSessionUri, true, null); amendCalendarAndSetUpNotificationIfRequired(); sendAnalyticsEventForStarUnstarSession(true); callback.onModelUpdated(this, action); break; case UNSTAR: mInSchedule = false; mSessionsHelper.setSessionStarred(mSessionUri, false, null); amendCalendarAndSetUpNotificationIfRequired(); sendAnalyticsEventForStarUnstarSession(false); callback.onModelUpdated(this, action); break; case SHOW_MAP: // ANALYTICS EVENT: Click on Map action in Session Details page. // Contains: Session title/subtitle sendAnalyticsEvent("Session", "Map", mTitle); callback.onModelUpdated(this, action); break; case SHOW_SHARE: // ANALYTICS EVENT: Share a session. // Contains: Session title. sendAnalyticsEvent("Session", "Shared", mTitle); callback.onModelUpdated(this, action); break; case GIVE_FEEDBACK: // ANALYTICS EVENT: Click on the "send feedback" action in Session Details. // Contains: The session title. sendAnalyticsEvent("Session", "Feedback", getSessionTitle()); callback.onModelUpdated(this, action); break; case EXTENDED: // ANALYTICS EVENT: Click on the extended session link in Session Details. sendAnalyticsEvent("Session", "Extended Session", getSessionTitle()); callback.onModelUpdated(this, action); break; default: callback.onError(action); } } private void amendCalendarAndSetUpNotificationIfRequired() { if (!hasSessionStarted()) { Intent intent; if (mInSchedule) { intent = new Intent(SessionCalendarService.ACTION_ADD_SESSION_CALENDAR, mSessionUri); intent.putExtra(SessionCalendarService.EXTRA_SESSION_START, mSessionStart); intent.putExtra(SessionCalendarService.EXTRA_SESSION_END, mSessionEnd); intent.putExtra(SessionCalendarService.EXTRA_SESSION_ROOM, mRoomName); intent.putExtra(SessionCalendarService.EXTRA_SESSION_TITLE, mTitle); } else { intent = new Intent(SessionCalendarService.ACTION_REMOVE_SESSION_CALENDAR, mSessionUri); intent.putExtra(SessionCalendarService.EXTRA_SESSION_START, mSessionStart); intent.putExtra(SessionCalendarService.EXTRA_SESSION_END, mSessionEnd); intent.putExtra(SessionCalendarService.EXTRA_SESSION_TITLE, mTitle); } intent.setClass(mContext, SessionCalendarService.class); mContext.startService(intent); if (mInSchedule) { setUpNotification(); } } } private void setUpNotification() { Intent scheduleIntent; // Schedule session notification if (!hasSessionStarted()) { LOGD(TAG, "Scheduling notification about session start."); scheduleIntent = new Intent( SessionAlarmService.ACTION_SCHEDULE_STARRED_BLOCK, null, mContext, SessionAlarmService.class); scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_START, mSessionStart); scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_END, mSessionEnd); mContext.startService(scheduleIntent); } else { LOGD(TAG, "Not scheduling notification about session start, too late."); } // Schedule feedback notification if (!hasSessionEnded()) { LOGD(TAG, "Scheduling notification about session feedback."); scheduleIntent = new Intent( SessionAlarmService.ACTION_SCHEDULE_FEEDBACK_NOTIFICATION, null, mContext, SessionAlarmService.class); scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_ID, mSessionId); scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_START, mSessionStart); scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_END, mSessionEnd); scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_TITLE, mTitle); mContext.startService(scheduleIntent); } else { LOGD(TAG, "Not scheduling feedback notification, too late."); } } @VisibleForTesting public void sendAnalyticsEvent(String category, String action, String label) { AnalyticsHelper.sendEvent(category, action, label); } private void sendAnalyticsEventForStarUnstarSession(boolean starred) { // ANALYTICS EVENT: Add or remove a session from My Schedule (starred vs unstarred) // Contains: Session title, whether it was added or removed (starred or unstarred) sendAnalyticsEvent("Session", starred ? "Starred" : "Unstarred", mTitle); } @Override public void cleanUp() { // Nothing to clean up } public static class Speaker { private String mName; private String mImageUrl; private String mCompany; private String mUrl; private String mPlusoneUrl; private String mTwitterUrl; private String mAbstract; public Speaker(String name, String imageUrl, String company, String url, String plusoneUrl, String twitterUrl, String anAbstract) { mName = name; mImageUrl = imageUrl; mCompany = company; mUrl = url; mPlusoneUrl = plusoneUrl; mTwitterUrl = twitterUrl; mAbstract = anAbstract; } public String getName() { return mName; } public String getImageUrl() { return mImageUrl; } public String getCompany() { return mCompany; } public String getUrl() { return mUrl; } public String getPlusoneUrl() { return mPlusoneUrl; } public String getTwitterUrl() { return mTwitterUrl; } public String getAbstract() { return mAbstract; } } public enum SessionDetailQueryEnum implements QueryEnum { SESSIONS(0, new String[]{ScheduleContract.Sessions.SESSION_START, ScheduleContract.Sessions.SESSION_END, ScheduleContract.Sessions.SESSION_LEVEL, ScheduleContract.Sessions.SESSION_TITLE, ScheduleContract.Sessions.SESSION_ABSTRACT, ScheduleContract.Sessions.SESSION_REQUIREMENTS, ScheduleContract.Sessions.SESSION_IN_MY_SCHEDULE, ScheduleContract.Sessions.SESSION_HASHTAG, ScheduleContract.Sessions.SESSION_URL, ScheduleContract.Sessions.SESSION_YOUTUBE_URL, ScheduleContract.Sessions.SESSION_PDF_URL, ScheduleContract.Sessions.SESSION_NOTES_URL, ScheduleContract.Sessions.SESSION_LIVESTREAM_ID, ScheduleContract.Sessions.SESSION_MODERATOR_URL, ScheduleContract.Sessions.ROOM_ID, ScheduleContract.Rooms.ROOM_NAME, ScheduleContract.Sessions.SESSION_COLOR, ScheduleContract.Sessions.SESSION_PHOTO_URL, ScheduleContract.Sessions.SESSION_RELATED_CONTENT, ScheduleContract.Sessions.SESSION_TAGS, ScheduleContract.Sessions.SESSION_SPEAKER_NAMES, ScheduleContract.Sessions.SESSION_MAIN_TAG}), SPEAKERS(1, new String[]{ScheduleContract.Speakers.SPEAKER_NAME, ScheduleContract.Speakers.SPEAKER_IMAGE_URL, ScheduleContract.Speakers.SPEAKER_COMPANY, ScheduleContract.Speakers.SPEAKER_ABSTRACT, ScheduleContract.Speakers.SPEAKER_URL, ScheduleContract.Speakers.SPEAKER_PLUSONE_URL, ScheduleContract.Speakers.SPEAKER_TWITTER_URL}), FEEDBACK(2, new String[]{ScheduleContract.Feedback.SESSION_ID}), TAG_METADATA(3, null), MY_VIEWED_VIDEOS(4, new String[]{ScheduleContract.MyViewedVideos.VIDEO_ID}); private int id; private String[] projection; SessionDetailQueryEnum(int id, String[] projection) { this.id = id; this.projection = projection; } @Override public int getId() { return id; } @Override public String[] getProjection() { return projection; } } public enum SessionDetailUserActionEnum implements UserActionEnum { STAR(1), UNSTAR(2), SHOW_MAP(3), SHOW_SHARE(4), GIVE_FEEDBACK(5), EXTENDED(6); private int id; SessionDetailUserActionEnum(int id) { this.id = id; } @Override public int getId() { return id; } } }