/*
* 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.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.app.Fragment;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Loader;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.samples.apps.iosched.Config;
import com.google.samples.apps.iosched.R;
import com.google.samples.apps.iosched.provider.ScheduleContract;
import com.google.samples.apps.iosched.ui.widget.CollectionView;
import com.google.samples.apps.iosched.ui.widget.CollectionViewCallbacks;
import com.google.samples.apps.iosched.util.AnalyticsManager;
import com.google.samples.apps.iosched.util.ImageLoader;
import com.google.samples.apps.iosched.util.ParserUtils;
import com.google.samples.apps.iosched.util.UIUtils;
import java.util.ArrayList;
import java.util.Locale;
import static com.google.samples.apps.iosched.util.LogUtils.LOGD;
import static com.google.samples.apps.iosched.util.LogUtils.LOGW;
import static com.google.samples.apps.iosched.util.LogUtils.makeLogTag;
public class VideoLibraryFragment extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor>, CollectionViewCallbacks {
private static final String TAG = makeLogTag("VideoLibraryFragment");
boolean mInformedYearsAndTopics = false;
ImageLoader mImageLoader;
CollectionView mCollectionView = null;
View mEmptyView = null;
Cursor mCursor = null;
int mFilterYear = 0;
String mFilterTopic = null;
private static final String LOADER_ARG_YEAR = "LOADER_ARG_YEAR";
private static final String LOADER_ARG_TOPIC = "LOADER_ARG_TOPIC";
private static final int GROUP_ID_HERO = 1000;
private static final int GROUP_ID_NORMAL = 1001;
public interface Callbacks {
public void onAvailableVideoYearsChanged(ArrayList<Integer> years);
public void onAvailableVideoTopicsChanged(ArrayList<String> availableTopics);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_video_library, container, false);
mCollectionView = (CollectionView) root.findViewById(R.id.videos_collection_view);
mEmptyView = root.findViewById(android.R.id.empty);
return root;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mImageLoader = new ImageLoader(getActivity(), android.R.color.transparent);
setFilterAndReload(0, null);
}
public void setContentTopClearance(int clearance) {
if (mCollectionView != null) {
mCollectionView.setContentTopClearance(clearance);
}
}
public void setFilterAndReload(int year, String topic) {
mFilterYear = year;
mFilterTopic = topic;
Bundle args = new Bundle();
args.putInt(LOADER_ARG_YEAR, mFilterYear);
args.putString(LOADER_ARG_TOPIC, mFilterTopic == null ? "" : mFilterTopic);
getLoaderManager().restartLoader(VideosQuery._TOKEN, args, this);
}
@Override
public Loader<Cursor> onCreateLoader(int token, Bundle bundle) {
if (token == VideosQuery._TOKEN) {
int year = bundle.getInt(LOADER_ARG_YEAR);
String topic = bundle.getString(LOADER_ARG_TOPIC);
ArrayList<String> selectionArgs = new ArrayList<String>();
ArrayList<String> selectionClauses = new ArrayList<String>();
if (year > 0) {
selectionClauses.add(ScheduleContract.Videos.VIDEO_YEAR + "=?");
selectionArgs.add(String.format(Locale.US, "%d", year));
}
if (!TextUtils.isEmpty(topic)) {
selectionClauses.add(ScheduleContract.Videos.VIDEO_TOPIC + "=?");
selectionArgs.add(topic);
}
String selection = selectionClauses.isEmpty() ? null :
ParserUtils.joinStrings(" AND ", selectionClauses, null);
String[] args = selectionArgs.isEmpty() ? null : selectionArgs.toArray(new String[0]);
LOGD(TAG, "Starting videos query, selection=" + selection + " (year=" + year
+ ", topic=" + topic);
return new CursorLoader(getActivity(), ScheduleContract.Videos.CONTENT_URI,
VideosQuery.PROJECTION, selection, args, ScheduleContract.Videos.DEFAULT_SORT);
}
LOGW(TAG, "Invalid query token: " + token);
return null;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mCursor = data;
updateCollectionView();
Callbacks callbacks = getCallbacks();
if (callbacks != null) {
if (!mInformedYearsAndTopics) {
informAvailableYearsTo(callbacks);
informAvailableTopicsTo(callbacks);
mInformedYearsAndTopics = true;
}
} else {
LOGW(TAG, "No callbacks to inform filters to. Video filtering will probably not work.");
}
}
private void informAvailableYearsTo(Callbacks callbacks) {
ArrayList<Integer> years = new ArrayList<Integer>();
mCursor.moveToPosition(-1);
while (mCursor.moveToNext()) {
if (!years.contains(mCursor.getInt(VideosQuery.YEAR))) {
years.add(mCursor.getInt(VideosQuery.YEAR));
}
}
callbacks.onAvailableVideoYearsChanged(years);
}
private void informAvailableTopicsTo(Callbacks callbacks) {
ArrayList<String> topics = new ArrayList<String>();
mCursor.moveToPosition(-1);
while (mCursor.moveToNext()) {
String topic = mCursor.getString(VideosQuery.TOPIC);
if (!TextUtils.isEmpty(topic) && !topics.contains(topic)) {
topics.add(mCursor.getString(VideosQuery.TOPIC));
}
}
callbacks.onAvailableVideoTopicsChanged(topics);
}
private void updateCollectionView() {
LOGD(TAG, "Updating video library collection view.");
CollectionView.InventoryGroup curGroup = null;
CollectionView.Inventory inventory = new CollectionView.Inventory();
mCursor.moveToPosition(-1);
int dataIndex = -1;
int normalColumns = getResources().getInteger(R.integer.video_library_columns);
boolean isEmpty = mCursor.getCount() == 0;
while (mCursor.moveToNext()) {
++dataIndex;
String topic = mCursor.getString(VideosQuery.TOPIC);
boolean isHero = TextUtils.isEmpty(topic);
int year = mCursor.getInt(VideosQuery.YEAR);
String groupName = TextUtils.isEmpty(topic) ?
getString(R.string.google_i_o_year, year) : topic + " (" + year + ")";
if (curGroup == null || !curGroup.getHeaderLabel().equals(groupName)) {
if (curGroup != null) {
inventory.addGroup(curGroup);
}
curGroup = new CollectionView.InventoryGroup(
isHero ? GROUP_ID_HERO : GROUP_ID_NORMAL)
.setDataIndexStart(dataIndex)
.setHeaderLabel(groupName)
.setShowHeader(true)
.setDisplayCols(isHero ? 1 : normalColumns)
.setItemCount(1);
} else {
curGroup.incrementItemCount();
}
}
if (curGroup != null) {
inventory.addGroup(curGroup);
}
mCollectionView.setCollectionAdapter(this);
mCollectionView.updateInventory(inventory);
mEmptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
}
@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 headerLabel) {
((TextView) view.findViewById(android.R.id.text1)).setText(headerLabel);
}
@Override
public View newCollectionItemView(Context context, int groupId, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
// TODO: if groupId is GROUP_ID_HERO, inflate different layout
return inflater.inflate(R.layout.video_library_item, parent, false);
}
@Override
public void bindCollectionItemView(Context context, View view, int groupId,
int indexInGroup, int dataIndex, Object tag) {
if (!mCursor.moveToPosition(dataIndex)) {
return;
}
ImageView thumbnailView = (ImageView) view.findViewById(R.id.thumbnail);
TextView titleView = (TextView) view.findViewById(R.id.title);
TextView speakersView = (TextView) view.findViewById(R.id.speakers);
TextView descriptionView = (TextView) view.findViewById(R.id.description);
titleView.setText(mCursor.getString(VideosQuery.TITLE));
speakersView.setText(mCursor.getString(VideosQuery.SPEAKERS));
descriptionView.setText(mCursor.getString(VideosQuery.DESC));
String thumbUrl = mCursor.getString(VideosQuery.THUMBNAIL_URL);
if (TextUtils.isEmpty(thumbUrl)) {
thumbnailView.setImageResource(android.R.color.transparent);
} else {
mImageLoader.loadImage(thumbUrl, thumbnailView);
}
final String videoId = mCursor.getString(VideosQuery.VIDEO_ID);
final String youtubeLink = TextUtils.isEmpty(videoId) ? "" :
videoId.contains("://") ? videoId :
String.format(Locale.US, Config.VIDEO_LIBRARY_URL_FMT, videoId);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!TextUtils.isEmpty(youtubeLink)) {
LOGD(TAG, "Launching Youtube video: " + youtubeLink);
/* [ANALYTICS:EVENT]
* TRIGGER: Click on a video on the Video Library screen.
* CATEGORY: 'Video Library'
* ACTION: selectvideo
* LABEL: video's YouTube URL, http://www.youtube.com/...
* [/ANALYTICS]
*/
AnalyticsManager.sendEvent("Video Library", "selectvideo", youtubeLink, 0L);
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(youtubeLink));
UIUtils.preferPackageForIntent(getActivity(), i,
UIUtils.YOUTUBE_PACKAGE_NAME);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
startActivity(i);
}
}
});
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
private Callbacks getCallbacks() {
Activity act = getActivity();
if (act == null || !(act instanceof Callbacks)) {
return null;
}
return (Callbacks) act;
}
private interface VideosQuery {
int _TOKEN = 0x1;
String[] PROJECTION = {
BaseColumns._ID,
ScheduleContract.Videos.VIDEO_ID,
ScheduleContract.Videos.VIDEO_YEAR,
ScheduleContract.Videos.VIDEO_TITLE,
ScheduleContract.Videos.VIDEO_DESC,
ScheduleContract.Videos.VIDEO_VID,
ScheduleContract.Videos.VIDEO_TOPIC,
ScheduleContract.Videos.VIDEO_SPEAKERS,
ScheduleContract.Videos.VIDEO_THUMBNAIL_URL
};
int _ID = 0;
int VIDEO_ID = 1;
int YEAR = 2;
int TITLE = 3;
int DESC = 4;
int VID = 5;
int TOPIC = 6;
int SPEAKERS = 7;
int THUMBNAIL_URL = 8;
}
}