/*
* Copyright (C) 2010 - 2013 Niall 'Rivernile' Scott
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors or contributors be held liable for
* any damages arising from the use of this software.
*
* The aforementioned copyright holder(s) hereby grant you a
* non-transferrable right to use this software for any purpose (including
* commercial applications), and to modify it and redistribute it, subject to
* the following conditions:
*
* 1. This notice may not be removed or altered from any file it appears in.
*
* 2. Any modifications made to this software, except those defined in
* clause 3 of this agreement, must be released under this license, and
* the source code of any modifications must be made available on a
* publically accessible (and locateable) website, or sent to the
* original author of this software.
*
* 3. Software modifications that do not alter the functionality of the
* software but are simply adaptations to a specific environment are
* exempt from clause 2.
*/
package uk.org.rivernile.edinburghbustracker.android.fragments.general;
import static uk.org.rivernile.edinburghbustracker.android.twitter
.TwitterUpdatesLoader.ERROR_NODATA;
import static uk.org.rivernile.edinburghbustracker.android.twitter
.TwitterUpdatesLoader.ERROR_PARSEERR;
import static uk.org.rivernile.edinburghbustracker.android.twitter
.TwitterUpdatesLoader.ERROR_IOERR;
import static uk.org.rivernile.edinburghbustracker.android.twitter
.TwitterUpdatesLoader.ERROR_URLERR;
import static uk.org.rivernile.edinburghbustracker.android.twitter
.TwitterUpdatesLoader.ERROR_URLMISMATCH;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import uk.org.rivernile.edinburghbustracker.android.R;
import uk.org.rivernile.edinburghbustracker.android.twitter.TwitterLoaderResult;
import uk.org.rivernile.edinburghbustracker.android.twitter.TwitterNewsItem;
import uk.org.rivernile.edinburghbustracker.android.twitter
.TwitterUpdatesLoader;
/**
* This Fragments displays a ListView of Tweets which informs users of things
* that may affect their journey. No action can be taken on the ListView items
* but URLs can be tapped.
*
* @author Niall Scott
*/
public class TwitterUpdatesFragment extends ListFragment
implements LoaderManager.LoaderCallbacks<TwitterLoaderResult> {
private View progress;
private TextView txtError;
/**
* {@inheritDoc}
*/
@Override
public View onCreateView(final LayoutInflater inflater,
final ViewGroup container, final Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.newsupdates, container, false);
progress = v.findViewById(R.id.progress);
txtError = (TextView)v.findViewById(R.id.txtError);
return v;
}
/**
* {@inheritDoc}
*/
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Tell the underlying Activity that it should create an options menu
// for this Fragment.
setHasOptionsMenu(true);
// Initialise the Loader for loading tweets.
getLoaderManager().initLoader(0, null, this);
}
/**
* {@inheritDoc}
*/
@Override
public void onCreateOptionsMenu(final Menu menu,
final MenuInflater inflater) {
// Inflate the options menu.
inflater.inflate(R.menu.newsupdates_option_menu, menu);
}
/**
* {@inheritDoc}
*/
@Override
public void onPrepareOptionsMenu(final Menu menu) {
super.onPrepareOptionsMenu(menu);
final MenuItem refreshItem = menu.findItem(
R.id.newsupdates_option_menu_refresh);
if(progress.getVisibility() == View.VISIBLE) {
// Disable the refresh item if a refresh is in progress.
refreshItem.setEnabled(false);
} else {
// Enable the refresh item otherwise.
refreshItem.setEnabled(true);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch(item.getItemId()) {
case R.id.newsupdates_option_menu_refresh:
// Fetch the data.
getLoaderManager().restartLoader(0, null, this);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* {@inheritDoc}
*/
@Override
public Loader<TwitterLoaderResult> onCreateLoader(final int id,
final Bundle args) {
showProgress();
return new TwitterUpdatesLoader(getActivity());
}
/**
* {@inheritDoc}
*/
@Override
public void onLoadFinished(final Loader<TwitterLoaderResult> loader,
final TwitterLoaderResult result) {
if (isAdded()) {
if(result.hasError()) {
handleError(result.getError());
} else {
populateList(result.getResult());
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void onLoaderReset(final Loader<TwitterLoaderResult> loader) {
// Nothing to do here.
}
/**
* Show the user progress on loading.
*/
private void showProgress() {
// Empty the ListView.
setListAdapter(null);
// Show the progress.
txtError.setVisibility(View.GONE);
progress.setVisibility(View.VISIBLE);
// Invalidate so that the refresh item gets disabled.
getActivity().supportInvalidateOptionsMenu();
}
/**
* Handle error codes.
*
* @param errorCode The error code.
*/
private void handleError(final int errorCode) {
// Set the TextView depending on what the error code is.
switch(errorCode) {
case ERROR_NODATA:
txtError.setText(R.string.newsupdates_err_nodata);
break;
case ERROR_PARSEERR:
txtError.setText(R.string.newsupdates_err_parseerr);
break;
case ERROR_IOERR:
txtError.setText(R.string.newsupdates_err_ioerr);
break;
case ERROR_URLERR:
txtError.setText(R.string.newsupdates_err_urlerr);
break;
case ERROR_URLMISMATCH:
txtError.setText(R.string.newsupdates_err_urlmismatch);
break;
}
// Show the error layout.
progress.setVisibility(View.GONE);
txtError.setVisibility(View.VISIBLE);
// Invalidate the options menu so that the refresh item is shown again.
getActivity().supportInvalidateOptionsMenu();
}
/**
* Populate the ListView with the Twitter news items.
*
* @param items An ArrayList of {@link TwitterNewsItem}s.
*/
private void populateList(final ArrayList<TwitterNewsItem> items) {
// If there's 0 items, display an error.
if(items == null || items.isEmpty()) {
handleError(ERROR_NODATA);
return;
}
// Ensure that the progress and error layouts are removed.
txtError.setVisibility(View.GONE);
progress.setVisibility(View.GONE);
// Fun and magic happens here.
final ArrayList<HashMap<String, String>> list =
new ArrayList<HashMap<String, String>>();
HashMap<String, String> map;
for(TwitterNewsItem item : items) {
map = new HashMap<String, String>();
map.put("TEXT", item.getBody());
map.put("INFO", item.getPoster() + " - " + item.getDate());
list.add(map);
}
// Create the ListAdapter.
final NewsItemsAdapter adapter = new NewsItemsAdapter(getActivity(),
list, R.layout.newsupdateslist, new String[] { "TEXT", "INFO" },
new int[] { R.id.twitText, R.id.twitInfo });
setListAdapter(adapter);
// Invalidate the options menu to make sure that the refresh item is
// shown.
getActivity().supportInvalidateOptionsMenu();
}
/**
* This is the ListAdapter which is used to display the Twitter items.
*/
private static class NewsItemsAdapter extends SimpleAdapter {
/**
* {@inheritDoc}
*/
public NewsItemsAdapter(final Context context,
final List<? extends Map<String, ?>> data, final int resource,
final String[] from, final int[] to) {
super(context, data, resource, from, to);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isEnabled(final int index) {
return false;
}
}
}