package ca.josephroque.bowlingcompanion.fragment;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
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.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import ca.josephroque.bowlingcompanion.Constants;
import ca.josephroque.bowlingcompanion.MainActivity;
import ca.josephroque.bowlingcompanion.R;
import ca.josephroque.bowlingcompanion.adapter.SeriesAdapter;
import ca.josephroque.bowlingcompanion.database.Contract.FrameEntry;
import ca.josephroque.bowlingcompanion.database.Contract.GameEntry;
import ca.josephroque.bowlingcompanion.database.Contract.SeriesEntry;
import ca.josephroque.bowlingcompanion.database.DatabaseHelper;
import ca.josephroque.bowlingcompanion.dialog.ChangeDateDialog;
import ca.josephroque.bowlingcompanion.theme.Theme;
import ca.josephroque.bowlingcompanion.utilities.DateUtils;
import ca.josephroque.bowlingcompanion.utilities.DisplayUtils;
import ca.josephroque.bowlingcompanion.utilities.FloatingActionButtonHandler;
import ca.josephroque.bowlingcompanion.wrapper.Series;
/**
* Created by Joseph Roque on 15-03-17. Manages the UI to display information about the series being tracked by the
* application, and offers a callback interface {@code SeriesFragment.SeriesCallback} for handling interactions.
*/
@SuppressWarnings("Convert2Lambda")
public class SeriesFragment
extends Fragment
implements
Theme.ChangeableTheme,
SeriesAdapter.SeriesEventHandler,
ChangeDateDialog.ChangeDateDialogListener,
FloatingActionButtonHandler {
/** Identifies output from this class in Logcat. */
@SuppressWarnings("unused")
private static final String TAG = "SeriesFragment";
/** Adapter to manage data displayed in mRecyclerViewSeries. */
private SeriesAdapter mAdapterSeries;
/** Callback listener for user events related to series. */
private SeriesCallback mSeriesCallback;
/** List to store series data from series table in database. */
private List<Series> mListSeries;
/** Indicates if the dialog to display the combine dialog has already been shown. */
private boolean mCombineDialogShown;
/** Indicates if the user is currently selecting a series to duplicate. */
private boolean mDuplicatingSeries;
@Override
public void onCreate(Bundle savedInstaceState) {
super.onCreate(savedInstaceState);
setHasOptionsMenu(true);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
/*
* This makes sure the container Activity has implemented
* the callback interface. If not, an exception is thrown
*/
try {
mSeriesCallback = (SeriesCallback) context;
} catch (ClassCastException ex) {
throw new ClassCastException(context.toString()
+ " must implement SeriesListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mSeriesCallback = null;
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_list, container, false);
mListSeries = new ArrayList<>();
/* View to display series dates and games to user. */
RecyclerView recyclerViewSeries = (RecyclerView) rootView.findViewById(R.id.rv_names);
recyclerViewSeries.setHasFixedSize(true);
ItemTouchHelper.SimpleCallback touchCallback = new ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(final RecyclerView.ViewHolder viewHolder, int direction) {
final int position = viewHolder.getAdapterPosition();
mListSeries.get(position).setIsDeleted(!mListSeries.get(position).wasDeleted());
mAdapterSeries.notifyItemChanged(position);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(touchCallback);
itemTouchHelper.attachToRecyclerView(recyclerViewSeries);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
recyclerViewSeries.setLayoutManager(layoutManager);
mAdapterSeries = new SeriesAdapter(getActivity(), this, mListSeries);
recyclerViewSeries.setAdapter(mAdapterSeries);
return rootView;
}
@Override
public void onResume() {
super.onResume();
if (getActivity() != null) {
MainActivity mainActivity = (MainActivity) getActivity();
mainActivity.setActionBarTitle(R.string.title_fragment_series, true);
mainActivity.setFloatingActionButtonState(R.drawable.ic_add_black_24dp, 0);
mainActivity.setDrawerState(false);
}
updateTheme();
// Creates AsyncTask to load data from database
new LoadSeriesTask(this).execute();
}
@Override
public void onPause() {
super.onPause();
mCombineDialogShown = false;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_series, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
boolean drawerOpen = ((MainActivity) getActivity()).isDrawerOpen();
MenuItem menuItem = menu.findItem(R.id.action_stats).setVisible(!drawerOpen);
Drawable drawable = menuItem.getIcon();
if (drawable != null)
drawable.setAlpha(DisplayUtils.BLACK_ICON_ALPHA);
menu.findItem(R.id.action_combine_series).setVisible(!drawerOpen
&& ((MainActivity) getActivity()).getLeagueName().equals(Constants.NAME_OPEN_LEAGUE));
menu.findItem(R.id.action_edit_date).setVisible(!drawerOpen);
super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
enableDuplicatingSeries(false);
switch (item.getItemId()) {
case R.id.action_edit_date:
showEditDateDialog();
return true;
case R.id.action_stats:
if (mSeriesCallback != null)
mSeriesCallback.onLeagueStatsOpened();
return true;
case R.id.action_combine_series:
showCombineSeriesDialog(true);
return true;
case R.id.action_duplicate_series:
showDuplicateSeriesDialog();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void updateTheme() {
mAdapterSeries.updateTheme();
}
@Override
public void onSItemClick(final int position) {
// When series is clicked, its games are displayed in a new GameFragment
if (mSeriesCallback != null)
mSeriesCallback.onSeriesSelected(mListSeries.get(position), false);
}
@Override
public void onSItemDelete(long id) {
for (int i = 0; i < mListSeries.size(); i++) {
if (mListSeries.get(i).getSeriesId() == id) {
Series series = mListSeries.remove(i);
mAdapterSeries.notifyItemRemoved(i);
deleteSeries(series.getSeriesId());
}
}
}
@Override
public void onSItemUndoDelete(long id) {
for (int i = 0; i < mListSeries.size(); i++) {
if (mListSeries.get(i).getSeriesId() == id) {
mListSeries.get(i).setIsDeleted(false);
mAdapterSeries.notifyItemChanged(i);
}
}
}
@Override
public void onViewStatsClick(final int position) {
if (mSeriesCallback != null)
mSeriesCallback.onSeriesStatsOpened(mListSeries.get(position));
}
@Override
public void onEditClick(final int position) {
DialogFragment dateDialog = ChangeDateDialog.newInstance(this, mListSeries.get(position));
dateDialog.show(getFragmentManager(), "ChangeDateDialog");
}
@Override
public void onDuplicateClick(final int position) {
enableDuplicatingSeries(false);
new DuplicateSeriesTask(this).execute(mListSeries.get(position));
}
@SuppressWarnings("CheckStyle")
@Override
public void onChangeDate(final Series series, int year, int month, int day) {
final int index = mListSeries.indexOf(series);
final Series seriesInList = mListSeries.get(index);
Calendar c = Calendar.getInstance();
c.set(year, month, day);
final SimpleDateFormat dateFormat =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CANADA);
final String formattedDate = dateFormat.format(c.getTime());
seriesInList.setSeriesDate(DateUtils.formattedDateToPrettyCompact(formattedDate.substring(0, 10)));
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapterSeries.notifyItemChanged(index);
}
});
((MainActivity) getActivity()).addSavingThread(
new Thread(new Runnable() {
@Override
public void run() {
SQLiteDatabase database =
DatabaseHelper.getInstance(getContext()).getWritableDatabase();
ContentValues values = new ContentValues();
values.put(SeriesEntry.COLUMN_SERIES_DATE, formattedDate);
database.beginTransaction();
try {
database.update(SeriesEntry.TABLE_NAME,
values,
SeriesEntry._ID + "=?",
new String[]{String.valueOf(seriesInList.getSeriesId())});
database.setTransactionSuccessful();
} catch (Exception ex) {
Log.e(TAG, "Series date was not updated", ex);
} finally {
database.endTransaction();
}
}
}));
}
@Override
public void onFabClick() {
if (mSeriesCallback != null)
mSeriesCallback.onCreateNewSeries(false);
}
@Override
public void onSecondaryFabClick() {
// does nothing - secondary fab has no function here
}
/**
* Prompts user to create a new series by copying another.
*/
private void showDuplicateSeriesDialog() {
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
enableDuplicatingSeries(true);
}
dialog.dismiss();
}
};
new AlertDialog.Builder(getContext())
.setMessage(R.string.dialog_duplicate_series)
.setPositiveButton(R.string.dialog_okay, listener)
.setNegativeButton(R.string.dialog_cancel, listener)
.create()
.show();
}
/**
* Prompts user to combine series in the league into one. Only shown if the user has not disabled the option in the
* preferences.
*
* @param manuallyOpened if true, the dialog will be shown regardless of their settings, or if they have seen it
* before.
*/
private void showCombineSeriesDialog(boolean manuallyOpened) {
MainActivity mainActivity = (MainActivity) getActivity();
if (mainActivity == null || (mCombineDialogShown && !manuallyOpened)
|| !mainActivity.getLeagueName().equals(Constants.NAME_OPEN_LEAGUE))
return;
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
if (!preferences.getBoolean(Constants.KEY_ASK_COMBINE, true) && !manuallyOpened)
return;
mCombineDialogShown = true;
boolean showDialog = false;
Series prevSeries = null;
for (Series series : mListSeries) {
if (prevSeries != null && series.getSeriesDate().equals(prevSeries.getSeriesDate())) {
showDialog = true;
break;
}
prevSeries = series;
}
if (showDialog) {
final AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
View rootView = View.inflate(getContext(), R.layout.dialog_combine_series, null);
dialog.setView(rootView);
final AlertDialog alertDialog = dialog.create();
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
alertDialog.dismiss();
switch (v.getId()) {
case R.id.btn_combine:
startCombineSimilarSeries();
break;
case R.id.btn_do_not_ask:
preferences.edit().putBoolean(Constants.KEY_ASK_COMBINE, false)
.apply();
break;
default:
// do nothing
}
}
};
rootView.findViewById(R.id.btn_combine).setOnClickListener(listener);
rootView.findViewById(R.id.btn_do_not_combine).setOnClickListener(listener);
rootView.findViewById(R.id.btn_do_not_ask).setOnClickListener(listener);
alertDialog.show();
}
}
/**
* Displays a modal dialog to the user while similar series in the league are combined.
*/
private void startCombineSimilarSeries() {
if (getContext() == null)
return;
new CombineSimilarSeriesTask(this).execute();
}
/**
* Informs user of how to change series dates.
*/
private void showEditDateDialog() {
new AlertDialog.Builder(getContext())
.setMessage(R.string.dialog_edit_date)
.setPositiveButton(R.string.dialog_okay, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.create()
.show();
}
/**
* Enters or exits a mode where the user can select a series and be prompted to duplicate it.
*
* @param enable true to enable duplication and show options, false to disable
*/
private void enableDuplicatingSeries(boolean enable) {
View rootView = getView();
if (rootView == null)
return;
if (mDuplicatingSeries == enable)
return;
mDuplicatingSeries = enable;
final LinearLayout linearLayout = (LinearLayout) rootView.findViewById(R.id.ll_active_state);
if (mDuplicatingSeries) {
View.OnClickListener cancelDuplicateListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
enableDuplicatingSeries(false);
}
};
TextView txtCancel = (TextView) linearLayout.findViewById(R.id.tv_active_state);
txtCancel.setOnClickListener(cancelDuplicateListener);
txtCancel.setText(R.string.text_cancel_duplicate);
ImageView ivCopy = (ImageView) linearLayout.findViewById(R.id.iv_active_state);
ivCopy.setOnClickListener(cancelDuplicateListener);
ivCopy.setImageResource(R.drawable.ic_content_copy_white_24dp);
ivCopy.setColorFilter(Theme.getSecondaryThemeColor());
linearLayout.findViewById(R.id.iv_active_state_clear).setOnClickListener(cancelDuplicateListener);
linearLayout.setVisibility(View.VISIBLE);
} else {
linearLayout.setVisibility(View.GONE);
linearLayout.findViewById(R.id.iv_active_state).setOnClickListener(null);
linearLayout.findViewById(R.id.tv_active_state).setOnClickListener(null);
linearLayout.findViewById(R.id.iv_active_state_clear).setOnClickListener(null);
}
mAdapterSeries.setDuplicatingSeries(mDuplicatingSeries);
}
/**
* Deletes all data regarding a certain series id in the database.
*
* @param seriesId id of series whose data will be deleted
*/
private void deleteSeries(final long seriesId) {
new Thread(new Runnable() {
@Override
public void run() {
String[] whereArgs = {String.valueOf(seriesId)};
SQLiteDatabase database =
DatabaseHelper.getInstance(getContext()).getWritableDatabase();
database.beginTransaction();
try {
database.delete(SeriesEntry.TABLE_NAME,
SeriesEntry._ID + "=?",
whereArgs);
database.setTransactionSuccessful();
} catch (Exception ex) {
Log.i(TAG, "Unable to delete series", ex);
} finally {
database.endTransaction();
}
}
}).start();
}
/**
* Creates a new instance of this fragment to display.
*
* @return a new instance of SeriesFragment
*/
public static SeriesFragment newInstance() {
return new SeriesFragment();
}
/**
* Loads series relevant to the current bowler and league, and displays them in the recycler view.
*/
private static final class LoadSeriesTask
extends AsyncTask<Void, Void, List<Series>> {
/** Weak reference to the parent fragment. */
private final WeakReference<SeriesFragment> mFragment;
/**
* Assigns a weak reference to the parent fragment.
*
* @param fragment parent fragment
*/
private LoadSeriesTask(SeriesFragment fragment) {
mFragment = new WeakReference<>(fragment);
}
@Override
protected void onPreExecute() {
SeriesFragment fragment = mFragment.get();
if (fragment == null)
return;
fragment.mListSeries.clear();
fragment.mAdapterSeries.notifyDataSetChanged();
}
@Override
protected List<Series> doInBackground(Void... params) {
SeriesFragment fragment = mFragment.get();
if (fragment == null || !fragment.isAdded() || mFragment.get().getActivity() == null)
return null;
MainActivity mainActivity = (MainActivity) fragment.getActivity();
if (mainActivity == null)
return null;
MainActivity.waitForSaveThreads(new WeakReference<>(mainActivity));
SQLiteDatabase database =
DatabaseHelper.getInstance(mainActivity).getReadableDatabase();
List<Series> listSeries = new ArrayList<>();
String rawSeriesQuery = "SELECT "
+ "series." + SeriesEntry._ID + " AS sid, "
+ SeriesEntry.COLUMN_SERIES_DATE + ", "
+ GameEntry.COLUMN_SCORE + ", "
+ GameEntry.COLUMN_GAME_NUMBER + ", "
+ GameEntry.COLUMN_MATCH_PLAY
+ " FROM " + SeriesEntry.TABLE_NAME + " AS series"
+ " INNER JOIN " + GameEntry.TABLE_NAME
+ " ON sid=" + GameEntry.COLUMN_SERIES_ID
+ " WHERE " + SeriesEntry.COLUMN_LEAGUE_ID + "=?"
+ " ORDER BY " + SeriesEntry.COLUMN_SERIES_DATE + " DESC, "
+ GameEntry.COLUMN_GAME_NUMBER;
String[] rawSeriesArgs = {
String.valueOf(mainActivity.getLeagueId())
};
Cursor cursor = database.rawQuery(rawSeriesQuery, rawSeriesArgs);
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
long seriesId = cursor.getLong(cursor.getColumnIndex("sid"));
String seriesDate = cursor.getString(cursor.getColumnIndex(SeriesEntry.COLUMN_SERIES_DATE));
short gameScore = cursor.getShort(cursor.getColumnIndex(GameEntry.COLUMN_SCORE));
byte matchPlay = (byte) cursor.getInt(cursor.getColumnIndex(GameEntry.COLUMN_MATCH_PLAY));
if (listSeries.size() == 0 || listSeries.get(listSeries.size() - 1).getSeriesId() != seriesId) {
listSeries.add(new Series(seriesId,
mainActivity.getLeagueId(),
DateUtils.formattedDateToPrettyCompact(seriesDate),
new ArrayList<Short>(),
new ArrayList<Byte>()));
}
listSeries.get(listSeries.size() - 1).getSeriesMatchPlayResults().add(matchPlay);
listSeries.get(listSeries.size() - 1).getSeriesGames().add(gameScore);
cursor.moveToNext();
}
}
cursor.close();
return listSeries;
}
@Override
@SuppressWarnings("unchecked")
protected void onPostExecute(List<Series> listSeries) {
SeriesFragment fragment = mFragment.get();
if (listSeries == null || fragment == null)
return;
fragment.mListSeries.addAll(listSeries);
fragment.mAdapterSeries.notifyDataSetChanged();
fragment.showCombineSeriesDialog(false);
}
}
/**
* Creates a new series entry in the database which is a copy of another series. The new series will use "manual"
* games, rather than using the frames, so the user's stats are not affected.
*/
private static final class DuplicateSeriesTask
extends AsyncTask<Series, Void, Boolean> {
/** Weak reference to the parent fragment. */
private final WeakReference<SeriesFragment> mFragment;
/**
* Assigns a weak reference to the parent fragment.
*
* @param fragment parent fragment
*/
private DuplicateSeriesTask(SeriesFragment fragment) {
mFragment = new WeakReference<>(fragment);
}
@Override
protected Boolean doInBackground(Series... series) {
SeriesFragment fragment = mFragment.get();
if (fragment == null)
return false;
Context context = fragment.getContext();
if (context == null)
return false;
Series seriesToDuplicate = series[0];
long[] gameId = new long[seriesToDuplicate.getNumberOfGames()];
SQLiteDatabase database = DatabaseHelper.getInstance(context).getReadableDatabase();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CANADA);
String seriesDate = dateFormat.format(new Date());
database.beginTransaction();
try {
ContentValues values = new ContentValues();
values.put(SeriesEntry.COLUMN_SERIES_DATE, seriesDate);
values.put(SeriesEntry.COLUMN_LEAGUE_ID, seriesToDuplicate.getLeagueId());
long seriesId = database.insert(SeriesEntry.TABLE_NAME, null, values);
List<Short> gameScores = seriesToDuplicate.getSeriesGames();
for (byte i = 0; i < gameScores.size(); i++) {
values = new ContentValues();
values.put(GameEntry.COLUMN_GAME_NUMBER, i + 1);
values.put(GameEntry.COLUMN_SCORE, gameScores.get(i));
values.put(GameEntry.COLUMN_IS_LOCKED, 1);
values.put(GameEntry.COLUMN_IS_MANUAL, 1);
values.put(GameEntry.COLUMN_SERIES_ID, seriesId);
gameId[i] = database.insert(GameEntry.TABLE_NAME, null, values);
for (byte j = 0; j < Constants.NUMBER_OF_FRAMES; j++) {
values = new ContentValues();
values.put(FrameEntry.COLUMN_FRAME_NUMBER, j + 1);
values.put(FrameEntry.COLUMN_GAME_ID, gameId[i]);
database.insert(FrameEntry.TABLE_NAME, null, values);
}
}
database.setTransactionSuccessful();
} catch (Exception ex) {
throw new RuntimeException("Could not duplicate series in database: " + ex.getMessage());
} finally {
database.endTransaction();
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
SeriesFragment fragment = mFragment.get();
if (fragment == null || fragment.getContext() == null || !fragment.isVisible())
return;
new LoadSeriesTask(fragment).execute();
}
}
/**
* Combines series with similar dates in the database into singular series, with maximum 5 games. If a combined
* series would have more than 5 series, extra games are moved into a new series.
*/
private static final class CombineSimilarSeriesTask
extends AsyncTask<Void, Void, Void> {
/** Progress dialog. */
private WeakReference<ProgressDialog> mProgressDialog;
/** Weak reference to the parent fragment. */
private final WeakReference<SeriesFragment> mFragment;
/**
* Assigns a weak reference to the parent fragment.
*
* @param fragment parent fragment
*/
private CombineSimilarSeriesTask(SeriesFragment fragment) {
mFragment = new WeakReference<>(fragment);
}
@Override
protected void onPreExecute() {
SeriesFragment fragment = mFragment.get();
if (fragment == null || fragment.getContext() == null)
return;
ProgressDialog pd = new ProgressDialog(fragment.getContext());
pd.setTitle("Combining series...");
pd.setIndeterminate(true);
pd.setCancelable(false);
pd.show();
mProgressDialog = new WeakReference<>(pd);
}
@Override
protected Void doInBackground(Void... params) {
final SeriesFragment fragment = mFragment.get();
if (fragment == null || !fragment.isAdded()) {
dismissDialog();
return null;
}
final MainActivity mainActivity = (MainActivity) fragment.getActivity();
if (mainActivity == null) {
dismissDialog();
return null;
}
mainActivity.addSavingThread(new Thread(
new Runnable() {
@Override
public void run() {
SQLiteDatabase db = DatabaseHelper.getInstance(mainActivity)
.getReadableDatabase();
String seriesQuery = "SELECT "
+ "s." + SeriesEntry._ID + " AS sid, "
+ SeriesEntry.COLUMN_SERIES_DATE + ", "
+ "g." + GameEntry._ID + " AS gid, "
+ GameEntry.COLUMN_SERIES_ID + ", "
+ GameEntry.COLUMN_GAME_NUMBER
+ " FROM " + SeriesEntry.TABLE_NAME + " AS s"
+ " INNER JOIN " + GameEntry.TABLE_NAME + " AS g"
+ " ON g." + GameEntry.COLUMN_SERIES_ID + "=sid"
+ " WHERE " + SeriesEntry.COLUMN_LEAGUE_ID + "=?"
+ " ORDER BY " + SeriesEntry.COLUMN_SERIES_DATE + ", "
+ GameEntry.COLUMN_GAME_NUMBER;
Cursor cursor = db.rawQuery(seriesQuery,
new String[]{String.valueOf(mainActivity.getLeagueId())});
String lastSeriesDate = null;
int startOfLastSeries = -1;
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
int gameNumber = cursor.getInt(cursor.getColumnIndex(
GameEntry.COLUMN_GAME_NUMBER));
if (gameNumber == 1) {
String seriesDate = cursor.getString(cursor.getColumnIndex(
SeriesEntry.COLUMN_SERIES_DATE));
String dateFormatted = DateUtils.formattedDateToPrettyCompact(seriesDate);
if (dateFormatted.equals(lastSeriesDate)) {
int startOfCurrentSeries = cursor.getPosition();
combineSeries(db,
cursor,
startOfLastSeries,
startOfCurrentSeries);
cursor.moveToPosition(startOfCurrentSeries);
}
startOfLastSeries = cursor.getPosition();
lastSeriesDate = dateFormatted;
}
cursor.moveToNext();
}
}
if (!cursor.isClosed())
cursor.close();
mainActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
dismissDialog();
new LoadSeriesTask(fragment).execute();
}
});
}
}));
return null;
}
/**
* Combines two series into one.
*
* @param db database
* @param cursor cursor with series date
* @param startOfFirstSeries position in cursor of game 1 of the first series
* @param startOfSecondSeries position in cursor of game 1 of the second series
*/
private void combineSeries(SQLiteDatabase db,
Cursor cursor,
int startOfFirstSeries,
int startOfSecondSeries) {
int firstSeriesNumGames = startOfSecondSeries - startOfFirstSeries;
int secondSeriesNumGames = 0;
cursor.moveToPosition(startOfFirstSeries);
long firstSeriesId = cursor.getLong(cursor.getColumnIndex("sid"));
cursor.moveToPosition(startOfSecondSeries);
long secondSeriesId = cursor.getLong(cursor.getColumnIndex("sid"));
while (!cursor.isAfterLast()
&& cursor.getLong(cursor.getColumnIndex("sid")) == secondSeriesId) {
secondSeriesNumGames++;
cursor.moveToNext();
}
try {
db.beginTransaction();
while (secondSeriesNumGames > 0
&& firstSeriesNumGames < Constants.MAX_NUMBER_LEAGUE_GAMES) {
cursor.moveToPosition(startOfSecondSeries);
long gameId = cursor.getLong(cursor.getColumnIndex("gid"));
ContentValues values = new ContentValues();
values.put(GameEntry.COLUMN_GAME_NUMBER, firstSeriesNumGames + 1);
values.put(GameEntry.COLUMN_SERIES_ID, firstSeriesId);
db.update(GameEntry.TABLE_NAME,
values,
GameEntry._ID + "=?",
new String[]{Long.toString(gameId)});
firstSeriesNumGames++;
secondSeriesNumGames--;
startOfSecondSeries++;
}
int secondSeriesNewNumGames = 0;
while (secondSeriesNumGames > 0) {
cursor.moveToPosition(startOfSecondSeries);
long gameId = cursor.getLong(cursor.getColumnIndex("gid"));
ContentValues values = new ContentValues();
values.put(GameEntry.COLUMN_GAME_NUMBER, secondSeriesNewNumGames + 1);
db.update(GameEntry.TABLE_NAME,
values,
GameEntry._ID + "=?",
new String[]{Long.toString(gameId)});
startOfSecondSeries++;
secondSeriesNewNumGames++;
secondSeriesNumGames--;
}
db.setTransactionSuccessful();
} catch (Exception ex) {
Log.e(TAG, "Error combining series", ex);
} finally {
db.endTransaction();
}
}
/**
* Dismisses the progress dialog if it is still showing.
*/
private void dismissDialog() {
ProgressDialog pd = mProgressDialog.get();
if (pd != null)
pd.dismiss();
}
}
/**
* Container Activity must implement this interface to allow GameFragment/StatsFragment to be loaded when a series
* is selected.
*/
public interface SeriesCallback {
/**
* Should be overridden to created a GameFragment with the games belonging to the series represented by
* seriesId.
*
* @param series series whose games will be displayed
* @param isEvent indicates if an event series is being displayed or not
*/
void onSeriesSelected(Series series, boolean isEvent);
/**
* Called when user opts to create a new series.
*
* @param isEvent indicates if the new series will belong to an event
*/
void onCreateNewSeries(boolean isEvent);
/**
* Displays the stats of the current league in a new StatsFragment.
*/
void onLeagueStatsOpened();
/**
* Displays the stats of the selected series in a new StatsFragment.
*
* @param series series to view stats for
*/
void onSeriesStatsOpened(Series series);
}
}