/*
* 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.map;
import com.google.samples.apps.iosched.R;
import com.google.samples.apps.iosched.map.util.SingleSessionLoader;
import com.google.samples.apps.iosched.map.util.OverviewSessionLoader;
import com.google.samples.apps.iosched.map.util.MarkerModel;
import com.google.samples.apps.iosched.map.util.SessionLoader;
import com.google.samples.apps.iosched.model.ScheduleHelper;
import com.google.samples.apps.iosched.util.MapUtils;
import com.google.samples.apps.iosched.util.TimeUtils;
import com.google.samples.apps.iosched.util.UIUtils;
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager;
import android.content.Context;
import android.content.Loader;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
/**
* Displays information about the map.
* This includes a list of sessions that are directly loaded by this fragment.
*/
public abstract class MapInfoFragment extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor> {
private static final int QUERY_TOKEN_SESSION_ROOM = 0x1;
private static final int QUERY_TOKEN_SUBTITLE = 0x2;
private static final String QUERY_ARG_ROOMID = "roomid";
private static final String QUERY_ARG_ROOMTITLE = "roomtitle";
private static final String QUERY_ARG_ROOMTYPE = "roomicon";
protected TextView mTitle;
protected TextView mSubtitle;
protected ImageView mIcon;
protected RecyclerView mList;
protected Callback mCallback = sDummyCallback;
private static Callback sDummyCallback = new Callback() {
@Override
public void onInfoSizeChanged(int left, int top, int right, int bottom) {
}
@Override
public void onSessionClicked(String id) {
}
};
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof Callback)) {
throw new ClassCastException("Activity must implement fragment's callback.");
}
mCallback = (Callback) activity;
}
@Override
public void onDetach() {
super.onDetach();
mCallback = sDummyCallback;
}
@Nullable
@Override
public abstract View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState);
@Nullable
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState, int layout) {
View root = inflater.inflate(layout, container, false);
mTitle = (TextView) root.findViewById(R.id.map_info_title);
mSubtitle = (TextView) root.findViewById(R.id.map_info_subtitle);
mIcon = (ImageView) root.findViewById(R.id.map_info_icon);
mIcon.setColorFilter(getResources().getColor(R.color.my_schedule_icon_default));
mList = (RecyclerView) root.findViewById(R.id.map_info_list);
final Context context = mList.getContext();
mList.addItemDecoration(new DividerDecoration(context));
mList.setLayoutManager(new LinearLayoutManager(context));
return root;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (id != QUERY_TOKEN_SESSION_ROOM && id != QUERY_TOKEN_SUBTITLE) {
return null;
}
final long time = TimeUtils.getCurrentTime(getActivity());
final String roomId = args.getString(QUERY_ARG_ROOMID);
final String roomTitle = args.getString(QUERY_ARG_ROOMTITLE);
final int roomType = args.getInt(QUERY_ARG_ROOMTYPE);
if (id == QUERY_TOKEN_SESSION_ROOM) {
return new OverviewSessionLoader(getActivity(), roomId, roomTitle, roomType, time);
} else if (id == QUERY_TOKEN_SUBTITLE) {
return new SingleSessionLoader(getActivity(), roomId, roomTitle, roomType);
}
return null;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (getActivity() == null) {
return;
}
switch (loader.getId()) {
case QUERY_TOKEN_SESSION_ROOM: {
SessionLoader sessionLoader = (SessionLoader) loader;
showSessionList(sessionLoader.getRoomTitle(), sessionLoader.getRoomType(), cursor);
break;
}
case QUERY_TOKEN_SUBTITLE: {
SessionLoader sessionLoader = (SessionLoader) loader;
showSessionSubtitle(sessionLoader.getRoomTitle(), sessionLoader.getRoomType(),
cursor);
}
}
}
/**
* Creates a new instance depending of the form factor of the device.
* For tablets, creates an {@link com.google.samples.apps.iosched.map.InlineInfoFragment},
* for other form factors a {@link com.google.samples.apps.iosched.map.SlideableInfoFragment}.
*
* @see com.google.samples.apps.iosched.util.UIUtils#isTablet(android.content.Context)
*/
public static MapInfoFragment newInstace(Context c) {
if (UIUtils.isTablet(c)) {
return InlineInfoFragment.newInstance();
} else {
return SlideableInfoFragment.newInstance();
}
}
private void showSessionList(String roomTitle, int roomType, Cursor sessions) {
if (sessions == null || sessions.isAfterLast()) {
onSessionLoadingFailed(roomTitle, roomType);
return;
}
onSessionsLoaded(roomTitle, roomType, sessions);
mList.setAdapter(new SessionAdapter(getActivity(), sessions,
MapUtils.hasInfoSessionListIcons(roomType), mOnClickListener));
}
private void showSessionSubtitle(String roomTitle, int roomType, Cursor sessions) {
if (sessions == null || sessions.isAfterLast()) {
onSessionLoadingFailed(roomTitle, roomType);
return;
}
sessions.moveToFirst();
final String title = roomTitle;
final String subtitle = sessions.getString(SingleSessionLoader.Query.SESSION_ABSTRACT);
setHeader(MapUtils.getRoomIcon(roomType), title, subtitle);
mList.setVisibility(View.GONE);
onRoomSubtitleLoaded(title, roomType, subtitle);
}
/**
* Called when the subtitle has been loaded for a room.
*/
protected void onRoomSubtitleLoaded(String roomTitle, int roomType, String subTitle){
}
/**
* Called when the session list is about to be loaded for a new room.
*/
protected void onSessionListLoading(String roomId, String roomTitle) {
// No default behavior
}
/**
* Prepares and starts a SessionLoader for the specified query token.
*/
private void loadSessions(String roomId, String roomTitle, int roomType, int queryToken){
setHeader(MapUtils.getRoomIcon(roomType), roomTitle, null);
onSessionListLoading(roomId, roomTitle);
// Load the following sessions for this room
LoaderManager lm = getLoaderManager();
Bundle args = new Bundle();
args.putString(QUERY_ARG_ROOMID, roomId);
args.putString(QUERY_ARG_ROOMTITLE, roomTitle);
args.putInt(QUERY_ARG_ROOMTYPE, roomType);
lm.restartLoader(queryToken, args, this);
}
/**
* Called when the abstract of the first session in this room is to be used as the subtitle.
*/
public void showFirstSessionTitle(String roomId, String roomTitle, int roomType) {
loadSessions(roomId,roomTitle,roomType, QUERY_TOKEN_SUBTITLE);
}
/**
* Called when a session list is to be displayed and has to be loaded.
*/
public void showSessionList(String roomId, String roomTitle, int roomType) {
loadSessions(roomId,roomTitle,roomType, QUERY_TOKEN_SESSION_ROOM);
}
protected void onSessionsLoaded(String roomTitle, int roomType, Cursor cursor) {
setHeader(MapUtils.getRoomIcon(roomType), roomTitle, null);
mList.setVisibility(View.VISIBLE);
}
protected void onSessionLoadingFailed(String roomTitle, int roomType) {
setHeader(MapUtils.getRoomIcon(roomType), roomTitle, null);
mList.setVisibility(View.GONE);
}
public void showVenue() {
setHeader(MapUtils.getRoomIcon(MarkerModel.TYPE_VENUE), R.string.map_venue_name,
R.string.map_venue_address);
mList.setVisibility(View.GONE);
}
protected void setHeader(int icon, int title, int subTitle) {
mIcon.setImageResource(icon);
if (title != 0) {
mTitle.setText(title);
mTitle.setVisibility(View.VISIBLE);
} else {
mTitle.setVisibility(View.GONE);
}
if (subTitle != 0) {
mSubtitle.setText(subTitle);
mSubtitle.setVisibility(View.VISIBLE);
} else {
mSubtitle.setVisibility(View.GONE);
}
}
private void setHeader(int icon, String title, String subTitle) {
mIcon.setImageResource(icon);
if (title != null && !title.isEmpty()) {
mTitle.setText(title);
mTitle.setVisibility(View.VISIBLE);
} else {
mTitle.setVisibility(View.GONE);
}
if (subTitle != null && !subTitle.isEmpty()) {
mSubtitle.setText(subTitle);
mSubtitle.setVisibility(View.VISIBLE);
} else {
mSubtitle.setVisibility(View.GONE);
}
}
public void showTitleOnly(int roomType, String title) {
setHeader(MapUtils.getRoomIcon(roomType), title, null);
mList.setVisibility(View.GONE);
}
public abstract void hide();
public abstract boolean isExpanded();
public abstract void minimize();
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
interface Callback {
public void onInfoSizeChanged(int left, int top, int right, int bottom);
public void onSessionClicked(String id);
}
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(final View v) {
String sessionId = (String) v.getTag(R.id.tag_session_id);
if (sessionId != null) {
mCallback.onSessionClicked(sessionId);
}
}
};
private static class DividerDecoration extends RecyclerView.ItemDecoration {
private final Paint mPaint = new Paint();
private final int mHeight;
public DividerDecoration(Context context) {
final Resources resources = context.getResources();
mPaint.setColor(ResourcesCompat.getColor(resources, R.color.divider,
context.getTheme()));
mHeight = resources.getDimensionPixelSize(R.dimen.divider_height);
}
@Override
public void getItemOffsets(final Rect outRect, final View view, final RecyclerView parent,
final RecyclerView.State state) {
outRect.set(0, 0, 0, mHeight);
}
@Override
public void onDraw(final Canvas c, final RecyclerView parent,
final RecyclerView.State state) {
int width = parent.getWidth();
for (int i = 0, count = parent.getChildCount(); i < count; i++) {
View child = parent.getChildAt(i);
int bottom = child.getBottom();
c.drawRect(0, bottom, width, bottom + mHeight, mPaint);
}
}
}
/**
* Adapter that displays a list of sessions.
* This includes its title, time slot and icon.
*/
private static class SessionAdapter extends RecyclerView.Adapter<ItemHolder> {
private final StringBuilder mStringBuilder = new StringBuilder();
private final Context mContext;
private final Cursor mCursor;
private final boolean mDisplayIcons;
private final View.OnClickListener mListener;
public SessionAdapter(Context context, Cursor cursor, boolean displayIcons,
View.OnClickListener listener) {
mContext = context;
mCursor = cursor;
mDisplayIcons = displayIcons;
mListener = listener;
}
public String getSessionIdAtPosition(int position) {
if (mCursor.moveToPosition(position)) {
return mCursor.getString(OverviewSessionLoader.Query.SESSION_ID);
} else {
return null;
}
}
@Override
public ItemHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
final ItemHolder holder = new ItemHolder(LayoutInflater.from(mContext), parent);
holder.image.setVisibility(mDisplayIcons ? View.VISIBLE : View.INVISIBLE);
holder.itemView.setOnClickListener(mListener);
return holder;
}
@Override
public void onBindViewHolder(final ItemHolder holder, final int position) {
mCursor.moveToPosition(position);
final String title = mCursor.getString(OverviewSessionLoader.Query.SESSION_TITLE);
final String sessionId = mCursor.getString(OverviewSessionLoader.Query.SESSION_ID);
final long blockStart = mCursor.getLong(OverviewSessionLoader.Query.SESSION_START);
final long blockEnd = mCursor.getLong(OverviewSessionLoader.Query.SESSION_END);
final String sessionTag = mCursor.getString(OverviewSessionLoader.Query.SESSION_TAGS);
final int sessionType = ScheduleHelper.detectSessionType(sessionTag);
final String text = UIUtils.formatIntervalTimeString(blockStart, blockEnd,
mStringBuilder, mContext);
holder.itemView.setTag(R.id.tag_session_id, sessionId);
holder.title.setText(title);
holder.text.setText(text);
if (mDisplayIcons) {
holder.image.setImageResource(UIUtils.getSessionIcon(sessionType));
}
}
@Override
public int getItemCount() {
return mCursor.getCount();
}
}
private static class ItemHolder extends RecyclerView.ViewHolder {
TextView title;
TextView text;
ImageView image;
public ItemHolder(LayoutInflater inflater, ViewGroup parent) {
super(inflater.inflate(R.layout.map_item_session, parent, false));
title = (TextView) itemView.findViewById(R.id.map_item_title);
text = (TextView) itemView.findViewById(R.id.map_item_text);
image = (ImageView) itemView.findViewById(R.id.map_item_image);
}
}
}