Java tutorial
/* * Copyright (C) 2012-2014 Jamie Nicol <jamie@thenicols.net> * * 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 3 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, see <http://www.gnu.org/licenses/>. */ package org.jamienicol.episodes; import android.app.Activity; import android.content.AsyncQueryHandler; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; 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.BaseAdapter; import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.ToggleButton; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import java.util.ArrayList; import java.util.List; import org.jamienicol.episodes.db.EpisodesTable; import org.jamienicol.episodes.db.ShowsProvider; import org.jamienicol.episodes.db.ShowsTable; import org.jamienicol.episodes.services.RefreshShowService; public class ShowsListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> { private static final int LOADER_ID_SHOWS = 0; private static final int LOADER_ID_EPISODES = 1; private static final String KEY_PREF_SHOWS_FILTER = "pref_shows_filter"; private static final int SHOWS_FILTER_ALL = 0; private static final int SHOWS_FILTER_STARRED = 1; private static final int SHOWS_FILTER_UNCOMPLETED = 2; private static final int SHOWS_FILTER_ARCHIVED = 3; private ShowsListAdapter listAdapter; private Cursor showsData; private Cursor episodesData; public interface OnShowSelectedListener { public void onShowSelected(int showId); } private OnShowSelectedListener onShowSelectedListener; @Override public void onAttach(Activity activity) { super.onAttach(activity); try { onShowSelectedListener = (OnShowSelectedListener) activity; } catch (ClassCastException e) { final String message = String.format("%s must implement OnShowSelectedListener", activity.toString()); throw new ClassCastException(message); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.shows_list_fragment, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); listAdapter = new ShowsListAdapter(getActivity(), null, null); setListAdapter(listAdapter); getLoaderManager().initLoader(LOADER_ID_SHOWS, null, this); getLoaderManager().initLoader(LOADER_ID_EPISODES, null, this); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.shows_list_fragment, menu); } @Override public void onPrepareOptionsMenu(Menu menu) { // hide refresh all option if no shows exist final boolean showsExist = (showsData != null && showsData.moveToFirst()); menu.findItem(R.id.menu_refresh_all_shows).setVisible(showsExist); /* set the currently selected filter's menu item as checked */ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); final int filter = prefs.getInt(KEY_PREF_SHOWS_FILTER, SHOWS_FILTER_ALL); switch (filter) { case SHOWS_FILTER_ALL: menu.findItem(R.id.menu_filter_all).setChecked(true); break; case SHOWS_FILTER_STARRED: menu.findItem(R.id.menu_filter_starred).setChecked(true); break; case SHOWS_FILTER_UNCOMPLETED: menu.findItem(R.id.menu_filter_uncompleted).setChecked(true); break; case SHOWS_FILTER_ARCHIVED: menu.findItem(R.id.menu_filter_archived).setChecked(true); break; } super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_refresh_all_shows: refreshAllShows(); return true; case R.id.menu_filter_all: case R.id.menu_filter_starred: case R.id.menu_filter_uncompleted: case R.id.menu_filter_archived: if (!item.isChecked()) { item.setChecked(true); } final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); final SharedPreferences.Editor editor = prefs.edit(); if (item.getItemId() == R.id.menu_filter_all) { editor.putInt(KEY_PREF_SHOWS_FILTER, SHOWS_FILTER_ALL); } else if (item.getItemId() == R.id.menu_filter_starred) { editor.putInt(KEY_PREF_SHOWS_FILTER, SHOWS_FILTER_STARRED); } else if (item.getItemId() == R.id.menu_filter_uncompleted) { editor.putInt(KEY_PREF_SHOWS_FILTER, SHOWS_FILTER_UNCOMPLETED); } else if (item.getItemId() == R.id.menu_filter_archived) { editor.putInt(KEY_PREF_SHOWS_FILTER, SHOWS_FILTER_ARCHIVED); } editor.apply(); return true; default: return super.onOptionsItemSelected(item); } } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { if (id == LOADER_ID_SHOWS) { final String[] projection = { ShowsTable.COLUMN_ID, ShowsTable.COLUMN_NAME, ShowsTable.COLUMN_STARRED, ShowsTable.COLUMN_ARCHIVED, ShowsTable.COLUMN_BANNER_PATH }; return new CursorLoader(getActivity(), ShowsProvider.CONTENT_URI_SHOWS, projection, null, null, ShowsTable.COLUMN_STARRED + " DESC," + ShowsTable.COLUMN_NAME + " ASC"); } else if (id == LOADER_ID_EPISODES) { final String[] projection = { EpisodesTable.COLUMN_SHOW_ID, EpisodesTable.COLUMN_SEASON_NUMBER, EpisodesTable.COLUMN_FIRST_AIRED, EpisodesTable.COLUMN_WATCHED }; final String selection = String.format("%s!=?", EpisodesTable.COLUMN_SEASON_NUMBER); final String[] selectionArgs = { "0" }; return new CursorLoader(getActivity(), ShowsProvider.CONTENT_URI_EPISODES, projection, selection, selectionArgs, null); } else { throw new IllegalArgumentException("invalid loader id"); } } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { switch (loader.getId()) { case LOADER_ID_SHOWS: showsData = data; listAdapter.swapShowsCursor(data); break; case LOADER_ID_EPISODES: episodesData = data; listAdapter.swapEpisodesCursor(data); break; } getActivity().supportInvalidateOptionsMenu(); } @Override public void onLoaderReset(Loader<Cursor> loader) { onLoadFinished(loader, null); } @Override public void onListItemClick(ListView l, View v, int position, long id) { onShowSelectedListener.onShowSelected((int) id); } private void refreshAllShows() { if (showsData != null && showsData.moveToFirst()) { do { final int idColumnIndex = showsData.getColumnIndexOrThrow(ShowsTable.COLUMN_ID); final int id = showsData.getInt(idColumnIndex); final Intent intent = new Intent(getActivity(), RefreshShowService.class); intent.putExtra("showId", id); getActivity().startService(intent); } while (showsData.moveToNext()); } } private static class ShowsListAdapter extends BaseAdapter implements SharedPreferences.OnSharedPreferenceChangeListener { private Context context; private Cursor showsCursor; private int filter; private EpisodesCounter episodesCounter; // list of shows to be displayed with current filter. maps from // the show's position in the list to its position in the cursor. private List<Integer> filteredShows; public ShowsListAdapter(Context context, Cursor showsCursor, Cursor episodesCursor) { this.context = context; episodesCounter = new EpisodesCounter(EpisodesTable.COLUMN_SHOW_ID); episodesCounter.swapCursor(episodesCursor); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); prefs.registerOnSharedPreferenceChangeListener(this); filter = prefs.getInt(KEY_PREF_SHOWS_FILTER, SHOWS_FILTER_ALL); filteredShows = new ArrayList<Integer>(); swapShowsCursor(showsCursor); } public void swapShowsCursor(Cursor showsCursor) { this.showsCursor = showsCursor; updateFilter(); notifyDataSetChanged(); } public void swapEpisodesCursor(Cursor episodesCursor) { episodesCounter.swapCursor(episodesCursor); if (showsCursor != null) { updateFilter(); notifyDataSetChanged(); } } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals(KEY_PREF_SHOWS_FILTER)) { filter = sharedPreferences.getInt(KEY_PREF_SHOWS_FILTER, SHOWS_FILTER_ALL); if (showsCursor != null) { updateFilter(); notifyDataSetChanged(); } } } private void updateFilter() { filteredShows.clear(); if (showsCursor == null || !showsCursor.moveToFirst()) { return; } do { switch (filter) { case SHOWS_FILTER_STARRED: final int starredColumnIndex = showsCursor.getColumnIndexOrThrow(ShowsTable.COLUMN_STARRED); if (showsCursor.getInt(starredColumnIndex) > 0) { filteredShows.add(showsCursor.getPosition()); } break; case SHOWS_FILTER_ARCHIVED: final int archivedColumnIndex = showsCursor.getColumnIndexOrThrow(ShowsTable.COLUMN_ARCHIVED); if (showsCursor.getInt(archivedColumnIndex) > 0) { filteredShows.add(showsCursor.getPosition()); } break; case SHOWS_FILTER_UNCOMPLETED: final int idColumnIndex = showsCursor.getColumnIndexOrThrow(ShowsTable.COLUMN_ID); final int id = showsCursor.getInt(idColumnIndex); if ((episodesCounter.getNumWatchedEpisodes(id) < episodesCounter.getNumAiredEpisodes(id)) && showsCursor .getInt(showsCursor.getColumnIndexOrThrow(ShowsTable.COLUMN_ARCHIVED)) == 0) { filteredShows.add(showsCursor.getPosition()); } break; default: final int columnIndex = showsCursor.getColumnIndexOrThrow(ShowsTable.COLUMN_ARCHIVED); if (showsCursor.getInt(columnIndex) == 0) { filteredShows.add(showsCursor.getPosition()); } break; } } while (showsCursor.moveToNext()); } @Override public int getCount() { if (showsCursor == null) { return 0; } else { return filteredShows.size(); } } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { showsCursor.moveToPosition(filteredShows.get(position)); final int idColumnIndex = showsCursor.getColumnIndexOrThrow(ShowsTable.COLUMN_ID); return showsCursor.getInt(idColumnIndex); } @Override public View getView(int position, View convertView, ViewGroup parent) { final LayoutInflater inflater = LayoutInflater.from(context); if (convertView == null) { convertView = inflater.inflate(R.layout.shows_list_item, parent, false); } showsCursor.moveToPosition(filteredShows.get(position)); final int idColumnIndex = showsCursor.getColumnIndexOrThrow(ShowsTable.COLUMN_ID); final int id = showsCursor.getInt(idColumnIndex); final ContentResolver contentResolver = context.getContentResolver(); final TextView nameView = (TextView) convertView.findViewById(R.id.show_name_view); final int nameColumnIndex = showsCursor.getColumnIndexOrThrow(ShowsTable.COLUMN_NAME); final String name = showsCursor.getString(nameColumnIndex); nameView.setText(name); final ImageView bannerView = (ImageView) convertView.findViewById(R.id.banner_view); final int bannerPathColumnIndex = showsCursor.getColumnIndexOrThrow(ShowsTable.COLUMN_BANNER_PATH); final String bannerPath = showsCursor.getString(bannerPathColumnIndex); bannerView.setImageResource(R.drawable.blank_show_banner); if (bannerPath != null && !bannerPath.equals("")) { final String bannerUrl = String.format("https://thetvdb.com/banners/%s", bannerPath); final DisplayImageOptions options = new DisplayImageOptions.Builder().cacheInMemory(true) .cacheOnDisk(true).build(); ImageLoader.getInstance().displayImage(bannerUrl, bannerView, options); } final ToggleButton starredToggle = (ToggleButton) convertView.findViewById(R.id.show_starred_toggle); final int starredColumnIndex = showsCursor.getColumnIndexOrThrow(ShowsTable.COLUMN_STARRED); final boolean starred = showsCursor.getInt(starredColumnIndex) > 0 ? true : false; starredToggle.setOnCheckedChangeListener(null); starredToggle.setChecked(starred); starredToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { final AsyncQueryHandler handler = new AsyncQueryHandler(contentResolver) { }; final ContentValues showValues = new ContentValues(); showValues.put(ShowsTable.COLUMN_STARRED, isChecked); final Uri showUri = Uri.withAppendedPath(ShowsProvider.CONTENT_URI_SHOWS, String.valueOf(id)); handler.startUpdate(0, null, showUri, showValues, null, null); } }); final ToggleButton archivedToggle = (ToggleButton) convertView.findViewById(R.id.show_archived_toggle); final int archivedColumnIndex = showsCursor.getColumnIndexOrThrow(ShowsTable.COLUMN_ARCHIVED); final boolean archived = showsCursor.getInt(archivedColumnIndex) > 0 ? true : false; archivedToggle.setOnCheckedChangeListener(null); archivedToggle.setChecked(archived); archivedToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { final AsyncQueryHandler handler = new AsyncQueryHandler(contentResolver) { }; final ContentValues showValues = new ContentValues(); showValues.put(ShowsTable.COLUMN_ARCHIVED, isChecked); final Uri showUri = Uri.withAppendedPath(ShowsProvider.CONTENT_URI_SHOWS, String.valueOf(id)); handler.startUpdate(0, null, showUri, showValues, null, null); } }); final int numAired = episodesCounter.getNumAiredEpisodes(id); final int numWatched = episodesCounter.getNumWatchedEpisodes(id); final int numUpcoming = episodesCounter.getNumUpcomingEpisodes(id); final ProgressBar progressBar = (ProgressBar) convertView.findViewById(R.id.show_progress_bar); progressBar.setMax(numAired); progressBar.setProgress(numWatched); final TextView watchedCountView = (TextView) convertView.findViewById(R.id.watched_count_view); String watchedCountText = context.getString(R.string.watched_count, numWatched, numAired); if (numUpcoming != 0) { watchedCountText += " " + context.getString(R.string.upcoming_count, numUpcoming); } watchedCountView.setText(watchedCountText); return convertView; } } }