nl.sogeti.android.gpstracker.viewer.TrackList.java Source code

Java tutorial

Introduction

Here is the source code for nl.sogeti.android.gpstracker.viewer.TrackList.java

Source

/*------------------------------------------------------------------------------
 **     Ident: Sogeti Smart Mobile Solutions
 **    Author: rene
 ** Copyright: (c) Apr 24, 2011 Sogeti Nederland B.V. All Rights Reserved.
 **------------------------------------------------------------------------------
 ** Sogeti Nederland B.V.            |  No part of this file may be reproduced  
 ** Distributed Software Engineering |  or transmitted in any form or by any        
 ** Lange Dreef 17                   |  means, electronic or mechanical, for the      
 ** 4131 NJ Vianen                   |  purpose, without the express written    
 ** The Netherlands                  |  permission of the copyright holder.
 *------------------------------------------------------------------------------
 *
 *   This file is part of OpenGPSTracker.
 *
 *   OpenGPSTracker 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 3 of the License, or
 *   (at your option) any later version.
 *
 *   OpenGPSTracker 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 OpenGPSTracker.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package nl.sogeti.android.gpstracker.viewer;

import android.app.Dialog;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.NavUtils;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AlertDialog.Builder;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;

import nl.sogeti.android.gpstracker.R;
import nl.sogeti.android.gpstracker.actions.Statistics;
import nl.sogeti.android.gpstracker.actions.tasks.GpxParser;
import nl.sogeti.android.gpstracker.actions.utils.ProgressListener;
import nl.sogeti.android.gpstracker.adapter.SectionedListAdapter;
import nl.sogeti.android.gpstracker.service.db.DatabaseHelper;
import nl.sogeti.android.gpstracker.service.db.GPStracking;
import nl.sogeti.android.gpstracker.service.db.GPStracking.Tracks;
import nl.sogeti.android.log.Log;

/**
 * Show a list view of all tracks, also doubles for showing search results
 *
 * @author rene (c) Jan 11, 2009, Sogeti B.V.
 * @version $Id$
 */
public class TrackList extends AppCompatActivity implements ProgressListener {
    protected static final int DIALOG_ERROR = Menu.FIRST + 28;
    private static final int MENU_DELETE = Menu.FIRST + 0;
    private static final int MENU_SHARE = Menu.FIRST + 1;
    private static final int MENU_RENAME = Menu.FIRST + 2;
    private static final int MENU_STATS = Menu.FIRST + 3;
    private static final int MENU_SEARCH = Menu.FIRST + 4;
    private static final int MENU_VACUUM = Menu.FIRST + 5;
    private static final int MENU_PICKER = Menu.FIRST + 6;
    private static final int DIALOG_RENAME = Menu.FIRST + 23;
    private static final int DIALOG_DELETE = Menu.FIRST + 24;
    private static final int DIALOG_VACUUM = Menu.FIRST + 25;
    private static final int DIALOG_IMPORT = Menu.FIRST + 26;
    private static final int FILE_PICKER = Menu.FIRST + 29;
    protected ListView mList;
    protected ListAdapter mAdapter;
    private EditText mTrackNameView;
    private Uri mDialogTrackUri;
    private String mDialogCurrentName = "";
    private String mErrorDialogMessage;
    private Exception mErrorDialogException;
    private Runnable mImportAction;
    private String mImportTrackName;
    private String mErrorTask;
    private int mPausePosition;
    private OnClickListener mDeleteOnClickListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            getContentResolver().delete(mDialogTrackUri, null, null);
        }
    };
    private OnClickListener mRenameOnClickListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            //         Log.d( TAG, "Context item selected: "+mDialogUri+" with name "+mDialogCurrentName );

            String trackName = mTrackNameView.getText().toString();
            ContentValues values = new ContentValues();
            values.put(Tracks.NAME, trackName);
            TrackList.this.getContentResolver().update(mDialogTrackUri, values, null, null);
        }
    };
    private OnClickListener mVacuumOnClickListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            DatabaseHelper helper = new DatabaseHelper(TrackList.this);
            helper.vacuum();
        }
    };
    private OnClickListener mImportOnClickListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            mImportAction.run();
        }
    };
    private boolean mFinishedStart = false;
    private Handler mHandler = new Handler();
    private Runnable mRequestFocus = new Runnable() {
        public void run() {
            mList.focusableViewAvailable(mList);
        }
    };
    private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
            onListItemClick((ListView) parent, v, position, id);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.tracklist);
        Toolbar toolbar = (Toolbar) findViewById(R.id.support_actionbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        displayIntent(getIntent());

        ListView listView = getListView();
        listView.setItemsCanFocus(true);
        // Add the context menu (the long press thing)
        registerForContextMenu(listView);

        if (savedInstanceState != null) {
            setSelection(savedInstanceState.getInt("POSITION"));
        }
    }

    @Override
    protected void onResume() {
        if (mPausePosition != 0) {
            getListView().setSelection(mPausePosition);
        }
        super.onResume();
    }

    @Override
    public void onNewIntent(Intent newIntent) {
        displayIntent(newIntent);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable("URI", mDialogTrackUri);
        outState.putString("NAME", mDialogCurrentName);
        outState.putInt("POSITION", getListView().getFirstVisiblePosition());
    }

    @Override
    protected void onPause() {
        mPausePosition = getListView().getFirstVisiblePosition();
        super.onPause();
    }

    /*******************************************************************/

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        boolean result = super.onCreateOptionsMenu(menu);
        MenuItem item;
        item = menu.add(ContextMenu.NONE, MENU_SEARCH, ContextMenu.NONE, android.R.string.search_go)
                .setIcon(R.drawable.ic_search_24dp).setAlphabeticShortcut(SearchManager.MENU_KEY);
        MenuItemCompat.setShowAsAction(item, MenuItem.SHOW_AS_ACTION_ALWAYS);
        item = menu.add(ContextMenu.NONE, MENU_PICKER, ContextMenu.NONE, R.string.menu_picker)
                .setIcon(R.drawable.ic_import_24dp);
        MenuItemCompat.setShowAsAction(item, MenuItem.SHOW_AS_ACTION_ALWAYS);
        item = menu.add(ContextMenu.NONE, MENU_VACUUM, ContextMenu.NONE, R.string.menu_vacuum)
                .setIcon(android.R.drawable.ic_menu_crop);
        MenuItemCompat.setShowAsAction(item, MenuItem.SHOW_AS_ACTION_NEVER);
        return result;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        boolean handled = false;
        switch (item.getItemId()) {
        case android.R.id.home:
            NavUtils.navigateUpFromSameTask(this);
            handled = true;
            break;
        case MENU_SEARCH:
            onSearchRequested();
            handled = true;
            break;
        case MENU_VACUUM:
            showDialog(DIALOG_VACUUM);
            break;
        case MENU_PICKER:
            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
            intent.setType("*/*");
            startActivityForResult(intent, FILE_PICKER);
            break;
        default:
            handled = super.onOptionsItemSelected(item);
        }
        return handled;
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        if (menuInfo instanceof AdapterView.AdapterContextMenuInfo) {
            AdapterView.AdapterContextMenuInfo itemInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
            TextView textView = (TextView) itemInfo.targetView.findViewById(R.id.listitem_name);
            if (textView != null) {
                menu.setHeaderTitle(textView.getText());
            }

            Object listItem = getListAdapter().getItem(itemInfo.position);
            if (listItem instanceof Cursor) {
                menu.add(0, MENU_STATS, 0, R.string.menu_statistics);
                menu.add(0, MENU_SHARE, 0, R.string.menu_shareTrack);
                menu.add(0, MENU_RENAME, 0, R.string.menu_renameTrack);
                menu.add(0, MENU_DELETE, 0, R.string.menu_deleteTrack);
            }
        }
    }

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

        Object listItem = getListAdapter().getItem(info.position);
        if (listItem instanceof Cursor) {
            Cursor cursor = (Cursor) listItem;
            mDialogTrackUri = ContentUris.withAppendedId(Tracks.CONTENT_URI, cursor.getLong(0));
            mDialogCurrentName = cursor.getString(1);
            mDialogCurrentName = mDialogCurrentName != null ? mDialogCurrentName : "";
            switch (item.getItemId()) {
            case MENU_DELETE: {
                showDialog(DIALOG_DELETE);
                handled = true;
                break;
            }
            case MENU_SHARE: {
                Intent actionIntent = new Intent(Intent.ACTION_RUN);
                actionIntent.setDataAndType(mDialogTrackUri, Tracks.CONTENT_ITEM_TYPE);
                actionIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                startActivity(Intent.createChooser(actionIntent, getString(R.string.share_track)));
                handled = true;
                break;
            }
            case MENU_RENAME: {
                showDialog(DIALOG_RENAME);
                handled = true;
                break;
            }
            case MENU_STATS: {
                Intent actionIntent = new Intent(this, Statistics.class);
                actionIntent.setData(mDialogTrackUri);
                startActivity(actionIntent);
                handled = true;
                break;
            }
            default:
                handled = super.onContextItemSelected(item);
                break;
            }
        }
        return handled;
    }

    /*
     * (non-Javadoc)
     * @see android.app.Activity#onCreateDialog(int)
     */
    @Override
    protected Dialog onCreateDialog(int id) {
        Dialog dialog = null;
        Builder builder = null;
        switch (id) {
        case DIALOG_RENAME:
            LayoutInflater factory = LayoutInflater.from(this);
            View view = factory.inflate(R.layout.namedialog, (ViewGroup) findViewById(android.R.id.content), false);
            mTrackNameView = (EditText) view.findViewById(R.id.nameField);
            builder = new AlertDialog.Builder(this).setTitle(R.string.dialog_routename_title)
                    .setMessage(R.string.dialog_routename_message).setIcon(android.R.drawable.ic_dialog_alert)
                    .setPositiveButton(R.string.btn_okay, mRenameOnClickListener)
                    .setNegativeButton(R.string.btn_cancel, null).setView(view);
            dialog = builder.create();
            return dialog;
        case DIALOG_DELETE:
            builder = new AlertDialog.Builder(TrackList.this).setTitle(R.string.dialog_delete_title)
                    .setIcon(android.R.drawable.ic_dialog_alert).setNegativeButton(android.R.string.cancel, null)
                    .setPositiveButton(android.R.string.ok, mDeleteOnClickListener);
            dialog = builder.create();
            String messageFormat = this.getResources().getString(R.string.dialog_delete_message);
            String message = String.format(messageFormat, "");
            ((AlertDialog) dialog).setMessage(message);
            return dialog;
        case DIALOG_VACUUM:
            builder = new AlertDialog.Builder(TrackList.this).setTitle(R.string.dialog_vacuum_title)
                    .setMessage(R.string.dialog_vacuum_message).setIcon(android.R.drawable.ic_dialog_alert)
                    .setNegativeButton(android.R.string.cancel, null)
                    .setPositiveButton(android.R.string.ok, mVacuumOnClickListener);
            dialog = builder.create();
            return dialog;
        case DIALOG_IMPORT:
            builder = new AlertDialog.Builder(TrackList.this).setTitle(R.string.dialog_import_title)
                    .setMessage(getString(R.string.dialog_import_message, mImportTrackName))
                    .setIcon(android.R.drawable.ic_dialog_alert).setNegativeButton(android.R.string.cancel, null)
                    .setPositiveButton(android.R.string.ok, mImportOnClickListener);
            dialog = builder.create();
            return dialog;
        case DIALOG_ERROR:
            builder = new AlertDialog.Builder(this);
            builder.setIcon(android.R.drawable.ic_dialog_alert).setTitle(android.R.string.dialog_alert_title)
                    .setMessage(mErrorDialogMessage).setNeutralButton(android.R.string.cancel, null);
            dialog = builder.create();
            return dialog;
        default:
            return super.onCreateDialog(id);
        }
    }

    /*
     * (non-Javadoc)
     * @see android.app.Activity#onPrepareDialog(int, android.app.Dialog)
     */
    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        super.onPrepareDialog(id, dialog);
        AlertDialog alert;
        String message;
        switch (id) {
        case DIALOG_RENAME:
            mTrackNameView.setText(mDialogCurrentName);
            mTrackNameView.setSelection(0, mDialogCurrentName.length());
            break;
        case DIALOG_DELETE:
            alert = (AlertDialog) dialog;
            String messageFormat = this.getResources().getString(R.string.dialog_delete_message);
            message = String.format(messageFormat, mDialogCurrentName);
            alert.setMessage(message);
            break;
        case DIALOG_ERROR:
            alert = (AlertDialog) dialog;
            message = "Failed task:\n" + mErrorTask;
            message += "\n\n";
            message += "Reason:\n" + mErrorDialogMessage;
            if (mErrorDialogException != null) {
                message += " (" + mErrorDialogException.getMessage() + ") ";
            }
            alert.setMessage(message);
            break;
        case DIALOG_IMPORT:
            alert = (AlertDialog) dialog;
            alert.setMessage(getString(R.string.dialog_import_message, mImportTrackName));
            break;
        }
    }

    /**
     * ProgressListener interface and UI actions (non-Javadoc)
     **/

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != RESULT_CANCELED) {
            switch (requestCode) {
            case FILE_PICKER:
                new GpxParser(TrackList.this, TrackList.this).execute(data.getData());
                break;
            default:
                super.onActivityResult(requestCode, resultCode, data);
                break;
            }
        }
    }

    /*******************************************************************/

    @Override
    public void setIndeterminate(boolean indeterminate) {
        setProgressBarIndeterminate(indeterminate);
    }

    private void displayIntent(Intent intent) {
        final String queryAction = intent.getAction();
        final String orderby = Tracks.CREATION_TIME + " DESC";
        Cursor tracksCursor = null;
        if (Intent.ACTION_SEARCH.equals(queryAction)) {
            // Got to SEARCH a query for tracks, make a list
            tracksCursor = doSearchWithIntent(intent);
        } else if (Intent.ACTION_VIEW.equals(queryAction)) {
            final Uri uri = intent.getData();
            if ("content".equals(uri.getScheme()) && GPStracking.AUTHORITY.equals(uri.getAuthority())) {
                // Got to VIEW a single track, instead hand it of to the LoggerMap
                Intent notificationIntent = new Intent(this, LoggerMap.class);
                notificationIntent.setData(uri);
                startActivity(notificationIntent);
                finish();
            } else if (uri.getScheme().equals("file") || uri.getScheme().equals("content")) {

                mImportTrackName = uri.getLastPathSegment();
                // Got to VIEW a GPX filename
                mImportAction = new Runnable() {
                    @Override
                    public void run() {
                        new GpxParser(TrackList.this, TrackList.this).execute(uri);
                    }
                };
                showDialog(DIALOG_IMPORT);
                tracksCursor = managedQuery(Tracks.CONTENT_URI,
                        new String[] { Tracks._ID, Tracks.NAME, Tracks.CREATION_TIME }, null, null, orderby);
            } else {
                Log.e(this, "Unable to VIEW " + uri);
            }
        } else {
            // Got to nothing, make a list of everything
            tracksCursor = managedQuery(Tracks.CONTENT_URI,
                    new String[] { Tracks._ID, Tracks.NAME, Tracks.CREATION_TIME }, null, null, orderby);
        }
        displayCursor(tracksCursor);

    }

    @Override
    public void started() {
        setProgressBarVisibility(true);
        setProgress(Window.PROGRESS_START);
    }

    // Copy of the ListActivity addition to Activity

    private Cursor doSearchWithIntent(final Intent queryIntent) {
        final String queryString = queryIntent.getStringExtra(SearchManager.QUERY);
        Cursor cursor = managedQuery(Tracks.CONTENT_URI,
                new String[] { Tracks._ID, Tracks.NAME, Tracks.CREATION_TIME }, "name LIKE ?",
                new String[] { "%" + queryString + "%" }, null);
        return cursor;
    }

    @Override
    public void finished(Uri result) {
        setProgressBarVisibility(false);
        setProgressBarIndeterminate(false);
    }

    private void displayCursor(Cursor tracksCursor) {
        SectionedListAdapter sectionedAdapter = new SectionedListAdapter(this);

        String[] fromColumns = new String[] { Tracks.NAME, Tracks.CREATION_TIME, Tracks._ID };
        int[] toItems = new int[] { R.id.listitem_name, R.id.listitem_from, R.id.bcSyncedCheckBox };
        SimpleCursorAdapter trackAdapter = new SimpleCursorAdapter(this, R.layout.trackitem, tracksCursor,
                fromColumns, toItems);
        sectionedAdapter.addSection("", trackAdapter);

        // Enrich the track adapter with Breadcrumbs adapter data
        trackAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
            @Override
            public boolean setViewValue(View view, final Cursor cursor, int columnIndex) {
                if (columnIndex == 0) {
                    final long trackId = cursor.getLong(0);
                    final String trackName = cursor.getString(1);
                    // Show the check if Breadcrumbs is online
                    final CheckBox checkbox = (CheckBox) view;
                    final ProgressBar progressbar = (ProgressBar) ((View) view.getParent())
                            .findViewById(R.id.bcExportProgress);
                    checkbox.setVisibility(View.INVISIBLE);
                    checkbox.setOnCheckedChangeListener(null);

                    return true;
                }
                return false;
            }
        });

        setListAdapter(sectionedAdapter);
    }

    @Override
    public void showError(String task, String errorDialogMessage, Exception errorDialogException) {
        mErrorTask = task;
        mErrorDialogMessage = errorDialogMessage;
        mErrorDialogException = errorDialogException;
        Log.e(this, errorDialogMessage, errorDialogException);
        if (!isFinishing()) {
            showDialog(DIALOG_ERROR);
        }
        setProgressBarVisibility(false);
        setProgressBarIndeterminate(false);
    }

    protected void onListItemClick(ListView listView, View view, int position, long id) {
        Intent intent = new Intent();
        Uri trackUri = ContentUris.withAppendedId(Tracks.CONTENT_URI, id);
        intent.setData(trackUri);
        ComponentName caller = this.getCallingActivity();
        if (caller != null) {
            setResult(RESULT_OK, intent);
            finish();
        } else {
            intent.setClass(this, LoggerMap.class);
            startActivity(intent);
        }

    }

    @Override
    protected void onRestoreInstanceState(Bundle state) {
        super.onRestoreInstanceState(state);
        ensureList();
        mDialogTrackUri = state.getParcelable("URI");
        mDialogCurrentName = state.getString("NAME");
        mDialogCurrentName = mDialogCurrentName != null ? mDialogCurrentName : "";
        getListView().setSelection(state.getInt("POSITION"));
    }

    @Override
    protected void onDestroy() {
        mHandler.removeCallbacks(mRequestFocus);
        super.onDestroy();
    }

    @Override
    public void onContentChanged() {
        super.onContentChanged();
        View emptyView = findViewById(android.R.id.empty);
        mList = (ListView) findViewById(android.R.id.list);
        if (mList == null) {
            throw new RuntimeException(
                    "Your content must have a ListView whose id attribute is " + "'android.R.id.list'");
        }
        if (emptyView != null) {
            mList.setEmptyView(emptyView);
        }
        mList.setOnItemClickListener(mOnClickListener);
        if (mFinishedStart) {
            setListAdapter(mAdapter);
        }
        mHandler.post(mRequestFocus);
        mFinishedStart = true;
    }

    public ListAdapter getListAdapter() {
        return mAdapter;
    }

    public void setListAdapter(ListAdapter adapter) {
        synchronized (this) {
            ensureList();
            mAdapter = adapter;
            mList.setAdapter(adapter);
        }
    }

    private void ensureList() {
        if (mList != null) {
            return;
        }
        setContentView(new ListView(this));

    }

    public ListView getListView() {
        ensureList();
        return mList;
    }

    public void setSelection(int position) {
        mList.setSelection(position);
    }
}