org.level28.android.moca.ui.schedule.ScheduleActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.level28.android.moca.ui.schedule.ScheduleActivity.java

Source

// @formatter:off
/*
 * ScheduleActivity.java - display MOCA schedule
 * Copyright (C) 2012 Matteo Panella <morpheus@level28.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
// @formatter:on

package org.level28.android.moca.ui.schedule;

import static android.widget.AdapterView.INVALID_ROW_ID;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkElementIndex;
import static com.google.common.base.Preconditions.checkNotNull;

import org.level28.android.moca.R;
import org.level28.android.moca.provider.ScheduleContract;
import org.level28.android.moca.provider.ScheduleContract.Sessions;
import org.level28.android.moca.sync.MocaAuthenticator;

import android.accounts.Account;
import android.content.ContentResolver;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.NavUtils;
import android.text.TextUtils;
import android.widget.ArrayAdapter;
import android.widget.SpinnerAdapter;

import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;

/**
 * Display MOCA schedule for talks and main events.
 * 
 * @author Matteo Panella
 */
public class ScheduleActivity extends SherlockFragmentActivity
        implements ActionBar.OnNavigationListener, SessionListFragment.OnSessionSelectedListener {

    private static final String CURRENT_DAY_KEY = "currentDay";
    private static final String CURRENT_SESSION_KEY = "currentSession";
    private static final String SESSION_LIST_ID_KEY = "sessionListId";

    static final String DETAILS_FRAGMENT_TAG = "sessionDetails";

    // Used in dual-pane mode only
    private String mCurrentSessionUUID;
    private long mCurrentSessionRowId = INVALID_ROW_ID;

    // Day 2 sunrise: 2012-08-25T00:00:00+02:00
    private static final long DAY2_SUNRISE = 1345845600000L;
    // Day 3 sunrise: 2012-08-26T00:00:00+02:00
    private static final long DAY3_SUNRISE = 1345932000000L;

    private CharSequence[] mScheduleDays;

    private int mCurrentDay;

    private SessionListFragment mSessionListFragment;
    private SessionDetailFragment mSessionDetailsFragment;

    private SpinnerAdapter mSpinnerAdapter;

    private boolean mDualPane = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.schedule);

        // Check if we're running in dual-pane mode
        mDualPane = getResources().getBoolean(R.bool.dualPaned);

        // Lookup our fragments
        final FragmentManager fm = getSupportFragmentManager();
        mSessionListFragment = (SessionListFragment) fm.findFragmentById(R.id.sessionListFragment);
        mSessionDetailsFragment = (SessionDetailFragment) fm.findFragmentById(R.id.sessionDetailsFragment);

        // Session list should be selectable only in dual-pane mode
        mSessionListFragment.setSelectable(mDualPane);

        // Unpack day labels from resources and create a SpinnerAdapter suitable
        // for the ActionBar navigation mode
        mScheduleDays = getResources().getTextArray(R.array.scheduleDays);
        mSpinnerAdapter = new ArrayAdapter<CharSequence>(getSupportActionBar().getThemedContext(),
                com.actionbarsherlock.R.layout.sherlock_spinner_dropdown_item, mScheduleDays);

        // Choose a sensible default day based on current date/time
        final long now = System.currentTimeMillis();
        if (now < DAY2_SUNRISE) {
            mCurrentDay = 1;
        } else if (now < DAY3_SUNRISE) {
            mCurrentDay = 2;
        } else {
            mCurrentDay = 3;
        }

        if (savedInstanceState == null) {
            // Force a refresh on startup
            triggerRefresh();
        } else {
            restoreState(savedInstanceState);
        }

        // Perform ActionBar setup
        setupActionBar();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mSessionListFragment.setOnSessionSelectedListener(this);
    }

    @Override
    protected void onStop() {
        mSessionListFragment.setOnSessionSelectedListener(null);
        super.onStop();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // Retain the currently selected day and session id (if any)
        outState.putInt(CURRENT_DAY_KEY, mCurrentDay);
        outState.putString(CURRENT_SESSION_KEY, mCurrentSessionUUID);
        outState.putLong(SESSION_LIST_ID_KEY, mCurrentSessionRowId);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        restoreState(savedInstanceState);
    }

    @Override
    protected void onDestroy() {
        mSpinnerAdapter = null;
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getSupportMenuInflater().inflate(R.menu.refresh_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            // Go up the stack
            NavUtils.navigateUpFromSameTask(this);
            return true;
        case R.id.menu_refresh:
            triggerRefresh();
            return true;
        }

        return false;
    }

    @Override
    public boolean onNavigationItemSelected(final int position, final long id) {
        checkElementIndex(position, mScheduleDays.length);
        // Update the current day and refresh all fragments
        setScheduleDay(position + 1);
        return true;
    }

    @Override
    public void onSessionSelected(final String sessionId, final long listItemId) {
        checkNotNull(sessionId);
        // Store the rowId for dual-pane mode lifecycle
        mCurrentSessionRowId = listItemId;
        displaySessionDetails(sessionId);
    }

    /**
     * Perform a manual synchronization
     */
    private void triggerRefresh() {
        Bundle extras = new Bundle();
        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
        ContentResolver.requestSync(
                new Account(MocaAuthenticator.HARDCODED_USERNAME, MocaAuthenticator.ACCOUNT_TYPE),
                ScheduleContract.CONTENT_AUTHORITY, extras);
    }

    /**
     * Restore activity state during some of the more involved lifecycle events.
     * 
     * @param savedInstanceState
     *            the previously saved state of this activity
     */
    private void restoreState(Bundle savedInstanceState) {
        // Restore selected day
        final int currentDay = savedInstanceState.getInt(CURRENT_DAY_KEY, mCurrentDay);
        // Safety check
        if (currentDay > 0 && currentDay < mScheduleDays.length) {
            mCurrentDay = currentDay;
        }
        // Restore selected session id
        mCurrentSessionUUID = savedInstanceState.getString(CURRENT_SESSION_KEY);

        // Now update the UI
        mSessionListFragment.loadScheduleForDay(mCurrentDay);
        if (mDualPane) {
            // Restore session list id
            mCurrentSessionRowId = savedInstanceState.getLong(SESSION_LIST_ID_KEY, INVALID_ROW_ID);
            mSessionListFragment.setSelectedId(mCurrentSessionRowId);
            displaySessionDetails(mCurrentSessionUUID);
        }
    }

    /**
     * Setup the action bar for list navigation mode.
     */
    private void setupActionBar() {
        final ActionBar actionBar = getSupportActionBar();
        // As usual, enable standard application icon navigation
        actionBar.setHomeButtonEnabled(true);
        actionBar.setDisplayHomeAsUpEnabled(true);
        // ... but hide the activity title, since it will be replaced by the
        // navigation mode spinner
        actionBar.setDisplayShowTitleEnabled(false);

        // Enable navigation mode
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
        actionBar.setListNavigationCallbacks(mSpinnerAdapter, this);

        // This will invoke refreshSchedule() through onNavigationItemSelected()
        actionBar.setSelectedNavigationItem(mCurrentDay - 1);
    }

    /**
     * Notify the {@link SessionListFragment} of a day change.
     * 
     * @param day
     *            the day that should be displayed
     */
    private void setScheduleDay(final int day) {
        mCurrentDay = day;

        // Update the schedule fragment
        mSessionListFragment.loadScheduleForDay(mCurrentDay);

        if (mDualPane) {
            // Clear the details fragment
            displaySessionDetails(null);
        }
    }

    /**
     * Display details for the given Session.
     * <p>
     * Depending on the UI mode, the details will be shown inline or a new
     * activity will be launched.
     * 
     * @param sessionId
     *            UUID of the session
     */
    private void displaySessionDetails(final String sessionId) {
        mCurrentSessionUUID = sessionId;
        if (mDualPane) {
            mSessionDetailsFragment.loadSessionDetails(sessionId);
        } else {
            // In single-pane mode we can't have a NULL sessionId
            checkNotNull(sessionId);
            checkArgument(!TextUtils.isEmpty(sessionId), "Session ID may not be empty");
            // Spawn the details activity
            startActivity(new Intent(Intent.ACTION_VIEW, Sessions.buildSessionUri(sessionId)));
        }
    }
}