| /* |
| * 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.ui; |
| |
| import android.app.LoaderManager; |
| import android.content.ActivityNotFoundException; |
| import android.content.Context; |
| import android.content.CursorLoader; |
| import android.content.Intent; |
| import android.content.Loader; |
| import android.content.res.Resources; |
| import android.database.Cursor; |
| import android.graphics.Paint; |
| import android.graphics.drawable.ShapeDrawable; |
| import android.graphics.drawable.shapes.OvalShape; |
| import android.net.Uri; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.support.v4.view.ViewCompat; |
| import android.support.v7.widget.Toolbar; |
| import android.text.TextUtils; |
| import android.util.Pair; |
| import android.util.TypedValue; |
| import android.view.LayoutInflater; |
| import android.view.Menu; |
| import android.view.MenuItem; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.ViewTreeObserver; |
| import android.view.WindowManager; |
| import android.widget.ImageView; |
| import android.widget.LinearLayout; |
| import android.widget.TextView; |
| |
| import com.bumptech.glide.request.bitmap.RequestListener; |
| import com.bumptech.glide.request.target.Target; |
| import com.google.android.gms.plus.PlusOneButton; |
| import com.google.android.youtube.player.YouTubeIntents; |
| import com.google.samples.apps.iosched.Config; |
| import com.google.samples.apps.iosched.R; |
| 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.ui.widget.CheckableFrameLayout; |
| import com.google.samples.apps.iosched.ui.widget.MessageCardView; |
| import com.google.samples.apps.iosched.ui.widget.ObservableScrollView; |
| import com.google.samples.apps.iosched.util.AccountUtils; |
| import com.google.samples.apps.iosched.util.AnalyticsManager; |
| import com.google.samples.apps.iosched.util.BeamUtils; |
| import com.google.samples.apps.iosched.util.ImageLoader; |
| import com.google.samples.apps.iosched.util.LogUtils; |
| 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.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| |
| import static com.google.samples.apps.iosched.util.LogUtils.LOGD; |
| |
| /** |
| * An activity that shows detail information for a session, including session title, abstract, |
| * time information, speaker photos and bios, etc. |
| */ |
| public class SessionDetailActivity extends BaseActivity implements |
| LoaderManager.LoaderCallbacks<Cursor>, |
| ObservableScrollView.Callbacks { |
| private static final String TAG = LogUtils.makeLogTag(SessionDetailActivity.class); |
| |
| private static final int[] SECTION_HEADER_RES_IDS = { |
| R.id.session_links_header, |
| R.id.session_speakers_header, |
| R.id.session_requirements_header, |
| R.id.related_videos_header, |
| }; |
| private static final float PHOTO_ASPECT_RATIO = 1.7777777f; |
| |
| public static final String TRANSITION_NAME_PHOTO = "photo"; |
| |
| private Handler mHandler = new Handler(); |
| private static final int TIME_HINT_UPDATE_INTERVAL = 10000; // 10 sec |
| |
| private TagMetadata mTagMetadata; |
| |
| private String mSessionId; |
| private Uri mSessionUri; |
| |
| private long mSessionStart; |
| private long mSessionEnd; |
| private String mTitleString; |
| private String mHashTag; |
| private String mUrl; |
| private String mRoomId; |
| private String mRoomName; |
| private String mTagsString; |
| |
| // A comma-separated list of speakers to be passed to Android Wear |
| private String mSpeakers; |
| |
| private boolean mStarred; |
| private boolean mInitStarred; |
| private boolean mDismissedWatchLivestreamCard = false; |
| private boolean mHasLivestream = false; |
| private MenuItem mSocialStreamMenuItem; |
| private MenuItem mShareMenuItem; |
| |
| private View mScrollViewChild; |
| private TextView mTitle; |
| private TextView mSubtitle; |
| private PlusOneButton mPlusOneButton; |
| |
| private ObservableScrollView mScrollView; |
| private CheckableFrameLayout mAddScheduleButton; |
| |
| private TextView mAbstract; |
| private LinearLayout mTags; |
| private ViewGroup mTagsContainer; |
| private TextView mRequirements; |
| private View mHeaderBox; |
| private View mDetailsContainer; |
| |
| private boolean mSessionCursor = false; |
| private boolean mSpeakersCursor = false; |
| private boolean mHasSummaryContent = false; |
| |
| private ImageLoader mSpeakersImageLoader, mNoPlaceholderImageLoader; |
| private List<Runnable> mDeferredUiOperations = new ArrayList<Runnable>(); |
| |
| private StringBuilder mBuffer = new StringBuilder(); |
| |
| private int mPhotoHeightPixels; |
| private int mHeaderHeightPixels; |
| private int mAddScheduleButtonHeightPixels; |
| |
| private boolean mHasPhoto; |
| private View mPhotoViewContainer; |
| private ImageView mPhotoView; |
| private int mSessionColor; |
| private String mLivestreamUrl; |
| |
| private Runnable mTimeHintUpdaterRunnable = null; |
| |
| private boolean mAlreadyGaveFeedback = false; |
| private boolean mIsKeynote = false; |
| |
| // this set stores the session IDs for which the user has dismissed the |
| // "give feedback" card. This information is kept for the duration of the app's execution |
| // so that if they say "No, thanks", we don't show the card again for that session while |
| // the app is still executing. |
| private static HashSet<String> sDismissedFeedbackCard = new HashSet<String>(); |
| |
| private TextView mSubmitFeedbackView; |
| private float mMaxHeaderElevation; |
| private float mFABElevation; |
| |
| private int mTagColorDotSize; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| UIUtils.tryTranslateHttpIntent(this); |
| BeamUtils.tryUpdateIntentFromBeam(this); |
| boolean shouldBeFloatingWindow = shouldBeFloatingWindow(); |
| if (shouldBeFloatingWindow) { |
| setupFloatingWindow(); |
| } |
| |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.activity_session_detail); |
| |
| final Toolbar toolbar = getActionBarToolbar(); |
| toolbar.setNavigationIcon(shouldBeFloatingWindow |
| ? R.drawable.ic_ab_close : R.drawable.ic_up); |
| toolbar.setNavigationOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View view) { |
| finish(); |
| } |
| }); |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| toolbar.setTitle(""); |
| } |
| }); |
| |
| if (savedInstanceState == null) { |
| Uri sessionUri = getIntent().getData(); |
| BeamUtils.setBeamSessionUri(this, sessionUri); |
| } |
| |
| mSessionUri = getIntent().getData(); |
| |
| if (mSessionUri == null) { |
| return; |
| } |
| |
| mSessionId = ScheduleContract.Sessions.getSessionId(mSessionUri); |
| |
| mFABElevation = getResources().getDimensionPixelSize(R.dimen.fab_elevation); |
| mMaxHeaderElevation = getResources().getDimensionPixelSize( |
| R.dimen.session_detail_max_header_elevation); |
| |
| mTagColorDotSize = getResources().getDimensionPixelSize(R.dimen.tag_color_dot_size); |
| |
| mHandler = new Handler(); |
| |
| if (mSpeakersImageLoader == null) { |
| mSpeakersImageLoader = new ImageLoader(this, R.drawable.person_image_empty); |
| } |
| if (mNoPlaceholderImageLoader == null) { |
| mNoPlaceholderImageLoader = new ImageLoader(this); |
| } |
| |
| mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view); |
| mScrollView.addCallbacks(this); |
| ViewTreeObserver vto = mScrollView.getViewTreeObserver(); |
| if (vto.isAlive()) { |
| vto.addOnGlobalLayoutListener(mGlobalLayoutListener); |
| } |
| |
| mScrollViewChild = findViewById(R.id.scroll_view_child); |
| mScrollViewChild.setVisibility(View.INVISIBLE); |
| |
| mDetailsContainer = findViewById(R.id.details_container); |
| mHeaderBox = findViewById(R.id.header_session); |
| mTitle = (TextView) findViewById(R.id.session_title); |
| mSubtitle = (TextView) findViewById(R.id.session_subtitle); |
| mPhotoViewContainer = findViewById(R.id.session_photo_container); |
| mPhotoView = (ImageView) findViewById(R.id.session_photo); |
| |
| mPlusOneButton = (PlusOneButton) findViewById(R.id.plus_one_button); |
| mAbstract = (TextView) findViewById(R.id.session_abstract); |
| mRequirements = (TextView) findViewById(R.id.session_requirements); |
| mTags = (LinearLayout) findViewById(R.id.session_tags); |
| mTagsContainer = (ViewGroup) findViewById(R.id.session_tags_container); |
| |
| mAddScheduleButton = (CheckableFrameLayout) findViewById(R.id.add_schedule_button); |
| mAddScheduleButton.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View view) { |
| boolean starred = !mStarred; |
| SessionsHelper helper = new SessionsHelper(SessionDetailActivity.this); |
| showStarred(starred, true); |
| helper.setSessionStarred(mSessionUri, starred, mTitleString); |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { |
| mAddScheduleButton.announceForAccessibility(starred ? |
| getString(R.string.session_details_a11y_session_added) : |
| getString(R.string.session_details_a11y_session_removed)); |
| } |
| |
| /* [ANALYTICS:EVENT] |
| * TRIGGER: Add or remove a session from My Schedule. |
| * CATEGORY: 'Session' |
| * ACTION: 'Starred' or 'Unstarred' |
| * LABEL: Session title/subtitle. |
| * [/ANALYTICS] |
| */ |
| AnalyticsManager.sendEvent( |
| "Session", starred ? "Starred" : "Unstarred", mTitleString, 0L); |
| } |
| }); |
| |
| ViewCompat.setTransitionName(mPhotoView, TRANSITION_NAME_PHOTO); |
| |
| LoaderManager manager = getLoaderManager(); |
| manager.initLoader(SessionsQuery._TOKEN, null, this); |
| manager.initLoader(SpeakersQuery._TOKEN, null, this); |
| manager.initLoader(TAG_METADATA_TOKEN, null, this); |
| } |
| |
| @Override |
| public Intent getParentActivityIntent() { |
| // TODO(mangini): make this Activity navigate up to the right screen depending on how it was launched |
| return new Intent(this, MyScheduleActivity.class); |
| } |
| |
| private void setupFloatingWindow() { |
| // configure this Activity as a floating window, dimming the background |
| WindowManager.LayoutParams params = getWindow().getAttributes(); |
| params.width = getResources().getDimensionPixelSize(R.dimen.session_details_floating_width); |
| params.height = getResources().getDimensionPixelSize(R.dimen.session_details_floating_height); |
| params.alpha = 1; |
| params.dimAmount = 0.4f; |
| params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; |
| getWindow().setAttributes(params); |
| } |
| |
| private boolean shouldBeFloatingWindow() { |
| Resources.Theme theme = getTheme(); |
| TypedValue floatingWindowFlag = new TypedValue(); |
| if (theme == null || !theme.resolveAttribute(R.attr.isFloatingWindow, floatingWindowFlag, true)) { |
| // isFloatingWindow flag is not defined in theme |
| return false; |
| } |
| return (floatingWindowFlag.data != 0); |
| } |
| |
| private void recomputePhotoAndScrollingMetrics() { |
| mHeaderHeightPixels = mHeaderBox.getHeight(); |
| |
| mPhotoHeightPixels = 0; |
| if (mHasPhoto) { |
| mPhotoHeightPixels = (int) (mPhotoView.getWidth() / PHOTO_ASPECT_RATIO); |
| mPhotoHeightPixels = Math.min(mPhotoHeightPixels, mScrollView.getHeight() * 2 / 3); |
| } |
| |
| ViewGroup.LayoutParams lp; |
| lp = mPhotoViewContainer.getLayoutParams(); |
| if (lp.height != mPhotoHeightPixels) { |
| lp.height = mPhotoHeightPixels; |
| mPhotoViewContainer.setLayoutParams(lp); |
| } |
| |
| ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) |
| mDetailsContainer.getLayoutParams(); |
| if (mlp.topMargin != mHeaderHeightPixels + mPhotoHeightPixels) { |
| mlp.topMargin = mHeaderHeightPixels + mPhotoHeightPixels; |
| mDetailsContainer.setLayoutParams(mlp); |
| } |
| |
| onScrollChanged(0, 0); // trigger scroll handling |
| } |
| |
| @Override |
| protected void onDestroy() { |
| super.onDestroy(); |
| if (mScrollView == null) { |
| return; |
| } |
| |
| ViewTreeObserver vto = mScrollView.getViewTreeObserver(); |
| if (vto.isAlive()) { |
| vto.removeGlobalOnLayoutListener(mGlobalLayoutListener); |
| } |
| } |
| |
| private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener |
| = new ViewTreeObserver.OnGlobalLayoutListener() { |
| @Override |
| public void onGlobalLayout() { |
| mAddScheduleButtonHeightPixels = mAddScheduleButton.getHeight(); |
| recomputePhotoAndScrollingMetrics(); |
| } |
| }; |
| |
| @Override |
| public void onScrollChanged(int deltaX, int deltaY) { |
| // Reposition the header bar -- it's normally anchored to the top of the content, |
| // but locks to the top of the screen on scroll |
| int scrollY = mScrollView.getScrollY(); |
| |
| float newTop = Math.max(mPhotoHeightPixels, scrollY); |
| mHeaderBox.setTranslationY(newTop); |
| mAddScheduleButton.setTranslationY(newTop + mHeaderHeightPixels |
| - mAddScheduleButtonHeightPixels / 2); |
| |
| float gapFillProgress = 1; |
| if (mPhotoHeightPixels != 0) { |
| gapFillProgress = Math.min(Math.max(UIUtils.getProgress(scrollY, |
| 0, |
| mPhotoHeightPixels), 0), 1); |
| } |
| |
| ViewCompat.setElevation(mHeaderBox, gapFillProgress * mMaxHeaderElevation); |
| ViewCompat.setElevation(mAddScheduleButton, gapFillProgress * mMaxHeaderElevation |
| + mFABElevation); |
| |
| // Move background photo (parallax effect) |
| mPhotoViewContainer.setTranslationY(scrollY * 0.5f); |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| updatePlusOneButton(); |
| if (mTimeHintUpdaterRunnable != null) { |
| mHandler.postDelayed(mTimeHintUpdaterRunnable, TIME_HINT_UPDATE_INTERVAL); |
| } |
| |
| // Refresh whether or not feedback has been submitted |
| getLoaderManager().restartLoader(FeedbackQuery._TOKEN, null, this); |
| } |
| |
| @Override |
| public void onStop() { |
| super.onStop(); |
| if (mInitStarred != mStarred) { |
| if (UIUtils.getCurrentTime(this) < mSessionStart) { |
| // Update Calendar event through the Calendar API on Android 4.0 or new versions. |
| Intent intent = null; |
| if (mStarred) { |
| // Set up intent to add session to Calendar, if it doesn't exist already. |
| 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, mTitleString); |
| } else { |
| // Set up intent to remove session from Calendar, if exists. |
| 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, mTitleString); |
| } |
| intent.setClass(this, SessionCalendarService.class); |
| startService(intent); |
| |
| if (mStarred) { |
| setupNotification(); |
| } |
| } |
| } |
| } |
| |
| private void setupNotification() { |
| Intent scheduleIntent; |
| |
| // Schedule session notification |
| if (UIUtils.getCurrentTime(this) < mSessionStart) { |
| LOGD(TAG, "Scheduling notification about session start."); |
| scheduleIntent = new Intent( |
| SessionAlarmService.ACTION_SCHEDULE_STARRED_BLOCK, |
| null, this, SessionAlarmService.class); |
| scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_START, mSessionStart); |
| scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_END, mSessionEnd); |
| startService(scheduleIntent); |
| } else { |
| LOGD(TAG, "Not scheduling notification about session start, too late."); |
| } |
| |
| // Schedule feedback notification |
| if (UIUtils.getCurrentTime(this) < mSessionEnd) { |
| LOGD(TAG, "Scheduling notification about session feedback."); |
| scheduleIntent = new Intent( |
| SessionAlarmService.ACTION_SCHEDULE_FEEDBACK_NOTIFICATION, |
| null, this, 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, mTitleString); |
| scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_ROOM, mRoomName); |
| scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_SPEAKERS, mSpeakers); |
| startService(scheduleIntent); |
| } else { |
| LOGD(TAG, "Not scheduling feedback notification, too late."); |
| } |
| } |
| |
| private void updateTimeBasedUi() { |
| long currentTimeMillis = UIUtils.getCurrentTime(this); |
| boolean canShowLivestream = mHasLivestream; |
| |
| if (canShowLivestream && !mDismissedWatchLivestreamCard |
| && currentTimeMillis > mSessionStart |
| && currentTimeMillis <= mSessionEnd) { |
| // show the "watch now" card |
| showWatchNowCard(); |
| } else if (!mAlreadyGaveFeedback && mInitStarred && currentTimeMillis >= (mSessionEnd - |
| Config.FEEDBACK_MILLIS_BEFORE_SESSION_END) |
| && !sDismissedFeedbackCard.contains(mSessionId)) { |
| // show the "give feedback" card |
| showGiveFeedbackCard(); |
| } |
| |
| String timeHint = ""; |
| long countdownMillis = mSessionStart - currentTimeMillis; |
| |
| if (TimeUtils.hasConferenceEnded(this)) { |
| // no time hint to display |
| timeHint = ""; |
| } else if (currentTimeMillis >= mSessionEnd) { |
| timeHint = getString(R.string.time_hint_session_ended); |
| } else if (currentTimeMillis >= mSessionStart) { |
| long minutesAgo = (currentTimeMillis - mSessionStart) / 60000; |
| if (minutesAgo > 1) { |
| timeHint = getString(R.string.time_hint_started_min, minutesAgo); |
| } else { |
| timeHint = getString(R.string.time_hint_started_just); |
| } |
| } else if (countdownMillis > 0 && countdownMillis < Config.HINT_TIME_BEFORE_SESSION) { |
| long millisUntil = mSessionStart - currentTimeMillis; |
| long minutesUntil = millisUntil / 60000 + (millisUntil % 1000 > 0 ? 1 : 0); |
| if (minutesUntil > 1) { |
| timeHint = getString(R.string.time_hint_about_to_start_min, minutesUntil); |
| } else { |
| timeHint = getString(R.string.time_hint_about_to_start_shortly, minutesUntil); |
| } |
| } |
| |
| final TextView timeHintView = (TextView) findViewById(R.id.time_hint); |
| |
| if (!TextUtils.isEmpty(timeHint)) { |
| timeHintView.setVisibility(View.VISIBLE); |
| timeHintView.setText(timeHint); |
| } else { |
| timeHintView.setVisibility(View.GONE); |
| } |
| } |
| |
| private void setTextSelectable(TextView tv) { |
| if (tv != null && !tv.isTextSelectable()) { |
| tv.setTextIsSelectable(true); |
| } |
| } |
| |
| private void onFeedbackQueryComplete(Cursor cursor) { |
| // Is there existing feedback for this session? |
| mAlreadyGaveFeedback = cursor.getCount() > 0; |
| |
| if (mAlreadyGaveFeedback) { |
| final MessageCardView giveFeedbackCardView = (MessageCardView) findViewById(R.id.give_feedback_card); |
| if (giveFeedbackCardView != null) { |
| giveFeedbackCardView.setVisibility(View.GONE); |
| } |
| if (mSubmitFeedbackView != null) { |
| mSubmitFeedbackView.setVisibility(View.GONE); |
| } |
| } |
| LOGD(TAG, "User " + (mAlreadyGaveFeedback ? "already gave" : "has not given") + " feedback for session."); |
| cursor.close(); |
| } |
| |
| /** |
| * Handle {@link SessionsQuery} {@link Cursor}. |
| */ |
| private void onSessionQueryComplete(Cursor cursor) { |
| mSessionCursor = true; |
| if (!cursor.moveToFirst()) { |
| // TODO: Remove this in favor of a callbacks interface that the activity |
| // can implement. |
| finish(); |
| return; |
| } |
| |
| mTitleString = cursor.getString(SessionsQuery.TITLE); |
| mSessionColor = cursor.getInt(SessionsQuery.COLOR); |
| |
| if (mSessionColor == 0) { |
| // no color -- use default |
| mSessionColor = getResources().getColor(R.color.default_session_color); |
| } else { |
| // make sure it's opaque |
| mSessionColor = UIUtils.setColorAlpha(mSessionColor, 255); |
| } |
| |
| mHeaderBox.setBackgroundColor(mSessionColor); |
| getLUtils().setStatusBarColor(UIUtils.scaleColor(mSessionColor, 0.8f, false)); |
| |
| mLivestreamUrl = cursor.getString(SessionsQuery.LIVESTREAM_URL); |
| mHasLivestream = !TextUtils.isEmpty(mLivestreamUrl); |
| |
| // Format the time this session occupies |
| mSessionStart = cursor.getLong(SessionsQuery.START); |
| mSessionEnd = cursor.getLong(SessionsQuery.END); |
| mRoomName = cursor.getString(SessionsQuery.ROOM_NAME); |
| mSpeakers = cursor.getString(SessionsQuery.SPEAKER_NAMES); |
| String subtitle = UIUtils.formatSessionSubtitle( |
| mSessionStart, mSessionEnd, mRoomName, mBuffer, this); |
| if (mHasLivestream) { |
| subtitle += " " + UIUtils.getLiveBadgeText(this, mSessionStart, mSessionEnd); |
| } |
| |
| mTitle.setText(mTitleString); |
| mSubtitle.setText(subtitle); |
| |
| for (int resId : SECTION_HEADER_RES_IDS) { |
| ((TextView) findViewById(resId)).setTextColor(mSessionColor); |
| } |
| |
| mPhotoViewContainer.setBackgroundColor(UIUtils.scaleSessionColorToDefaultBG(mSessionColor)); |
| |
| String photo = cursor.getString(SessionsQuery.PHOTO_URL); |
| if (!TextUtils.isEmpty(photo)) { |
| mHasPhoto = true; |
| mNoPlaceholderImageLoader.loadImage(photo, mPhotoView, new RequestListener<String>() { |
| @Override |
| public void onException(Exception e, String url, Target target) { |
| mHasPhoto = false; |
| recomputePhotoAndScrollingMetrics(); |
| } |
| |
| @Override |
| public void onImageReady(String url, Target target, boolean b, boolean b2) { |
| // Trigger image transition |
| recomputePhotoAndScrollingMetrics(); |
| } |
| }); |
| recomputePhotoAndScrollingMetrics(); |
| } else { |
| mHasPhoto = false; |
| recomputePhotoAndScrollingMetrics(); |
| } |
| |
| mUrl = cursor.getString(SessionsQuery.URL); |
| if (TextUtils.isEmpty(mUrl)) { |
| mUrl = ""; |
| } |
| |
| mHashTag = cursor.getString(SessionsQuery.HASHTAG); |
| if (!TextUtils.isEmpty(mHashTag)) { |
| enableSocialStreamMenuItemDeferred(); |
| } |
| |
| mRoomId = cursor.getString(SessionsQuery.ROOM_ID); |
| |
| final boolean inMySchedule = cursor.getInt(SessionsQuery.IN_MY_SCHEDULE) != 0; |
| |
| setupShareMenuItemDeferred(); |
| |
| // Handle Keynote as a special case, where the user cannot remove it |
| // from the schedule (it is auto added to schedule on sync) |
| mTagsString = cursor.getString(SessionsQuery.TAGS); |
| mIsKeynote = mTagsString.contains(Config.Tags.SPECIAL_KEYNOTE); |
| mAddScheduleButton.setVisibility( |
| (AccountUtils.hasActiveAccount(this) && !mIsKeynote) |
| ? View.VISIBLE : View.INVISIBLE); |
| |
| tryRenderTags(); |
| |
| if (!mIsKeynote) { |
| showStarredDeferred(mInitStarred = inMySchedule, false); |
| } |
| |
| final String sessionAbstract = cursor.getString(SessionsQuery.ABSTRACT); |
| if (!TextUtils.isEmpty(sessionAbstract)) { |
| UIUtils.setTextMaybeHtml(mAbstract, sessionAbstract); |
| mAbstract.setVisibility(View.VISIBLE); |
| mHasSummaryContent = true; |
| } else { |
| mAbstract.setVisibility(View.GONE); |
| } |
| |
| updatePlusOneButton(); |
| |
| // Build requirements section |
| final View requirementsBlock = findViewById(R.id.session_requirements_block); |
| final String sessionRequirements = cursor.getString(SessionsQuery.REQUIREMENTS); |
| if (!TextUtils.isEmpty(sessionRequirements)) { |
| UIUtils.setTextMaybeHtml(mRequirements, sessionRequirements); |
| requirementsBlock.setVisibility(View.VISIBLE); |
| mHasSummaryContent = true; |
| } else { |
| requirementsBlock.setVisibility(View.GONE); |
| } |
| |
| // Build related videos section |
| final ViewGroup relatedVideosBlock = (ViewGroup) findViewById(R.id.related_videos_block); |
| relatedVideosBlock.setVisibility(View.GONE); |
| |
| // Build links section |
| buildLinksSection(cursor); |
| |
| updateEmptyView(); |
| |
| updateTimeBasedUi(); |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| onScrollChanged(0, 0); // trigger scroll handling |
| mScrollViewChild.setVisibility(View.VISIBLE); |
| //mAbstract.setTextIsSelectable(true); |
| } |
| }); |
| |
| mTimeHintUpdaterRunnable = new Runnable() { |
| @Override |
| public void run() { |
| updateTimeBasedUi(); |
| mHandler.postDelayed(mTimeHintUpdaterRunnable, TIME_HINT_UPDATE_INTERVAL); |
| } |
| }; |
| mHandler.postDelayed(mTimeHintUpdaterRunnable, TIME_HINT_UPDATE_INTERVAL); |
| } |
| |
| private void tryRenderTags() { |
| if (mTagMetadata == null || mTagsString == null) { |
| return; |
| } |
| |
| if (TextUtils.isEmpty(mTagsString)) { |
| mTagsContainer.setVisibility(View.GONE); |
| } else { |
| mTagsContainer.setVisibility(View.VISIBLE); |
| mTags.removeAllViews(); |
| LayoutInflater inflater = LayoutInflater.from(this); |
| String[] tagIds = mTagsString.split(","); |
| |
| List<TagMetadata.Tag> tags = new ArrayList<TagMetadata.Tag>(); |
| for (String tagId : tagIds) { |
| if (Config.Tags.SESSIONS.equals(tagId) || |
| Config.Tags.SPECIAL_KEYNOTE.equals(tagId)) { |
| continue; |
| } |
| |
| TagMetadata.Tag tag = mTagMetadata.getTag(tagId); |
| if (tag == null) { |
| continue; |
| } |
| |
| tags.add(tag); |
| } |
| |
| if (tags.size() == 0) { |
| mTagsContainer.setVisibility(View.GONE); |
| return; |
| } |
| |
| Collections.sort(tags, TagMetadata.TAG_DISPLAY_ORDER_COMPARATOR); |
| |
| for (final TagMetadata.Tag tag : tags) { |
| TextView chipView = (TextView) inflater.inflate( |
| R.layout.include_session_tag_chip, mTags, false); |
| chipView.setText(tag.getName()); |
| |
| if (Config.Tags.CATEGORY_TOPIC.equals(tag.getCategory())) { |
| ShapeDrawable colorDrawable = new ShapeDrawable(new OvalShape()); |
| colorDrawable.setIntrinsicWidth(mTagColorDotSize); |
| colorDrawable.setIntrinsicHeight(mTagColorDotSize); |
| colorDrawable.getPaint().setStyle(Paint.Style.FILL); |
| chipView.setCompoundDrawablesWithIntrinsicBounds(colorDrawable, |
| null, null, null); |
| colorDrawable.getPaint().setColor(tag.getColor()); |
| } |
| |
| chipView.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View view) { |
| finish(); // TODO: better encapsulation |
| Intent intent = new Intent(SessionDetailActivity.this, BrowseSessionsActivity.class) |
| .putExtra(BrowseSessionsActivity.EXTRA_FILTER_TAG, tag.getId()) |
| .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); |
| startActivity(intent); |
| } |
| }); |
| |
| mTags.addView(chipView); |
| } |
| } |
| } |
| |
| private void buildLinksSection(Cursor cursor) { |
| // Compile list of links (I/O live link, submit feedback, and normal links) |
| ViewGroup linkContainer = (ViewGroup) findViewById(R.id.links_container); |
| linkContainer.removeAllViews(); |
| |
| |
| // Build links section |
| // the Object can be either a string URL or an Intent |
| List<Pair<Integer, Object>> links = new ArrayList<Pair<Integer, Object>>(); |
| |
| long currentTimeMillis = UIUtils.getCurrentTime(this); |
| if (mHasLivestream |
| && currentTimeMillis > mSessionStart |
| && currentTimeMillis <= mSessionEnd) { |
| links.add(new Pair<Integer, Object>( |
| R.string.session_link_livestream, |
| getWatchLiveIntent(this))); |
| } |
| |
| // Add session feedback link, if appropriate |
| if (!mAlreadyGaveFeedback && currentTimeMillis > mSessionEnd |
| - Config.FEEDBACK_MILLIS_BEFORE_SESSION_END) { |
| links.add(new Pair<Integer, Object>( |
| R.string.session_feedback_submitlink, |
| getFeedbackIntent() |
| )); |
| } |
| |
| for (int i = 0; i < SessionsQuery.LINKS_INDICES.length; i++) { |
| final String linkUrl = cursor.getString(SessionsQuery.LINKS_INDICES[i]); |
| if (TextUtils.isEmpty(linkUrl)) { |
| continue; |
| } |
| |
| links.add(new Pair<Integer, Object>( |
| SessionsQuery.LINKS_TITLES[i], |
| new Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl)) |
| .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) |
| )); |
| } |
| |
| // Render links |
| if (links.size() > 0) { |
| LayoutInflater inflater = LayoutInflater.from(this); |
| int columns = getResources().getInteger(R.integer.links_columns); |
| |
| LinearLayout currentLinkRowView = null; |
| for (int i = 0; i < links.size(); i++) { |
| final Pair<Integer, Object> link = links.get(i); |
| |
| // Create link view |
| TextView linkView = (TextView) inflater.inflate(R.layout.list_item_session_link, |
| linkContainer, false); |
| if (link.first == R.string.session_feedback_submitlink) { |
| mSubmitFeedbackView = linkView; |
| } |
| linkView.setText(getString(link.first)); |
| linkView.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View view) { |
| fireLinkEvent(link.first); |
| Intent intent=null; |
| if (link.second instanceof Intent) { |
| intent = (Intent) link.second; |
| } else if (link.second instanceof String) { |
| intent = new Intent(Intent.ACTION_VIEW, Uri.parse((String) link.second)) |
| .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); |
| } |
| try { |
| startActivity(intent); |
| } catch (ActivityNotFoundException ignored) { |
| } |
| } |
| }); |
| |
| // Place it inside a container |
| if (columns == 1) { |
| linkContainer.addView(linkView); |
| } else { |
| // create a new link row |
| if (i % columns == 0) { |
| currentLinkRowView = (LinearLayout) inflater.inflate( |
| R.layout.include_link_row, linkContainer, false); |
| currentLinkRowView.setWeightSum(columns); |
| linkContainer.addView(currentLinkRowView); |
| } |
| |
| ((LinearLayout.LayoutParams) linkView.getLayoutParams()).width = 0; |
| ((LinearLayout.LayoutParams) linkView.getLayoutParams()).weight = 1; |
| currentLinkRowView.addView(linkView); |
| } |
| } |
| |
| findViewById(R.id.session_links_header).setVisibility(View.VISIBLE); |
| findViewById(R.id.links_container).setVisibility(View.VISIBLE); |
| |
| } else { |
| findViewById(R.id.session_links_header).setVisibility(View.GONE); |
| findViewById(R.id.links_container).setVisibility(View.GONE); |
| } |
| |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| if (mTimeHintUpdaterRunnable != null) { |
| mHandler.removeCallbacks(mTimeHintUpdaterRunnable); |
| } |
| } |
| |
| private Intent getWatchLiveIntent(Context context) { |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && |
| YouTubeIntents.canResolvePlayVideoIntent(context)) { |
| String youtubeVideoId = SessionLivestreamActivity.getVideoIdFromUrl(mLivestreamUrl); |
| return YouTubeIntents.createPlayVideoIntentWithOptions( |
| context, youtubeVideoId, true, false); |
| } |
| return new Intent(Intent.ACTION_VIEW, mSessionUri).setClass(context, |
| SessionLivestreamActivity.class); |
| } |
| |
| private void updatePlusOneButton() { |
| if (mPlusOneButton == null) { |
| return; |
| } |
| |
| if (!TextUtils.isEmpty(mUrl) && !mIsKeynote) { |
| mPlusOneButton.initialize(mUrl, 0); |
| mPlusOneButton.setVisibility(View.VISIBLE); |
| } else { |
| mPlusOneButton.setVisibility(View.GONE); |
| } |
| } |
| |
| private void showWatchNowCard() { |
| final MessageCardView messageCardView = (MessageCardView) findViewById(R.id.live_now_card); |
| messageCardView.show(); |
| messageCardView.setListener(new MessageCardView.OnMessageCardButtonClicked() { |
| @Override |
| public void onMessageCardButtonClicked(String tag) { |
| if ("WATCH_NOW".equals(tag)) { |
| Intent intent = getWatchLiveIntent(SessionDetailActivity.this); |
| startActivity(intent); |
| } else { |
| mDismissedWatchLivestreamCard = true; |
| messageCardView.dismiss(); |
| } |
| } |
| }); |
| } |
| |
| private void showGiveFeedbackCard() { |
| final MessageCardView messageCardView = (MessageCardView) findViewById(R.id.give_feedback_card); |
| messageCardView.show(); |
| messageCardView.setListener(new MessageCardView.OnMessageCardButtonClicked() { |
| @Override |
| public void onMessageCardButtonClicked(String tag) { |
| if ("GIVE_FEEDBACK".equals(tag)) { |
| /* [ANALYTICS:EVENT] |
| * TRIGGER: Click on the Send Feedback action on the Session Details page. |
| * CATEGORY: 'Session' |
| * ACTION: 'Feedback' |
| * LABEL: session title/subtitle |
| * [/ANALYTICS] |
| */ |
| AnalyticsManager.sendEvent("Session", "Feedback", mTitleString, 0L); |
| Intent intent = getFeedbackIntent(); |
| startActivity(intent); |
| } else { |
| sDismissedFeedbackCard.add(mSessionId); |
| messageCardView.dismiss(); |
| } |
| } |
| }); |
| } |
| |
| private Intent getFeedbackIntent() { |
| return new Intent(Intent.ACTION_VIEW, mSessionUri, this, |
| SessionFeedbackActivity.class); |
| } |
| |
| private void enableSocialStreamMenuItemDeferred() { |
| mDeferredUiOperations.add(new Runnable() { |
| @Override |
| public void run() { |
| mSocialStreamMenuItem.setVisible(true); |
| } |
| }); |
| tryExecuteDeferredUiOperations(); |
| } |
| |
| private void showStarredDeferred(final boolean starred, final boolean allowAnimate) { |
| mDeferredUiOperations.add(new Runnable() { |
| @Override |
| public void run() { |
| showStarred(starred, allowAnimate); |
| } |
| }); |
| tryExecuteDeferredUiOperations(); |
| } |
| |
| private void showStarred(boolean starred, boolean allowAnimate) { |
| mStarred = starred; |
| |
| mAddScheduleButton.setChecked(mStarred, allowAnimate); |
| |
| ImageView iconView = (ImageView) mAddScheduleButton.findViewById(R.id.add_schedule_icon); |
| getLUtils().setOrAnimatePlusCheckIcon(iconView, starred, allowAnimate); |
| mAddScheduleButton.setContentDescription(getString(starred |
| ? R.string.remove_from_schedule_desc |
| : R.string.add_to_schedule_desc)); |
| } |
| |
| private void setupShareMenuItemDeferred() { |
| mDeferredUiOperations.add(new Runnable() { |
| @Override |
| public void run() { |
| new SessionsHelper(SessionDetailActivity.this).tryConfigureShareMenuItem(mShareMenuItem, |
| R.string.share_template, mTitleString, mHashTag, mUrl); |
| } |
| }); |
| tryExecuteDeferredUiOperations(); |
| } |
| |
| private void tryExecuteDeferredUiOperations() { |
| if (mSocialStreamMenuItem != null) { |
| for (Runnable r : mDeferredUiOperations) { |
| r.run(); |
| } |
| mDeferredUiOperations.clear(); |
| } |
| } |
| |
| private void onSpeakersQueryComplete(Cursor cursor) { |
| mSpeakersCursor = true; |
| final ViewGroup speakersGroup = (ViewGroup) findViewById(R.id.session_speakers_block); |
| |
| // Remove all existing speakers (everything but first child, which is the header) |
| for (int i = speakersGroup.getChildCount() - 1; i >= 1; i--) { |
| speakersGroup.removeViewAt(i); |
| } |
| |
| final LayoutInflater inflater = getLayoutInflater(); |
| |
| boolean hasSpeakers = false; |
| |
| cursor.moveToPosition(-1); // move to just before first record |
| while (cursor.moveToNext()) { |
| final String speakerName = cursor.getString(SpeakersQuery.SPEAKER_NAME); |
| if (TextUtils.isEmpty(speakerName)) { |
| continue; |
| } |
| |
| final String speakerImageUrl = cursor.getString(SpeakersQuery.SPEAKER_IMAGE_URL); |
| final String speakerCompany = cursor.getString(SpeakersQuery.SPEAKER_COMPANY); |
| final String speakerUrl = cursor.getString(SpeakersQuery.SPEAKER_URL); |
| final String speakerAbstract = cursor.getString(SpeakersQuery.SPEAKER_ABSTRACT); |
| |
| String speakerHeader = speakerName; |
| if (!TextUtils.isEmpty(speakerCompany)) { |
| speakerHeader += ", " + speakerCompany; |
| } |
| |
| final View speakerView = inflater |
| .inflate(R.layout.speaker_detail, speakersGroup, false); |
| final TextView speakerHeaderView = (TextView) speakerView |
| .findViewById(R.id.speaker_header); |
| final ImageView speakerImageView = (ImageView) speakerView |
| .findViewById(R.id.speaker_image); |
| final TextView speakerAbstractView = (TextView) speakerView |
| .findViewById(R.id.speaker_abstract); |
| |
| if (!TextUtils.isEmpty(speakerImageUrl) && mSpeakersImageLoader != null) { |
| mSpeakersImageLoader.loadImage(speakerImageUrl, speakerImageView); |
| } |
| |
| speakerHeaderView.setText(speakerHeader); |
| speakerImageView.setContentDescription( |
| getString(R.string.speaker_googleplus_profile, speakerHeader)); |
| UIUtils.setTextMaybeHtml(speakerAbstractView, speakerAbstract); |
| |
| if (!TextUtils.isEmpty(speakerUrl)) { |
| speakerImageView.setEnabled(true); |
| speakerImageView.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View view) { |
| Intent speakerProfileIntent = new Intent(Intent.ACTION_VIEW, |
| Uri.parse(speakerUrl)); |
| speakerProfileIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); |
| UIUtils.preferPackageForIntent(SessionDetailActivity.this, |
| speakerProfileIntent, |
| UIUtils.GOOGLE_PLUS_PACKAGE_NAME); |
| startActivity(speakerProfileIntent); |
| } |
| }); |
| } else { |
| speakerImageView.setEnabled(false); |
| speakerImageView.setOnClickListener(null); |
| } |
| |
| speakersGroup.addView(speakerView); |
| hasSpeakers = true; |
| mHasSummaryContent = true; |
| } |
| |
| speakersGroup.setVisibility(hasSpeakers ? View.VISIBLE : View.GONE); |
| updateEmptyView(); |
| } |
| |
| private void updateEmptyView() { |
| findViewById(android.R.id.empty).setVisibility( |
| (mSpeakersCursor && mSessionCursor && !mHasSummaryContent) |
| ? View.VISIBLE |
| : View.GONE); |
| } |
| |
| @Override |
| public boolean onCreateOptionsMenu(Menu menu) { |
| super.onCreateOptionsMenu(menu); |
| getMenuInflater().inflate(R.menu.session_detail, menu); |
| mSocialStreamMenuItem = menu.findItem(R.id.menu_social_stream); |
| mShareMenuItem = menu.findItem(R.id.menu_share); |
| tryExecuteDeferredUiOperations(); |
| return true; |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| SessionsHelper helper = new SessionsHelper(this); |
| switch (item.getItemId()) { |
| case R.id.menu_map_room: |
| /* [ANALYTICS:EVENT] |
| * TRIGGER: Click on the Map action on the Session Details page. |
| * CATEGORY: 'Session' |
| * ACTION: 'Map' |
| * LABEL: session title/subtitle |
| * [/ANALYTICS] |
| */ |
| AnalyticsManager.sendEvent("Session", "Map", mTitleString, 0L); |
| helper.startMapActivity(mRoomId); |
| return true; |
| |
| case R.id.menu_share: |
| // On ICS+ devices, we normally won't reach this as ShareActionProvider will handle |
| // sharing. |
| helper.shareSession(this, R.string.share_template, mTitleString, |
| mHashTag, mUrl); |
| return true; |
| |
| case R.id.menu_social_stream: |
| if (!TextUtils.isEmpty(mHashTag)) { |
| /* [ANALYTICS:EVENT] |
| * TRIGGER: Click on the Social Stream action on the Session Details page. |
| * CATEGORY: 'Session' |
| * ACTION: 'Stream' |
| * LABEL: session title/subtitle |
| * [/ANALYTICS] |
| */ |
| AnalyticsManager.sendEvent("Session", "Stream", mTitleString, 0L); |
| UIUtils.showHashtagStream(this, mHashTag); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /* |
| * Event structure: |
| * Category -> "Session Details" |
| * Action -> Link Text |
| * Label -> Session's Title |
| * Value -> 0. |
| */ |
| void fireLinkEvent(int actionId) { |
| /* [ANALYTICS:EVENT] |
| * TRIGGER: Click on a link on the Session Details page. |
| * CATEGORY: 'Session' |
| * ACTION: The link's name ("Watch Live", "Follow us on Google+", etc) |
| * LABEL: The session's title/subtitle. |
| * [/ANALYTICS] |
| */ |
| AnalyticsManager.sendEvent("Session", getString(actionId), mTitleString, 0L); |
| } |
| |
| /** |
| * {@link com.google.samples.apps.iosched.provider.ScheduleContract.Sessions} query parameters. |
| */ |
| private interface SessionsQuery { |
| int _TOKEN = 0x1; |
| |
| String[] PROJECTION = { |
| 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_URL, |
| 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 |
| }; |
| |
| int START = 0; |
| int END = 1; |
| int LEVEL = 2; |
| int TITLE = 3; |
| int ABSTRACT = 4; |
| int REQUIREMENTS = 5; |
| int IN_MY_SCHEDULE = 6; |
| int HASHTAG = 7; |
| int URL = 8; |
| int YOUTUBE_URL = 9; |
| int PDF_URL = 10; |
| int NOTES_URL = 11; |
| int LIVESTREAM_URL = 12; |
| int MODERATOR_URL = 13; |
| int ROOM_ID = 14; |
| int ROOM_NAME = 15; |
| int COLOR = 16; |
| int PHOTO_URL = 17; |
| int RELATED_CONTENT = 18; |
| int TAGS = 19; |
| int SPEAKER_NAMES = 20; |
| |
| int[] LINKS_INDICES = { |
| YOUTUBE_URL, |
| MODERATOR_URL, |
| PDF_URL, |
| NOTES_URL, |
| }; |
| |
| int[] LINKS_TITLES = { |
| R.string.session_link_youtube, |
| R.string.session_link_moderator, |
| R.string.session_link_pdf, |
| R.string.session_link_notes, |
| }; |
| } |
| |
| private interface SpeakersQuery { |
| int _TOKEN = 0x3; |
| |
| String[] PROJECTION = { |
| ScheduleContract.Speakers.SPEAKER_NAME, |
| ScheduleContract.Speakers.SPEAKER_IMAGE_URL, |
| ScheduleContract.Speakers.SPEAKER_COMPANY, |
| ScheduleContract.Speakers.SPEAKER_ABSTRACT, |
| ScheduleContract.Speakers.SPEAKER_URL, |
| }; |
| |
| int SPEAKER_NAME = 0; |
| int SPEAKER_IMAGE_URL = 1; |
| int SPEAKER_COMPANY = 2; |
| int SPEAKER_ABSTRACT = 3; |
| int SPEAKER_URL = 4; |
| } |
| |
| private interface FeedbackQuery { |
| int _TOKEN = 0x4; |
| |
| String[] PROJECTION = { |
| ScheduleContract.Feedback.SESSION_ID |
| }; |
| } |
| |
| private static final int TAG_METADATA_TOKEN = 0x5; |
| |
| @Override |
| public Loader<Cursor> onCreateLoader(int id, Bundle data) { |
| CursorLoader loader = null; |
| if (id == SessionsQuery._TOKEN){ |
| loader = new CursorLoader(this, mSessionUri, SessionsQuery.PROJECTION, null, |
| null, null); |
| } else if (id == SpeakersQuery._TOKEN && mSessionUri != null){ |
| Uri speakersUri = ScheduleContract.Sessions.buildSpeakersDirUri(mSessionId); |
| loader = new CursorLoader(this, speakersUri, SpeakersQuery.PROJECTION, null, |
| null, ScheduleContract.Speakers.DEFAULT_SORT); |
| } else if (id == FeedbackQuery._TOKEN) { |
| Uri feedbackUri = ScheduleContract.Feedback.buildFeedbackUri(mSessionId); |
| loader = new CursorLoader(this, feedbackUri, FeedbackQuery.PROJECTION, null, |
| null, null); |
| } else if (id == TAG_METADATA_TOKEN) { |
| loader = TagMetadata.createCursorLoader(this); |
| } |
| return loader; |
| } |
| |
| @Override |
| public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { |
| if (loader.getId() == SessionsQuery._TOKEN) { |
| onSessionQueryComplete(cursor); |
| } else if (loader.getId() == SpeakersQuery._TOKEN) { |
| onSpeakersQueryComplete(cursor); |
| } else if (loader.getId() == FeedbackQuery._TOKEN) { |
| onFeedbackQueryComplete(cursor); |
| } else if (loader.getId() == TAG_METADATA_TOKEN) { |
| mTagMetadata = new TagMetadata(cursor); |
| cursor.close(); |
| tryRenderTags(); |
| } else { |
| cursor.close(); |
| } |
| } |
| |
| @Override |
| public void onLoaderReset(Loader<Cursor> loader) {} |
| } |