com.infine.android.devoxx.ui.ScheduleFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.infine.android.devoxx.ui.ScheduleFragment.java

Source

/*
 * 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.infine.android.devoxx.ui;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Rect;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.BaseColumns;
import android.support.v4.app.Fragment;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.infine.android.devoxx.R;
import com.infine.android.devoxx.provider.ScheduleContract;
import com.infine.android.devoxx.ui.widget.BlockColumnType;
import com.infine.android.devoxx.ui.widget.BlockView;
import com.infine.android.devoxx.ui.widget.BlocksLayout;
import com.infine.android.devoxx.ui.widget.ObservableScrollView;
import com.infine.android.devoxx.ui.widget.Workspace;
import com.infine.android.devoxx.util.MotionEventUtils;
import com.infine.android.devoxx.util.NotifyingAsyncQueryHandler;
import com.infine.android.devoxx.util.ParserUtils;
import com.infine.android.devoxx.util.UIUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;

/**
 * Shows a horizontally-pageable calendar of conference days. Horizontaly paging
 * is achieved using {@link Workspace}, and the primary UI classes for rendering
 * the calendar are {@link com.infine.android.devoxx.ui.widget.TimeRulerView},
 * {@link BlocksLayout}, and {@link BlockView}.
 */
public class ScheduleFragment extends Fragment implements NotifyingAsyncQueryHandler.AsyncQueryListener,
        ObservableScrollView.OnScrollListener, View.OnClickListener {

    private static final String TAG = "ScheduleFragment";

    /**
     * Flags used with {@link android.text.format.DateUtils#formatDateRange}.
     */
    private static final int TIME_FLAGS = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY
            | DateUtils.FORMAT_ABBREV_WEEKDAY;

    public static final long[] eventDays = { ParserUtils.parseTime("2014-04-16T00:00:00.000+01:00"),
            ParserUtils.parseTime("2014-04-17T00:00:00.000+01:00"),
            ParserUtils.parseTime("2014-04-18T00:00:00.000+01:00"), };

    private static final int DISABLED_BLOCK_ALPHA = 100;

    private NotifyingAsyncQueryHandler mHandler;

    private Workspace mWorkspace;
    private TextView mTitle;
    private int mTitleCurrentDayIndex = -1;
    private View mLeftIndicator;
    private View mRightIndicator;

    /**
     * A helper class containing object references related to a particular day
     * in the schedule.
     */
    private class Day {
        private ViewGroup rootView;
        private ObservableScrollView scrollView;
        private View nowView;
        private BlocksLayout blocksView;

        private int index = -1;
        private String label = null;
        private Uri blocksUri = null;
        private long timeStart = -1;
        private long timeEnd = -1;
    }

    private List<Day> mDays = new ArrayList<Day>();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new NotifyingAsyncQueryHandler(getActivity().getContentResolver(), this);
        setHasOptionsMenu(true);
        //      AnalyticsUtils.getInstance(getActivity()).trackPageView("/Schedule");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_schedule, null);

        mWorkspace = (Workspace) root.findViewById(R.id.workspace);

        mTitle = (TextView) root.findViewById(R.id.block_title);

        mLeftIndicator = root.findViewById(R.id.indicator_left);
        mLeftIndicator.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if ((motionEvent.getAction() & MotionEventUtils.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
                    mWorkspace.scrollLeft();
                    return true;
                }
                return false;
            }
        });
        mLeftIndicator.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                mWorkspace.scrollLeft();
            }
        });

        mRightIndicator = root.findViewById(R.id.indicator_right);
        mRightIndicator.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if ((motionEvent.getAction() & MotionEventUtils.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
                    mWorkspace.scrollRight();
                    return true;
                }
                return false;
            }
        });
        mRightIndicator.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                mWorkspace.scrollRight();
            }
        });

        // creation des jours
        for (long day : eventDays) {
            setupDay(inflater, day);
        }
        // setupDay(inflater, TUE_START);
        // setupDay(inflater, WED_START);

        updateWorkspaceHeader(0);
        mWorkspace.setOnScrollListener(new Workspace.OnScrollListener() {
            public void onScroll(float screenFraction) {
                updateWorkspaceHeader(Math.round(screenFraction));
            }
        }, true);

        //      mWorkspace.setDrawingCacheEnabled(false);
        //      mLeftIndicator.setDrawingCacheEnabled(false);
        //      mRightIndicator.setDrawingCacheEnabled(false);
        ////      mWorkspace.setDrawingCacheEnabled(false);
        ////      mWorkspace.setDrawingCacheEnabled(false);
        ////      mWorkspace.setDrawingCacheEnabled(false);
        //      for(Day day : mDays){
        //         day.rootView.setDrawingCacheEnabled(false);
        //         day.scrollView.setDrawingCacheEnabled(false);
        //      }

        return root;
    }

    public void updateWorkspaceHeader(int dayIndex) {
        if (mTitleCurrentDayIndex == dayIndex) {
            return;
        }

        mTitleCurrentDayIndex = dayIndex;
        Day day = mDays.get(dayIndex);
        mTitle.setText(day.label);

        // affichage ou non des fleches de navigation entre les jours
        mLeftIndicator.setVisibility((dayIndex != 0) ? View.VISIBLE : View.INVISIBLE);
        mRightIndicator.setVisibility((dayIndex < mDays.size() - 1) ? View.VISIBLE : View.INVISIBLE);
    }

    private void setupDay(LayoutInflater inflater, long startMillis) {
        Day day = new Day();

        // Setup data
        day.index = mDays.size();
        day.timeStart = startMillis;
        day.timeEnd = startMillis + DateUtils.DAY_IN_MILLIS;
        day.blocksUri = ScheduleContract.Blocks.buildBlocksBetweenDirUri(day.timeStart, day.timeEnd);

        // Setup views
        day.rootView = (ViewGroup) inflater.inflate(R.layout.blocks_content, null);

        day.scrollView = (ObservableScrollView) day.rootView.findViewById(R.id.blocks_scroll);
        day.scrollView.setOnScrollListener(this);

        day.blocksView = (BlocksLayout) day.rootView.findViewById(R.id.blocks);
        day.nowView = day.rootView.findViewById(R.id.blocks_now);

        //      day.blocksView.setDrawingCacheEnabled(true);
        //      day.blocksView.setAlwaysDrawnWithCacheEnabled(true);

        TimeZone.setDefault(UIUtils.CONFERENCE_TIME_ZONE);
        day.label = DateUtils.formatDateTime(getActivity(), startMillis, TIME_FLAGS);

        mWorkspace.addView(day.rootView);
        mDays.add(day);
    }

    @Override
    public void onResume() {
        super.onResume();

        // Since we build our views manually instead of using an adapter, we
        // need to manually requery every time launched.
        requery();

        getActivity().getContentResolver().registerContentObserver(ScheduleContract.Sessions.CONTENT_URI, true,
                mSessionChangesObserver);

        // Start listening for time updates to adjust "now" bar. TIME_TICK is
        // triggered once per minute, which is how we move the bar over time.
        final IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_TIME_TICK);
        filter.addAction(Intent.ACTION_TIME_CHANGED);
        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
        getActivity().registerReceiver(mReceiver, filter, null, new Handler());
    }

    private void requery() {
        for (Day day : mDays) {
            mHandler.startQuery(0, day, day.blocksUri, BlocksQuery.PROJECTION, null, null,
                    ScheduleContract.Blocks.DEFAULT_SORT);
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getActivity().runOnUiThread(new Runnable() {
            public void run() {
                updateNowView(true);
            }
        });
    }

    @Override
    public void onPause() {
        super.onPause();
        getActivity().unregisterReceiver(mReceiver);
        getActivity().getContentResolver().unregisterContentObserver(mSessionChangesObserver);
    }

    /**
     * {@inheritDoc}
     */
    public void onQueryComplete(int token, Object cookie, Cursor cursor) {
        if (getActivity() == null) {
            return;
        }

        Day day = (Day) cookie;

        // Clear out any existing sessions before inserting again
        day.blocksView.removeAllBlocks();

        try {
            while (cursor.moveToNext()) {
                final String type = cursor.getString(BlocksQuery.BLOCK_CATEGORY);
                // final Integer column = sTypeColumnMap.get(type);
                BlockColumnType columnType = BlockColumnType.typeOf(type);
                if (columnType == null || columnType == BlockColumnType.UNDEFINED) {
                    continue;
                }

                final String blockId = cursor.getString(BlocksQuery.BLOCK_ID);
                final String title = cursor.getString(BlocksQuery.BLOCK_TITLE);
                final long start = cursor.getLong(BlocksQuery.BLOCK_START);
                final long end = cursor.getLong(BlocksQuery.BLOCK_END);
                final int nbStar = cursor.getInt(BlocksQuery.COUNT_STARRED);
                final String titleStarred = cursor.getString(BlocksQuery.TITLE_STARRED);

                // TODO : temporary to manage code story
                columnType = patchColumnType(columnType, start, end);

                String blockTitle = title;
                if (title.contains("Code-Story")) {
                    blockTitle = "Code story";
                }
                if (title.contains("Devoxx4Kids")) {
                    blockTitle = "Devoxx4Kids";
                }
                if (title.contains("Devops Mercenaries")) {
                    blockTitle = "Devops Mercenaries";
                }

                final BlockView blockView = new BlockView(getActivity(), blockId, blockTitle, start, end, nbStar,
                        titleStarred != null ? titleStarred : "", columnType);

                final int sessionsCount = cursor.getInt(BlocksQuery.SESSIONS_COUNT);
                if (sessionsCount > 0) {
                    blockView.setOnClickListener(this);
                } else {
                    blockView.setFocusable(false);
                    blockView.setEnabled(false);
                    LayerDrawable buttonDrawable = (LayerDrawable) blockView.getBackground();
                    buttonDrawable.getDrawable(0).setAlpha(DISABLED_BLOCK_ALPHA);
                    buttonDrawable.getDrawable(2).setAlpha(DISABLED_BLOCK_ALPHA);
                }

                day.blocksView.addBlock(blockView);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
    }

    /*
     * TODO : temporary to manage code story
     */
    private BlockColumnType patchColumnType(BlockColumnType columnType, long start, long end) {
        long durationInMs = end - start;
        long durationInHours = durationInMs / 1000 / 60 / 60;
        if (durationInHours >= 6) {
            return BlockColumnType.CODE_STORY;
        }
        return columnType;
    }

    /** {@inheritDoc} */
    public void onClick(View view) {
        if (view instanceof BlockView) {
            String title = ((BlockView) view).getText().toString();
            //         AnalyticsUtils.getInstance(getActivity()).trackEvent("Schedule", "Session Click", title, 0);
            final String blockId = ((BlockView) view).getBlockId();
            final Uri sessionsUri = ScheduleContract.Blocks.buildSessionsUri(blockId);

            final Intent intent = new Intent(Intent.ACTION_VIEW, sessionsUri);
            intent.putExtra(SessionsFragment.EXTRA_SCHEDULE_TIME_STRING, ((BlockView) view).getBlockTimeString());
            ((BaseActivity) getActivity()).openActivityOrFragment(intent);
        }
    }

    /**
     * Update position and visibility of "now" view.
     */
    private boolean updateNowView(boolean forceScroll) {
        final long now = System.currentTimeMillis();

        Day nowDay = null; // effectively Day corresponding to today
        for (Day day : mDays) {
            if (now >= day.timeStart && now <= day.timeEnd) {
                nowDay = day;
                day.nowView.setVisibility(View.VISIBLE);
            } else {
                day.nowView.setVisibility(View.GONE);
            }
        }

        if (nowDay != null && forceScroll) {
            // Scroll to show "now" in center
            mWorkspace.setCurrentScreen(nowDay.index);
            final int offset = nowDay.scrollView.getHeight() / 16;
            nowDay.nowView.requestRectangleOnScreen(new Rect(0, offset, 0, offset), true);
            nowDay.blocksView.requestLayout();
            return true;
        }

        return false;
    }

    public void onScrollChanged(ObservableScrollView view) {
        // Keep each day view at the same vertical scroll offset.
        final int scrollY = view.getScrollY();
        for (Day day : mDays) {
            if (day.scrollView != view) {
                day.scrollView.scrollTo(0, scrollY);
            }
        }
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.schedule_menu_items, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.menu_now) {
            if (!updateNowView(true)) {
                Toast.makeText(getActivity(), R.string.toast_now_not_visible, Toast.LENGTH_SHORT).show();
            }
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private ContentObserver mSessionChangesObserver = new ContentObserver(new Handler()) {
        @Override
        public void onChange(boolean selfChange) {
            requery();
        }
    };

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "onReceive time update");
            }
            updateNowView(false);
        }
    };

    private interface BlocksQuery {
        String[] PROJECTION = { BaseColumns._ID, ScheduleContract.Blocks.BLOCK_ID,
                ScheduleContract.Blocks.BLOCK_TITLE, ScheduleContract.Blocks.BLOCK_START,
                ScheduleContract.Blocks.BLOCK_END, ScheduleContract.Blocks.BLOCK_CATEGORY,
                ScheduleContract.Blocks.SESSIONS_COUNT, ScheduleContract.Blocks.COUNT_STARRED,
                ScheduleContract.Blocks.TITLE_STARRED };

        int _ID = 0;
        int BLOCK_ID = 1;
        int BLOCK_TITLE = 2;
        int BLOCK_START = 3;
        int BLOCK_END = 4;
        int BLOCK_CATEGORY = 5;
        int SESSIONS_COUNT = 6;
        int COUNT_STARRED = 7;
        int TITLE_STARRED = 8;
    }
}