/*
* 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.explore;
import android.app.Fragment;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.samples.apps.iosched.R;
import com.google.samples.apps.iosched.explore.ExploreSessionsModel.ExploreSessionsQuery;
import com.google.samples.apps.iosched.model.TagMetadata;
import com.google.samples.apps.iosched.provider.ScheduleContract;
import com.google.samples.apps.iosched.ui.BaseActivity;
import com.google.samples.apps.iosched.ui.widget.DrawShadowFrameLayout;
import com.google.samples.apps.iosched.util.LogUtils;
import com.google.samples.apps.iosched.util.UIUtils;
import java.lang.ref.WeakReference;
/**
* A fragment that shows the sessions based on the specific {@code Uri} that is part of the
* arguments.
*/
public class ExploreSessionsFragment extends Fragment implements
LoaderManager.LoaderCallbacks<Cursor> {
private static final String TAG = LogUtils.makeLogTag(ExploreSessionsFragment.class);
private static final int TAG_METADATA_TOKEN = 0x8;
private static final String STATE_CURRENT_URI =
"com.google.samples.apps.iosched.explore.STATE_CURRENT_URI";
private static final String STATE_SESSION_QUERY_TOKEN =
"com.google.samples.apps.iosched.explore.STATE_SESSION_QUERY_TOKEN";
private static final String STATE_SHOW_LIVESTREAMED_SESSIONS =
"com.google.samples.apps.iosched.explore.EXTRA_SHOW_LIVESTREAMED_SESSIONS";
public static final String EXTRA_SHOW_LIVESTREAMED_SESSIONS =
"com.google.samples.apps.iosched.explore.EXTRA_SHOW_LIVESTREAMED_SESSIONS";
/**
* The delay before actual re-querying in milli seconds.
*/
private static final long QUERY_UPDATE_DELAY_MILLIS = 100;
private RecyclerView mSessionList;
private View mEmptyView;
private SessionsAdapter mSessionsAdapter;
private Uri mCurrentUri;
private int mSessionQueryToken;
private TagMetadata mTagMetadata;
private SearchHandler mSearchHandler = new SearchHandler(this);
/**
* Whether we should limit our selection to live streamed events.
*/
private boolean mShowLiveStreamedSessions;
/**
* Boolean that indicates whether the collectionView data is being fully reloaded in the case of
* filters and other query arguments changing VS just a data refresh.
*/
private boolean mFullReload = true;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.explore_sessions_frag, container, false);
mSessionList = (RecyclerView) rootView.findViewById(R.id.sessions_list);
mEmptyView = rootView.findViewById(android.R.id.empty);
getActivity().overridePendingTransition(0, 0);
return rootView;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLoaderManager().initLoader(TAG_METADATA_TOKEN, null, this);
// Setup the tag filters
if (savedInstanceState != null) {
mCurrentUri = savedInstanceState.getParcelable(STATE_CURRENT_URI);
mSessionQueryToken = savedInstanceState.getInt(STATE_SESSION_QUERY_TOKEN);
mShowLiveStreamedSessions = savedInstanceState
.getBoolean(STATE_SHOW_LIVESTREAMED_SESSIONS);
if (mSessionQueryToken > 0) {
// Only if this is a config change should we initLoader(), to reconnect with an
// existing loader. Otherwise, the loader will be initStaticDataAndObservers'd
// when reloadFromArguments
// is called.
getLoaderManager().initLoader(mSessionQueryToken, null, this);
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(STATE_CURRENT_URI, mCurrentUri);
outState.putInt(STATE_SESSION_QUERY_TOKEN, mSessionQueryToken);
outState.putBoolean(STATE_SHOW_LIVESTREAMED_SESSIONS, mShowLiveStreamedSessions);
}
@Override
public void onResume() {
super.onResume();
getActivity().invalidateOptionsMenu();
final DrawShadowFrameLayout drawShadowFrameLayout =
(DrawShadowFrameLayout) getActivity().findViewById(R.id.main_content);
if (drawShadowFrameLayout != null) {
// configure session fragment's top clearance to take our overlaid Toolbar into account.
drawShadowFrameLayout.setShadowTopOffset(UIUtils.calculateActionBarSize(getActivity()));
}
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) {
case ExploreSessionsModel.ExploreSessionsQuery.NORMAL_TOKEN:
return new CursorLoader(getActivity(),
mCurrentUri, ExploreSessionsModel.ExploreSessionsQuery.NORMAL_PROJECTION,
mShowLiveStreamedSessions ?
ScheduleContract.Sessions.LIVESTREAM_OR_YOUTUBE_URL_SELECTION :
null,
null,
ScheduleContract.Sessions.SORT_BY_TYPE_THEN_TIME);
case ExploreSessionsModel.ExploreSessionsQuery.SEARCH_TOKEN:
return new CursorLoader(getActivity(),
mCurrentUri, ExploreSessionsModel.ExploreSessionsQuery.SEARCH_PROJECTION,
mShowLiveStreamedSessions ?
ScheduleContract.Sessions.LIVESTREAM_OR_YOUTUBE_URL_SELECTION :
null,
null,
ScheduleContract.Sessions.SORT_BY_TYPE_THEN_TIME);
case TAG_METADATA_TOKEN:
return TagMetadata.createCursorLoader(getActivity());
default:
return null;
}
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
switch (loader.getId()) {
case ExploreSessionsQuery.NORMAL_TOKEN: // fall through
case ExploreSessionsQuery.SEARCH_TOKEN:
reloadSessionData(cursor);
break;
case TAG_METADATA_TOKEN:
mTagMetadata = new TagMetadata(cursor);
break;
default:
cursor.close();
}
}
private void reloadSessionData(Cursor cursor) {
mSessionList.setAdapter(null);
mSessionsAdapter = null;
final ExploreSessionsModel model = new ExploreSessionsModel(cursor, getActivity());
if (model.getSessionData() == null || model.getSessionData().isEmpty()) {
mEmptyView.setVisibility(View.VISIBLE);
return;
}
final GridLayoutManager glm = (GridLayoutManager) mSessionList.getLayoutManager();
mSessionsAdapter = SessionsAdapter.createVerticalGrid(
getActivity(), model.getSessionData(), glm.getSpanCount());
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(final int position) {
if (mSessionsAdapter == null) {
return 0;
}
return mSessionsAdapter.getSpanSize(position);
}
});
mSessionList.setAdapter(mSessionsAdapter);
mEmptyView.setVisibility(View.GONE);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) { }
public void reloadFromArguments(Bundle bundle) {
Uri oldUri = mCurrentUri;
int oldSessionQueryToken = mSessionQueryToken;
boolean oldShowLivestreamedSessions = mShowLiveStreamedSessions;
mCurrentUri = bundle.getParcelable("_uri");
if (ScheduleContract.Sessions.isSearchUri(mCurrentUri)) {
mSessionQueryToken = ExploreSessionsModel.ExploreSessionsQuery.SEARCH_TOKEN;
} else {
mSessionQueryToken = ExploreSessionsModel.ExploreSessionsQuery.NORMAL_TOKEN;
}
mShowLiveStreamedSessions = bundle.getBoolean(EXTRA_SHOW_LIVESTREAMED_SESSIONS, false);
if ((oldUri != null && oldUri.equals(mCurrentUri)) &&
oldSessionQueryToken == mSessionQueryToken &&
oldShowLivestreamedSessions == mShowLiveStreamedSessions) {
mFullReload = false;
getLoaderManager().initLoader(mSessionQueryToken, null, this);
} else {
// We need to re-run the query
mFullReload = true;
getLoaderManager().restartLoader(mSessionQueryToken, null, this);
}
}
public void requestQueryUpdate(String query) {
mSearchHandler.removeMessages(SearchHandler.MESSAGE_QUERY_UPDATE);
mSearchHandler.sendMessageDelayed(Message.obtain(mSearchHandler,
SearchHandler.MESSAGE_QUERY_UPDATE, query), QUERY_UPDATE_DELAY_MILLIS);
}
/**
* {@code Handler} that sends search queries to the ExploreSessionsFragment.
*/
private static class SearchHandler extends Handler {
public static final int MESSAGE_QUERY_UPDATE = 1;
private final WeakReference<ExploreSessionsFragment> mFragmentReference;
SearchHandler(ExploreSessionsFragment fragment) {
mFragmentReference = new WeakReference<>(fragment);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_QUERY_UPDATE:
String query = (String) msg.obj;
ExploreSessionsFragment instance = mFragmentReference.get();
if (instance != null) {
instance.reloadFromArguments(BaseActivity.intentToFragmentArguments(
new Intent(Intent.ACTION_SEARCH,
ScheduleContract.Sessions.buildSearchUri(query))));
}
break;
default:
break;
}
}
}
}