edu.mit.mobile.android.locast.ver2.itineraries.ItineraryDetail.java Source code

Java tutorial

Introduction

Here is the source code for edu.mit.mobile.android.locast.ver2.itineraries.ItineraryDetail.java

Source

package edu.mit.mobile.android.locast.ver2.itineraries;
/*
 * Copyright (C) 2011  MIT Mobile Experience Lab
 *
 * 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.
 */

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;

import android.accounts.Account;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SyncStatusObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4_map.app.LoaderManager;
import android.support.v4_map.app.MapFragmentActivity;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;

import edu.mit.mobile.android.imagecache.ImageCache;
import edu.mit.mobile.android.imagecache.ImageLoaderAdapter;
import edu.mit.mobile.android.locast.Constants;
import edu.mit.mobile.android.locast.accounts.Authenticator;
import edu.mit.mobile.android.locast.casts.CastCursorAdapter;
import edu.mit.mobile.android.locast.data.Cast;
import edu.mit.mobile.android.locast.data.Itinerary;
import edu.mit.mobile.android.locast.data.MediaProvider;
import edu.mit.mobile.android.locast.maps.CastsIconOverlay;
import edu.mit.mobile.android.locast.sync.LocastSyncService;
import edu.mit.mobile.android.locast.ver2.R;
import edu.mit.mobile.android.locast.ver2.browser.BrowserHome;
import edu.mit.mobile.android.widget.RefreshButton;

public class ItineraryDetail extends MapFragmentActivity implements LoaderManager.LoaderCallbacks<Cursor>,
        OnItemClickListener, OnClickListener, DialogInterface.OnClickListener {
    private static final String TAG = ItineraryDetail.class.getSimpleName();

    // if the layout for this doesn't need a map, set this to false.
    private static final boolean USE_MAP = true;
    private static final int DIALOG_CASTS = 0;

    private MapView mMapView;
    private MapController mMapController;

    private ListView mCastView;
    private CastCursorAdapter mCastAdapter;

    private ImageCache mImageCache;

    private Uri mUri;
    private Uri mCastsUri;

    private CastsIconOverlay mCastsOverlay;
    private PathOverlay mPathOverlay;
    private static final int UNKNOWN_COUNT = -1;
    private int mItineraryCastCount = UNKNOWN_COUNT;

    private Timer clickTimer;
    private final boolean markerClicked = false;
    private final ArrayList<OverlayItem> clickedItems = new ArrayList<OverlayItem>();

    CursorLoader itinLoader;
    CursorLoader castLoader;

    private boolean mFirstLoadSync = true;

    private static final String[] ITINERARY_PROJECTION = new String[] { Itinerary._ID, Itinerary._DESCRIPTION,
            Itinerary._TITLE, Itinerary._CASTS_COUNT, Itinerary._PATH };

    private static final int MSG_SET_REFRESHING = 100, MSG_SET_NOT_REFRESHING = 101;

    private RefreshButton mRefresh;

    private Object mSyncHandle;

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_SET_REFRESHING:
                Log.d(TAG, "refreshing...");
                ((TextView) ((findViewById(android.R.id.empty)).findViewById(R.id.empty_message)))
                        .setText(R.string.loading_data);
                mRefresh.setRefreshing(true);
                break;

            case MSG_SET_NOT_REFRESHING:
                Log.d(TAG, "done loading.");
                ((TextView) ((findViewById(android.R.id.empty)).findViewById(R.id.empty_message)))
                        .setText(R.string.itinerary_no_casts);
                mRefresh.setRefreshing(false);
                break;
            }
        };
    };

    @Override
    protected void onCreate(Bundle icicle) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        super.onCreate(icicle);
        setContentView(R.layout.itinerary_detail);

        mImageCache = ImageCache.getInstance(this);
        clickTimer = new Timer();

        mCastView = (ListView) findViewById(R.id.casts);
        findViewById(R.id.refresh).setOnClickListener(this);
        findViewById(R.id.home).setOnClickListener(this);

        final LayoutInflater layoutInflater = getLayoutInflater();

        mCastView.addHeaderView(layoutInflater.inflate(R.layout.itinerary_detail_list_header, mCastView, false),
                null, false);
        mCastView.addFooterView(layoutInflater.inflate(R.layout.list_footer, null), null, false);
        mCastView.setEmptyView(findViewById(android.R.id.empty));
        mRefresh = (RefreshButton) findViewById(R.id.refresh);
        mRefresh.setOnClickListener(this);

        final View addCast = findViewById(R.id.add_cast);
        addCast.setOnClickListener(this);
        if (Constants.CAN_CREATE_CASTS) {
            addCast.setVisibility(View.VISIBLE);
        }
        mCastView.setOnItemClickListener(this);

        mCastView.setAdapter(null);
        registerForContextMenu(mCastView);

        if (USE_MAP) {
            mMapView = (MapView) findViewById(R.id.map);
            mMapController = mMapView.getController();
        }

        final Intent intent = getIntent();
        final String action = intent.getAction();

        if (Intent.ACTION_VIEW.equals(action)) {
            mUri = intent.getData();

            mCastsUri = Itinerary.getCastsUri(mUri);

            final LoaderManager lm = getSupportLoaderManager();
            Bundle args = new Bundle();
            args.putParcelable(LOADER_ARG_DATA, mUri);
            lm.initLoader(LOADER_ITINERARY, args, this);

            args = new Bundle();
            args.putParcelable(LOADER_ARG_DATA, mCastsUri);
            lm.initLoader(LOADER_CASTS, args, this);

            initCastList();

        } else {
            finish();
        }
    }

    @Override
    protected void onResume() {
        mFirstLoadSync = true;
        super.onResume();
        mSyncHandle = ContentResolver.addStatusChangeListener(0xff, new SyncStatusObserver() {

            @Override
            public void onStatusChanged(int which) {
                final Account a = Authenticator.getFirstAccount(ItineraryDetail.this);
                if (!ContentResolver.isSyncActive(a, MediaProvider.AUTHORITY)
                        && !ContentResolver.isSyncPending(a, MediaProvider.AUTHORITY)) {
                    Log.d(TAG, "Sync finished, should refresh naw!!");
                    mHandler.sendEmptyMessage(MSG_SET_NOT_REFRESHING);

                } else {
                    Log.d(TAG, "Sync Active or Pending!!");
                    mHandler.sendEmptyMessage(MSG_SET_REFRESHING);
                }
            }
        });
        //check if synch is in progress
        final Account a = Authenticator.getFirstAccount(ItineraryDetail.this);
        if (!ContentResolver.isSyncActive(a, MediaProvider.AUTHORITY)
                && !ContentResolver.isSyncPending(a, MediaProvider.AUTHORITY)) {
            Log.d(TAG, "Sync finished, should refresh naw!!");
            mHandler.sendEmptyMessage(MSG_SET_NOT_REFRESHING);
        } else {
            Log.d(TAG, "Sync Active or Pending!!");
            mHandler.sendEmptyMessage(MSG_SET_REFRESHING);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mSyncHandle != null) {
            ContentResolver.removeStatusChangeListener(mSyncHandle);
        }
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        AdapterView.AdapterContextMenuInfo info;
        try {
            info = (AdapterView.AdapterContextMenuInfo) menuInfo;
        } catch (final ClassCastException e) {
            Log.e(TAG, "bad menuInfo", e);
            return;
        }

        // XXX the "- 1" below is due to having a header. I'm not sure where this is supposed to be handled.
        final Cursor c = (Cursor) mCastAdapter.getItem(info.position - 1);
        if (c == null) {
            return;
        }

        // load the base menus.
        final MenuInflater menuInflater = getMenuInflater();
        menuInflater.inflate(R.menu.cast_context, menu);
        menuInflater.inflate(R.menu.cast_options, menu);

        menu.setHeaderTitle(c.getString(c.getColumnIndex(Cast._TITLE)));

        final boolean canEdit = Cast.canEdit(this, c);
        menu.findItem(R.id.cast_edit).setVisible(canEdit);
        menu.findItem(R.id.cast_delete).setVisible(canEdit);

    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterView.AdapterContextMenuInfo info;
        try {
            info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        } catch (final ClassCastException e) {
            Log.e(TAG, "bad menuInfo", e);
            return false;
        }

        final Uri cast = Cast.getCanonicalUri(this, ContentUris.withAppendedId(mCastsUri, info.id));

        switch (item.getItemId()) {
        case R.id.cast_view:
            startActivity(new Intent(Intent.ACTION_VIEW, cast));
            return true;

        case R.id.cast_edit:
            startActivity(new Intent(Intent.ACTION_EDIT, cast));
            return true;

        case R.id.cast_delete:
            startActivity(new Intent(Intent.ACTION_DELETE, cast));
            return true;

        //       case R.id.cast_play:
        //          startActivity(new Intent(CastDetailsActivity.ACTION_PLAY_CAST, cast));
        //          return true;

        default:
            return super.onContextItemSelected(item);
        }
    }

    private void initCastList() {

        mCastAdapter = new CastCursorAdapter(ItineraryDetail.this, null);
        mCastView.setAdapter(new ImageLoaderAdapter(this, mCastAdapter, mImageCache,
                new int[] { R.id.media_thumbnail }, 48, 48, ImageLoaderAdapter.UNIT_DIP));

        if (USE_MAP) {

            mCastsOverlay = new CastsIconOverlay(ItineraryDetail.this);
            mPathOverlay = new PathOverlay(this);

            final List<Overlay> overlays = mMapView.getOverlays();
            overlays.add(mPathOverlay);
            overlays.add(mCastsOverlay);
        }
    }

    private void refresh(boolean explicitSync) {
        LocastSyncService.startSync(this, mUri, explicitSync);
        final Bundle extras = new Bundle();
        if (mItineraryCastCount == UNKNOWN_COUNT || (mItineraryCastCount != mCastAdapter.getCount())) {
            extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
        }
        LocastSyncService.startSync(this, mCastsUri, explicitSync, extras);
    }

    @Override
    public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {

        final Cursor cast = (Cursor) adapter.getItemAtPosition(position);
        final int dratCol = cast.getColumnIndex(Cast._DRAFT);
        final boolean isDraft = !cast.isNull(dratCol) && cast.getInt(dratCol) == 1;

        if (isDraft) {
            startActivity(new Intent(Intent.ACTION_EDIT, ContentUris.withAppendedId(mCastsUri, id)));
        } else {
            startActivity(new Intent(Intent.ACTION_VIEW, ContentUris.withAppendedId(mCastsUri, id)));
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.refresh:
            refresh(true);
            break;

        case R.id.home:
            startActivity(new Intent(this, BrowserHome.class));
            break;

        case R.id.add_cast:
            startActivity(new Intent(Intent.ACTION_INSERT, Itinerary.getCastsUri(getIntent().getData())));
            break;
        }
    }

    private static final int LOADER_ITINERARY = 0, LOADER_CASTS = 1;
    private static final String LOADER_ARG_DATA = "edu.mit.mobile.android.locast.LOADER_ARG_DATA";

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        final Uri uri = args.getParcelable(LOADER_ARG_DATA);

        CursorLoader cl = null;

        switch (id) {
        case LOADER_ITINERARY:
            cl = new CursorLoader(this, uri, ITINERARY_PROJECTION, null, null, null);
            break;

        case LOADER_CASTS:
            cl = new CursorLoader(this, uri, Cast.PROJECTION, null, null, Cast.SORT_ORDER_DEFAULT);
            break;

        }

        cl.setUpdateThrottle(Constants.UPDATE_THROTTLE);

        return cl;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
        switch (loader.getId()) {
        case LOADER_ITINERARY: {
            if (c.moveToFirst()) {
                mItineraryCastCount = c.getInt(c.getColumnIndex(Itinerary._CASTS_COUNT));
                final String description = c.getString(c.getColumnIndex(Itinerary._DESCRIPTION));
                ((TextView) findViewById(R.id.description)).setText(description);
                ((TextView) findViewById(R.id.description_empty)).setText(description);

                ((TextView) findViewById(R.id.title)).setText(c.getString(c.getColumnIndex(Itinerary._TITLE)));

                if (USE_MAP) {
                    final List<GeoPoint> path = Itinerary.getPath(c);
                    mPathOverlay.setPath(path);

                    mMapController.setCenter(mPathOverlay.getCenter());

                    if (Constants.USES_OSMDROID) {
                        // this needs to be run after the MapView has been first sized due to a bug in zoomToSpan()
                        mMapView.post(new Runnable() {

                            @Override
                            public void run() {
                                if (mMapView.getHeight() > 0) {
                                    mMapController.zoomToSpan(mPathOverlay.getLatSpanE6(),
                                            mPathOverlay.getLonSpanE6());
                                } else {
                                    mMapView.post(this);
                                }
                            }
                        });
                    } else {
                        mMapController.zoomToSpan(mPathOverlay.getLatSpanE6(), mPathOverlay.getLonSpanE6());
                    }

                    mMapView.setVisibility(View.VISIBLE);
                }
            } else {
                Log.e(TAG, "error loading itinerary");
            }

        }
            break;

        case LOADER_CASTS: {
            mCastAdapter.swapCursor(c);
            if (USE_MAP) {
                mCastsOverlay.swapCursor(c);
            }
            // this is done after the casts are loaded so that an expedited sync can be requested if the list is empty.
            if (mFirstLoadSync) {
                refresh(false);
                mFirstLoadSync = false;
            }
        }
            break;
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        switch (loader.getId()) {
        case LOADER_CASTS:
            mCastAdapter.swapCursor(null);
            if (USE_MAP) {
                mCastsOverlay.swapCursor(null);
            }
            break;

        case LOADER_ITINERARY:

            break;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.itinerary_detail, menu);
        if (Constants.CAN_CREATE_CASTS) {
            menu.findItem(R.id.add_cast).setVisible(true);
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.add_cast:
            startActivity(new Intent(Intent.ACTION_INSERT, Itinerary.getCastsUri(getIntent().getData())));
            return true;

        case R.id.refresh:
            refresh(true);
            return true;

        default:
            return super.onOptionsItemSelected(item);
        }
    }
    /*
       @Override
       public boolean onItemLongPress(int position, OverlayItem item) {
          return false;
       }
        
       @Override
       public boolean onItemSingleTapUp(int position, OverlayItem item) {
          if (!markerClicked) {
     clickedItems.clear();
     markerClicked = true;
     clickTimer.schedule(new ClickTimer(), 100);
          }
        
          clickedItems.add(item);
          return false;
       }*/

    @Override
    public void onClick(DialogInterface arg0, int arg1) {
        // TODO Auto-generated method stub

    }

    @Override
    protected boolean isRouteDisplayed() {
        // TODO Auto-generated method stub
        return false;
    }

    /*   protected void showCastChooserDialog() {
          final String[] items = new String[clickedItems.size()];
          for (int i = 0; i < items.length; i++)
          {
     items[i] = clickedItems.get(i).mTitle;
          }
        
          final AlertDialog.Builder builder = new AlertDialog.Builder(this);
          builder.setTitle("Casts");
          builder.setItems(items, this);
          final Dialog dialog = builder.create();
          dialog.show();
       }
        
       @Override
       public void onClick(DialogInterface dialog, int which) {
          selectOverlay(clickedItems.get(which));
       }
        
       private void selectOverlay(OverlayItem item) {
          try {
     final long selectedId = Long.parseLong(item);
     startActivity(new Intent(Intent.ACTION_VIEW, ContentUris.withAppendedId(mCastsUri, selectedId)));
          } catch (final Exception e) {
     Log.e(TAG, "Error trying to parse id", e);
          }
       }
        
       private class ClickTimer extends TimerTask {
          @Override
          public void run() {
     if (clickedItems.size() > 1) {
         runOnUiThread(new Runnable() {
           @Override
           public void run() {
              showCastChooserDialog();
           }
        });
      } else {
        selectOverlay(clickedItems.get(0));
     }
        
     markerClicked = false;
          }
       }*/
}