package com.boardgamegeek.ui;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.Dialog;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.ColorInt;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ContentLoadingProgressBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AlertDialog.Builder;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.graphics.Palette;
import android.support.v7.view.menu.MenuBuilder;
import android.support.v7.view.menu.MenuBuilder.Callback;
import android.support.v7.view.menu.MenuPopupHelper;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.support.v7.widget.SwitchCompat;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.Chronometer;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.boardgamegeek.R;
import com.boardgamegeek.events.ColorAssignmentCompleteEvent;
import com.boardgamegeek.model.Play;
import com.boardgamegeek.model.Player;
import com.boardgamegeek.model.builder.PlayBuilder;
import com.boardgamegeek.model.persister.PlayPersister;
import com.boardgamegeek.provider.BggContract;
import com.boardgamegeek.provider.BggContract.GameColors;
import com.boardgamegeek.provider.BggContract.Games;
import com.boardgamegeek.provider.BggContract.PlayPlayers;
import com.boardgamegeek.provider.BggContract.Plays;
import com.boardgamegeek.service.SyncService;
import com.boardgamegeek.tasks.ColorAssignerTask;
import com.boardgamegeek.ui.adapter.AutoCompleteAdapter;
import com.boardgamegeek.ui.dialog.ColorPickerDialogFragment;
import com.boardgamegeek.ui.dialog.NumberPadDialogFragment;
import com.boardgamegeek.ui.widget.DatePickerDialogFragment;
import com.boardgamegeek.ui.widget.PlayerRow;
import com.boardgamegeek.util.ActivityUtils;
import com.boardgamegeek.util.ColorUtils;
import com.boardgamegeek.util.DateTimeUtils;
import com.boardgamegeek.util.DialogUtils;
import com.boardgamegeek.util.HelpUtils;
import com.boardgamegeek.util.ImageUtils;
import com.boardgamegeek.util.NotificationUtils;
import com.boardgamegeek.util.PaletteUtils;
import com.boardgamegeek.util.PreferencesUtils;
import com.boardgamegeek.util.PresentationUtils;
import com.boardgamegeek.util.ShowcaseViewWizard;
import com.boardgamegeek.util.StringUtils;
import com.boardgamegeek.util.TaskUtils;
import com.boardgamegeek.util.ToolbarUtils;
import com.boardgamegeek.util.UIUtils;
import com.boardgamegeek.util.fabric.AddFieldEvent;
import com.crashlytics.android.answers.Answers;
import com.crashlytics.android.answers.CustomEvent;
import com.github.amlcurran.showcaseview.targets.Target;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import butterknife.BindDimen;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnCheckedChanged;
import butterknife.OnClick;
import butterknife.OnFocusChange;
import hugo.weaving.DebugLog;
import icepick.Icepick;
import icepick.State;
import timber.log.Timber;
public class LogPlayActivity extends AppCompatActivity {
private static final int HELP_VERSION = 3;
private static final int REQUEST_ADD_PLAYER = 1;
private static final int REQUEST_EDIT_PLAYER = 2;
private static final int TOKEN_PLAY = 1;
private static final int TOKEN_PLAYERS = 1 << 1;
private static final int TOKEN_COLORS = 1 << 2;
private static final int TOKEN_UNINITIALIZED = 1 << 15;
@State long internalId = BggContract.INVALID_ID;
private int gameId;
private String gameName;
private boolean isRequestingToEndPlay;
private boolean isRequestingRematch;
private String thumbnailUrl;
private String imageUrl;
private QueryHandler queryHandler;
private int outstandingQueries = TOKEN_UNINITIALIZED;
private Play play;
private Play originalPlay;
private PlayAdapter playAdapter;
private AutoCompleteAdapter locationAdapter;
private AlertDialog.Builder addPlayersBuilder;
private Player lastRemovedPlayer;
private final List<Player> playersToAdd = new ArrayList<>();
private final List<String> userNames = new ArrayList<>();
private final List<String> names = new ArrayList<>();
private final ArrayList<String> gameColors = new ArrayList<>();
@BindView(R.id.coordinator) CoordinatorLayout coordinatorLayout;
@BindView(R.id.progress) ContentLoadingProgressBar progressView;
@BindView(R.id.fab) FloatingActionButton fab;
@BindView(android.R.id.list) RecyclerView recyclerView;
private ShowcaseViewWizard showcaseWizard;
@ColorInt private int fabColor;
private final Paint swipePaint = new Paint();
private Bitmap deleteIcon;
private Bitmap editIcon;
@BindDimen(R.dimen.material_margin_horizontal) float horizontalPadding;
private ItemTouchHelper itemTouchHelper;
@State boolean isUserShowingLocation;
@State boolean isUserShowingLength;
@State boolean isUserShowingQuantity;
@State boolean isUserShowingIncomplete;
@State boolean isUserShowingNoWinStats;
@State boolean isUserShowingComments;
@State boolean isUserShowingPlayers;
@State boolean shouldDeletePlayOnActivityCancel;
@State boolean arePlayersCustomSorted;
private boolean isLaunchingActivity;
private boolean shouldSaveOnPause = true;
private final View.OnClickListener actionBarListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
onActionBarItemSelected(v.getId());
}
};
@SuppressLint("HandlerLeak")
private class QueryHandler extends AsyncQueryHandler {
public QueryHandler(ContentResolver cr) {
super(cr);
}
@DebugLog
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// If the query didn't return a cursor for some reason return
if (cursor == null) {
return;
}
// If the Activity is finishing, then close the cursor
if (isFinishing()) {
cursor.close();
return;
}
switch (token) {
case TOKEN_PLAY:
if (cursor.getCount() == 0) {
// The cursor is empty. This can happen if the play was deleted
cursor.close();
LogPlayActivity.this.finish();
return;
}
cursor.moveToFirst();
play = PlayBuilder.fromCursor(cursor);
cursor.close();
if (isRequestingToEndPlay) {
play.end();
}
if ((outstandingQueries & TOKEN_PLAYERS) != 0) {
queryHandler.startQuery(TOKEN_PLAYERS, null, Plays.buildPlayerUri(internalId), PlayBuilder.PLAYER_PROJECTION, null, null, null);
}
setModelIfDone(token);
break;
case TOKEN_PLAYERS:
try {
PlayBuilder.addPlayers(cursor, play);
} finally {
cursor.close();
}
if (play.getPlayerCount() > 0) {
arePlayersCustomSorted = play.arePlayersCustomSorted();
} else {
arePlayersCustomSorted = getIntent().getBooleanExtra(ActivityUtils.KEY_CUSTOM_PLAYER_SORT, false);
}
setModelIfDone(token);
break;
case TOKEN_COLORS:
if (cursor.getCount() == 0) {
cursor.close();
} else {
try {
if (cursor.moveToFirst()) {
gameColors.clear();
do {
gameColors.add(cursor.getString(0));
} while (cursor.moveToNext());
}
} finally {
cursor.close();
}
}
setModelIfDone(token);
break;
default:
cursor.close();
break;
}
}
}
@DebugLog
private void setModelIfDone(int queryType) {
synchronized (this) {
outstandingQueries &= ~queryType;
if (outstandingQueries == 0) {
if (play == null) {
// create a new play
play = new Play(gameId, gameName);
play.setCurrentDate();
long lastPlay = PreferencesUtils.getLastPlayTime(this);
if (DateTimeUtils.howManyHoursOld(lastPlay) < 3) {
play.location = PreferencesUtils.getLastPlayLocation(this);
play.setPlayers(PreferencesUtils.getLastPlayPlayers(this));
play.pickStartPlayer(0);
}
}
if (isRequestingRematch) {
play = PlayBuilder.rematch(play);
internalId = BggContract.INVALID_ID;
}
originalPlay = PlayBuilder.copy(play);
finishDataLoad();
}
}
}
@DebugLog
private void finishDataLoad() {
outstandingQueries = 0;
if (isRequestingToEndPlay) {
cancelNotification();
} else {
maybeShowNotification();
}
playAdapter.refresh();
progressView.hide();
recyclerView.setVisibility(View.VISIBLE);
}
@DebugLog
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logplay);
ToolbarUtils.setDoneCancelActionBarView(this, actionBarListener);
ButterKnife.bind(this);
playAdapter = new PlayAdapter(this);
recyclerView.setAdapter(playAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
swipePaint.setColor(ContextCompat.getColor(this, R.color.medium_blue));
deleteIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_delete_white);
editIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_edit_white);
itemTouchHelper = new ItemTouchHelper(
new SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (!(viewHolder instanceof PlayAdapter.PlayerViewHolder)) return;
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
View itemView = viewHolder.itemView;
// fade and slide item
float width = (float) itemView.getWidth();
float alpha = 1.0f - Math.abs(dX) / width;
itemView.setAlpha(alpha);
itemView.setTranslationX(dX);
// show background with an icon
Bitmap icon = editIcon;
if (dX > 0) {
icon = deleteIcon;
}
float verticalPadding = (itemView.getHeight() - icon.getHeight()) / 2;
RectF background;
Rect iconSrc;
RectF iconDst;
if (dX > 0) {
swipePaint.setColor(ContextCompat.getColor(LogPlayActivity.this, R.color.delete));
background = new RectF((float) itemView.getLeft(), (float) itemView.getTop(), dX, (float) itemView.getBottom());
iconSrc = new Rect(0, 0, (int) (dX - itemView.getLeft() - horizontalPadding), icon.getHeight());
iconDst = new RectF((float) itemView.getLeft() + horizontalPadding, (float) itemView.getTop() + verticalPadding, Math.min(itemView.getLeft() + horizontalPadding + icon.getWidth(), dX), (float) itemView.getBottom() - verticalPadding);
} else {
swipePaint.setColor(ContextCompat.getColor(LogPlayActivity.this, R.color.edit));
background = new RectF((float) itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom());
iconSrc = new Rect(Math.max(icon.getWidth() + (int) horizontalPadding + (int) dX, 0), 0, icon.getWidth(), icon.getHeight());
iconDst = new RectF(Math.max((float) itemView.getRight() + dX, (float) itemView.getRight() - horizontalPadding - icon.getWidth()), (float) itemView.getTop() + verticalPadding, (float) itemView.getRight() - horizontalPadding, (float) itemView.getBottom() - verticalPadding);
}
c.drawRect(background, swipePaint);
c.drawBitmap(icon, iconSrc, iconDst, swipePaint);
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
@Override
public void onSwiped(ViewHolder viewHolder, int swipeDir) {
final int position = playAdapter.getPlayerPosition(viewHolder.getAdapterPosition());
if (swipeDir == ItemTouchHelper.RIGHT) {
lastRemovedPlayer = playAdapter.getPlayer(position);
if (lastRemovedPlayer == null) return;
String description = lastRemovedPlayer.getDescription();
if (TextUtils.isEmpty(description)) {
description = getString(R.string.title_player);
}
String message = getString(R.string.msg_player_deleted, description);
Snackbar
.make(coordinatorLayout, message, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.undo, new View.OnClickListener() {
@Override
public void onClick(View v) {
if (lastRemovedPlayer == null) return;
play.addPlayer(lastRemovedPlayer);
playAdapter.notifyPlayerAdded(position);
}
})
.show();
play.removePlayer(lastRemovedPlayer, !arePlayersCustomSorted);
playAdapter.notifyPlayerRemoved(position);
} else {
editPlayer(position);
}
}
@Override
public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
if (play == null || playAdapter == null) return false;
if (!(target instanceof PlayAdapter.PlayerViewHolder)) return false;
final int fromPosition = viewHolder.getAdapterPosition();
final int toPosition = target.getAdapterPosition();
int from = playAdapter.getPlayerPosition(fromPosition);
int to = playAdapter.getPlayerPosition(toPosition);
if (!play.reorderPlayers(from + 1, to + 1)) {
Toast.makeText(LogPlayActivity.this, "Something went wrong", Toast.LENGTH_LONG).show();
} else {
playAdapter.notifyItemMoved(fromPosition, toPosition);
return true;
}
return false;
}
@Override
public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setBackgroundColor(0);
playAdapter.notifyPlayersChanged();
}
@Override
public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
// We only want the active item to change
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
viewHolder.itemView.setBackgroundColor(ContextCompat.getColor(LogPlayActivity.this, R.color.light_blue_transparent));
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
if (arePlayersCustomSorted) return makeMovementFlags(0, getSwipeDirs(recyclerView, viewHolder));
return super.getMovementFlags(recyclerView, viewHolder);
}
@Override
public boolean isLongPressDragEnabled() {
return false;
}
});
itemTouchHelper.attachToRecyclerView(recyclerView);
queryHandler = new QueryHandler(getContentResolver());
final Intent intent = getIntent();
internalId = intent.getLongExtra(ActivityUtils.KEY_ID, BggContract.INVALID_ID);
gameId = intent.getIntExtra(ActivityUtils.KEY_GAME_ID, BggContract.INVALID_ID);
gameName = intent.getStringExtra(ActivityUtils.KEY_GAME_NAME);
isRequestingToEndPlay = intent.getBooleanExtra(ActivityUtils.KEY_END_PLAY, false);
isRequestingRematch = intent.getBooleanExtra(ActivityUtils.KEY_REMATCH, false);
thumbnailUrl = intent.getStringExtra(ActivityUtils.KEY_THUMBNAIL_URL);
imageUrl = intent.getStringExtra(ActivityUtils.KEY_IMAGE_URL);
if (gameId <= 0) {
String message = "Can't log a play without a game ID.";
Timber.w(message);
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
finish();
}
playAdapter.notifyLayoutChanged(R.layout.row_log_play_player_header);
Icepick.restoreInstanceState(this, savedInstanceState);
if (savedInstanceState != null) {
play = PlayBuilder.fromBundle(savedInstanceState, "P");
originalPlay = PlayBuilder.fromBundle(savedInstanceState, "O");
}
startQuery();
setUpShowcaseViewWizard();
showcaseWizard.maybeShowHelp();
PresentationUtils.ensureFabIsShown(fab);
}
@DebugLog
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@DebugLog
@Override
protected void onResume() {
super.onResume();
isLaunchingActivity = false;
locationAdapter = new AutoCompleteAdapter(this, Plays.LOCATION, Plays.buildLocationsUri());
playAdapter.refresh();
}
@DebugLog
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (outstandingQueries == 0) {
PlayBuilder.toBundle(play, outState, "P");
PlayBuilder.toBundle(originalPlay, outState, "O");
}
Icepick.saveInstanceState(this, outState);
}
@DebugLog
@Override
protected void onPause() {
super.onPause();
locationAdapter.changeCursor(null);
if (shouldSaveOnPause && !isLaunchingActivity) {
saveDraft(false);
}
}
@DebugLog
@Override
protected void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
@DebugLog
@Override
public void onBackPressed() {
saveDraft(true);
setResult(Activity.RESULT_OK);
finish();
}
@DebugLog
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
Player player = data.getParcelableExtra(LogPlayerActivity.KEY_PLAYER);
int position = data.getIntExtra(LogPlayerActivity.KEY_POSITION, LogPlayerActivity.INVALID_POSITION);
if (requestCode == REQUEST_ADD_PLAYER) {
play.addPlayer(player);
maybeShowNotification();
addNewPlayer();
} else if (requestCode == REQUEST_EDIT_PLAYER) {
if (position == LogPlayerActivity.INVALID_POSITION) {
Timber.w("Invalid player position after edit");
} else {
play.replaceOrAddPlayer(player, position);
playAdapter.notifyPlayerChanged(position);
recyclerView.smoothScrollToPosition(position);
}
} else {
Timber.w("Received invalid request code: %d", requestCode);
}
} else if (resultCode == RESULT_CANCELED) {
playAdapter.notifyPlayersChanged();
recyclerView.smoothScrollToPosition(playAdapter.getItemCount());
}
}
@SuppressWarnings("unused")
@DebugLog
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(ColorAssignmentCompleteEvent event) {
Answers.getInstance().logCustom(new CustomEvent("LogPlayColorAssignment"));
EventBus.getDefault().removeStickyEvent(event);
if (event.isSuccessful()) {
playAdapter.notifyPlayersChanged();
}
if (event.getMessageId() != 0) {
Snackbar.make(coordinatorLayout, event.getMessageId(), Snackbar.LENGTH_LONG).show();
}
}
@DebugLog
private void setUpShowcaseViewWizard() {
showcaseWizard = new ShowcaseViewWizard(this, HelpUtils.HELP_LOGPLAY_KEY, HELP_VERSION);
showcaseWizard.addTarget(R.string.help_logplay, Target.NONE);
}
@DebugLog
private boolean shouldHideLocation() {
return play != null && !PreferencesUtils.showLogPlayLocation(this) && !isUserShowingLocation && TextUtils.isEmpty(play.location);
}
@DebugLog
private boolean shouldHideLength() {
return play != null && !PreferencesUtils.showLogPlayLength(this) && !isUserShowingLength && !(play.length > 0) && !play.hasStarted();
}
@DebugLog
private boolean shouldHideQuantity() {
return play != null && !PreferencesUtils.showLogPlayQuantity(this) && !isUserShowingQuantity && !(play.quantity > 1);
}
@DebugLog
private boolean shouldHideIncomplete() {
return play != null && !PreferencesUtils.showLogPlayIncomplete(this) && !isUserShowingIncomplete && !play.Incomplete();
}
@DebugLog
private boolean shouldHideNoWinStats() {
return play != null && !PreferencesUtils.showLogPlayNoWinStats(this) && !isUserShowingNoWinStats && !play.NoWinStats();
}
@DebugLog
private boolean shouldHideComments() {
return play != null && !PreferencesUtils.showLogPlayComments(this) && !isUserShowingComments && TextUtils.isEmpty(play.comments);
}
@DebugLog
private boolean shouldHidePlayers() {
return play != null && !PreferencesUtils.showLogPlayPlayerList(this) && !isUserShowingPlayers && (play.getPlayerCount() == 0);
}
@DebugLog
private void startQuery() {
if (play != null) {
// we already have the play from the saved instance
finishDataLoad();
} else {
outstandingQueries = TOKEN_COLORS;
if (internalId != BggContract.INVALID_ID) {
// Editing or copying an existing play, so retrieve it
shouldDeletePlayOnActivityCancel = false;
outstandingQueries |= TOKEN_PLAY | TOKEN_PLAYERS;
if (isRequestingRematch) {
shouldDeletePlayOnActivityCancel = true;
}
queryHandler.startQuery(TOKEN_PLAY, null, Plays.buildPlayUri(internalId), PlayBuilder.PLAY_PROJECTION, null, null, null);
} else {
// Starting a new play
shouldDeletePlayOnActivityCancel = true;
arePlayersCustomSorted = getIntent().getBooleanExtra(ActivityUtils.KEY_CUSTOM_PLAYER_SORT, false);
}
queryHandler.startQuery(TOKEN_COLORS, null, Games.buildColorsUri(gameId), new String[] { GameColors.COLOR }, null, null, null);
}
}
@DebugLog
private void onActionBarItemSelected(int itemId) {
switch (itemId) {
case R.id.menu_done:
if (play != null && outstandingQueries == 0) {
if (play.hasStarted()) {
saveDraft(true);
setResult(Activity.RESULT_OK);
finish();
} else {
logPlay();
}
} else {
cancel();
}
break;
case R.id.menu_cancel:
cancel();
break;
}
}
@DebugLog
private void logPlay() {
play.updateTimestamp = System.currentTimeMillis();
play.deleteTimestamp = 0;
play.dirtyTimestamp = System.currentTimeMillis();
if (save()) {
if (play.playId == 0 && DateUtils.isToday(play.getDateInMillis() + Math.max(60, play.length) * 60 * 1000)) {
PreferencesUtils.putLastPlayTime(this, System.currentTimeMillis());
PreferencesUtils.putLastPlayLocation(this, play.location);
PreferencesUtils.putLastPlayPlayers(this, play.getPlayers());
}
cancelNotification();
triggerUpload();
Toast.makeText(this, R.string.msg_logging_play, Toast.LENGTH_SHORT).show();
}
setResult(Activity.RESULT_OK);
finish();
}
@DebugLog
private void saveDraft(boolean showToast) {
if (play == null) return;
play.dirtyTimestamp = System.currentTimeMillis();
play.deleteTimestamp = 0;
if (save()) {
if (showToast) {
Toast.makeText(this, R.string.msg_saving_draft, Toast.LENGTH_SHORT).show();
}
maybeShowNotification();
}
}
private boolean save() {
if (play == null) return false;
shouldSaveOnPause = false;
final View focusedView = recyclerView.findFocus();
if (focusedView != null) focusedView.clearFocus();
internalId = new PlayPersister(this).save(play, internalId, true);
return true;
}
@DebugLog
private void cancel() {
shouldSaveOnPause = false;
if (play == null) {
setResult(RESULT_CANCELED);
finish();
} else if (play.equals(originalPlay)) {
if (shouldDeletePlayOnActivityCancel) {
play.updateTimestamp = 0;
play.deleteTimestamp = System.currentTimeMillis();
play.dirtyTimestamp = 0;
if (save()) {
triggerUpload();
}
}
setResult(RESULT_CANCELED);
finish();
} else {
if (shouldDeletePlayOnActivityCancel) {
DialogUtils.createConfirmationDialog(this, R.string.are_you_sure_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
play.updateTimestamp = 0;
play.deleteTimestamp = System.currentTimeMillis();
play.dirtyTimestamp = 0;
if (save()) {
triggerUpload();
cancelNotification();
}
setResult(RESULT_CANCELED);
finish();
}
})
.show();
} else {
DialogUtils.createCancelDialog(this).show();
}
}
}
@DebugLog
private void triggerUpload() {
SyncService.sync(this, SyncService.FLAG_SYNC_PLAYS_UPLOAD);
}
@DebugLog
@OnClick(R.id.fab)
public void addField() {
final CharSequence[] array = createAddFieldArray();
if (array == null || array.length == 0) {
return;
}
new Builder(this).setTitle(R.string.add_field)
.setItems(array, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Resources r = getResources();
String selection = array[which].toString();
if (selection.equals(r.getString(R.string.location))) {
isUserShowingLocation = true;
playAdapter.insertRow(R.layout.row_log_play_location);
} else if (selection.equals(r.getString(R.string.length))) {
isUserShowingLength = true;
playAdapter.insertRow(R.layout.row_log_play_length);
} else if (selection.equals(r.getString(R.string.quantity))) {
isUserShowingQuantity = true;
playAdapter.insertRow(R.layout.row_log_play_quantity);
} else if (selection.equals(r.getString(R.string.incomplete))) {
isUserShowingIncomplete = true;
play.setIncomplete(true);
playAdapter.insertRow(R.layout.row_log_play_incomplete);
} else if (selection.equals(r.getString(R.string.noWinStats))) {
isUserShowingNoWinStats = true;
play.setNoWinStats(true);
playAdapter.insertRow(R.layout.row_log_play_no_win_stats);
} else if (selection.equals(r.getString(R.string.comments))) {
isUserShowingComments = true;
playAdapter.insertRow(R.layout.row_log_play_comments);
} else if (selection.equals(r.getString(R.string.title_players))) {
isUserShowingPlayers = true;
playAdapter.insertRow(R.layout.row_log_play_add_player);
}
AddFieldEvent.log("Play", selection);
supportInvalidateOptionsMenu();
}
}).show();
}
@DebugLog
private CharSequence[] createAddFieldArray() {
Resources r = getResources();
List<CharSequence> list = new ArrayList<>();
if (shouldHideLocation()) list.add(r.getString(R.string.location));
if (shouldHideLength()) list.add(r.getString(R.string.length));
if (shouldHideQuantity()) list.add(r.getString(R.string.quantity));
if (shouldHideIncomplete()) list.add(r.getString(R.string.incomplete));
if (shouldHideNoWinStats()) list.add(r.getString(R.string.noWinStats));
if (shouldHideComments()) list.add(r.getString(R.string.comments));
if (shouldHidePlayers()) list.add(r.getString(R.string.title_players));
CharSequence[] array = {};
array = list.toArray(array);
return array;
}
@DebugLog
private boolean containsPlayer(String username, String name) {
for (Player p : play.getPlayers()) {
if (!TextUtils.isEmpty(username)) {
if (username.equals(p.username)) {
return true;
}
} else if (!TextUtils.isEmpty(name)) {
if (TextUtils.isEmpty(p.username) && name.equals(p.name)) {
return true;
}
}
}
return false;
}
@DebugLog
private boolean showPlayersToAddDialog() {
if (addPlayersBuilder == null) {
addPlayersBuilder = new AlertDialog.Builder(this).setTitle(R.string.title_add_players)
.setPositiveButton(android.R.string.ok, addPlayersButtonClickListener())
.setNeutralButton(R.string.more, addPlayersButtonClickListener())
.setNegativeButton(android.R.string.cancel, null);
}
playersToAdd.clear();
userNames.clear();
names.clear();
List<String> descriptions = new ArrayList<>();
String selection = null;
String[] selectionArgs = null;
if (!TextUtils.isEmpty(play.location)) {
selection = Plays.LOCATION + "=?";
selectionArgs = new String[] { play.location };
}
Cursor cursor = null;
try {
cursor = getContentResolver().query(Plays.buildPlayersByUniqueNameUri(),
new String[] { PlayPlayers._ID, PlayPlayers.USER_NAME, PlayPlayers.NAME, PlayPlayers.DESCRIPTION,
PlayPlayers.COUNT, PlayPlayers.UNIQUE_NAME }, selection, selectionArgs, PlayPlayers.SORT_BY_COUNT);
while (cursor != null && cursor.moveToNext()) {
String username = cursor.getString(1);
String name = cursor.getString(2);
if (!containsPlayer(username, name)) {
userNames.add(username);
names.add(name);
descriptions.add(cursor.getString(3));
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
if (descriptions.size() == 0) {
return false;
}
CharSequence[] array = {};
addPlayersBuilder
.setMultiChoiceItems(descriptions.toArray(array), null, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
Player player = new Player();
player.username = userNames.get(which);
player.name = names.get(which);
if (isChecked) {
playersToAdd.add(player);
} else {
playersToAdd.remove(player);
}
}
}).create().show();
return true;
}
@DebugLog
private DialogInterface.OnClickListener addPlayersButtonClickListener() {
return new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
play.setPlayers(playersToAdd);
if (!arePlayersCustomSorted) {
play.pickStartPlayer(0);
}
playAdapter.notifyPlayersChanged();
if (which == DialogInterface.BUTTON_NEUTRAL) {
addNewPlayer();
}
}
};
}
private Callback popupMenuCallback() {
return new MenuBuilder.Callback() {
@Override
public void onMenuModeChange(MenuBuilder menu) {
}
@Override
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_custom_player_order:
if (arePlayersCustomSorted) {
Answers.getInstance().logCustom(new CustomEvent("LogPlayPlayerOrder").putCustomAttribute("Order", "NotCustom"));
if (play.hasStartingPositions() && play.arePlayersCustomSorted()) {
Dialog dialog = DialogUtils.createConfirmationDialog(LogPlayActivity.this,
R.string.are_you_sure_player_sort_custom_off,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
play.pickStartPlayer(0);
arePlayersCustomSorted = false;
playAdapter.notifyPlayersChanged();
}
});
dialog.show();
} else {
play.pickStartPlayer(0);
arePlayersCustomSorted = false;
playAdapter.notifyPlayersChanged();
}
} else {
Answers.getInstance().logCustom(new CustomEvent("LogPlayPlayerOrder").putCustomAttribute("Order", "Custom"));
if (play.hasStartingPositions()) {
AlertDialog.Builder builder = new Builder(LogPlayActivity.this)
.setCancelable(true).setTitle(R.string.title_custom_player_order)
.setMessage(R.string.message_custom_player_order)
.setNegativeButton(R.string.keep, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
arePlayersCustomSorted = true;
playAdapter.notifyPlayersChanged();
}
}).setPositiveButton(R.string.clear, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
arePlayersCustomSorted = true;
play.clearPlayerPositions();
playAdapter.notifyPlayersChanged();
}
});
builder = DialogUtils.addAlertIcon(builder);
builder.create().show();
}
}
return true;
case R.id.menu_pick_start_player:
Answers.getInstance().logCustom(new CustomEvent("LogPlayPlayerOrder").putCustomAttribute("Order", "Random"));
promptPickStartPlayer();
return true;
case R.id.menu_random_start_player:
Answers.getInstance().logCustom(new CustomEvent("LogPlayPlayerOrder").putCustomAttribute("Order", "RandomStarter"));
int newSeat = new Random().nextInt(play.getPlayerCount());
play.pickStartPlayer(newSeat);
playAdapter.notifyPlayersChanged();
notifyStartPlayer();
return true;
case R.id.menu_random_player_order:
Answers.getInstance().logCustom(new CustomEvent("LogPlayPlayerOrder").putCustomAttribute("Order", "Random"));
play.randomizePlayerOrder();
playAdapter.notifyPlayersChanged();
notifyStartPlayer();
return true;
}
return false;
}
};
}
@DebugLog
private void promptPickStartPlayer() {
CharSequence[] array = createArrayOfPlayerDescriptions();
new AlertDialog.Builder(this).setTitle(R.string.title_pick_start_player)
.setItems(array, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
play.pickStartPlayer(which);
notifyStartPlayer();
playAdapter.notifyPlayersChanged();
}
}).show();
}
@DebugLog
private CharSequence[] createArrayOfPlayerDescriptions() {
String playerPrefix = getResources().getString(R.string.generic_player);
List<CharSequence> list = new ArrayList<>();
for (int i = 0; i < play.getPlayerCount(); i++) {
Player p = play.getPlayers().get(i);
String name = p.getDescription();
if (TextUtils.isEmpty(name)) {
name = String.format(playerPrefix, (i + 1));
}
list.add(name);
}
CharSequence[] array = {};
array = list.toArray(array);
return array;
}
@DebugLog
private void notifyStartPlayer() {
Player p = play.getPlayerAtSeat(1);
if (p != null) {
String name = p.getDescription();
if (TextUtils.isEmpty(name)) {
name = String.format(getResources().getString(R.string.generic_player), 1);
}
Snackbar.make(coordinatorLayout, String.format(getResources().getString(R.string.notification_start_player), name), Snackbar.LENGTH_LONG).show();
}
}
@DebugLog
private void addNewPlayer() {
editPlayer(new Intent(), REQUEST_ADD_PLAYER);
}
@DebugLog
private void editPlayer(int position) {
Player player = playAdapter.getPlayer(position);
Intent intent = new Intent();
intent.putExtra(LogPlayerActivity.KEY_PLAYER, player);
intent.putExtra(LogPlayerActivity.KEY_END_PLAY, isRequestingToEndPlay);
intent.putExtra(LogPlayerActivity.KEY_FAB_COLOR, fabColor);
if (!arePlayersCustomSorted && player != null) {
intent.putExtra(LogPlayerActivity.KEY_AUTO_POSITION, player.getSeat());
}
intent.putExtra(LogPlayerActivity.KEY_POSITION, position);
editPlayer(intent, REQUEST_EDIT_PLAYER);
playAdapter.notifyPlayerChanged(position);
}
@DebugLog
private void editPlayer(Intent intent, int requestCode) {
isLaunchingActivity = true;
intent.setClass(LogPlayActivity.this, LogPlayerActivity.class);
intent.putExtra(LogPlayerActivity.KEY_GAME_ID, play.gameId);
intent.putExtra(LogPlayerActivity.KEY_GAME_NAME, play.gameName);
intent.putExtra(LogPlayerActivity.KEY_IMAGE_URL, imageUrl);
intent.putExtra(LogPlayerActivity.KEY_END_PLAY, isRequestingToEndPlay);
intent.putExtra(LogPlayerActivity.KEY_FAB_COLOR, fabColor);
if (!arePlayersCustomSorted && requestCode == REQUEST_ADD_PLAYER) {
intent.putExtra(LogPlayerActivity.KEY_AUTO_POSITION, play.getPlayerCount() + 1);
}
List<String> colors = new ArrayList<>();
for (Player player : play.getPlayers()) {
colors.add(player.color);
}
intent.putExtra(LogPlayerActivity.KEY_USED_COLORS, colors.toArray(new String[colors.size()]));
startActivityForResult(intent, requestCode);
}
@DebugLog
private void maybeShowNotification() {
if (play != null && play.hasStarted() && internalId != BggContract.INVALID_ID) {
NotificationUtils.launchPlayingNotification(this, internalId, play, thumbnailUrl, imageUrl);
}
}
@DebugLog
private void cancelNotification() {
NotificationUtils.cancel(LogPlayActivity.this, NotificationUtils.TAG_PLAY_TIMER, internalId);
}
public class PlayAdapter extends RecyclerView.Adapter<PlayAdapter.PlayViewHolder> {
private final LayoutInflater inflater;
private final List<Integer> headerResources = new ArrayList<>();
private final List<Integer> footerResources = new ArrayList<>();
public PlayAdapter(Context context) {
setHasStableIds(false);
inflater = LayoutInflater.from(context);
buildLayoutMap();
}
@Override
public int getItemCount() {
return play == null ? 1 : headerResources.size() + play.getPlayerCount() + footerResources.size();
}
@Override
public int getItemViewType(int position) {
if (position < headerResources.size()) {
return headerResources.get(position);
} else if (position >= headerResources.size() + play.getPlayerCount()) {
return footerResources.get(position - headerResources.size() - play.getPlayerCount());
}
return R.layout.row_player;
}
@Override
public PlayViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case R.layout.row_log_play_header:
return new HeaderViewHolder(parent);
case R.layout.row_log_play_date:
return new DateViewHolder(parent);
case R.layout.row_log_play_location:
return new LocationViewHolder(parent);
case R.layout.row_log_play_length:
return new LengthViewHolder(parent);
case R.layout.row_log_play_quantity:
return new QuantityViewHolder(parent);
case R.layout.row_log_play_incomplete:
return new IncompleteViewHolder(parent);
case R.layout.row_log_play_no_win_stats:
return new NoWinStatsViewHolder(parent);
case R.layout.row_log_play_comments:
return new CommentsViewHolder(parent);
case R.layout.row_log_play_player_header:
return new PlayerHeaderViewHolder(parent);
case R.layout.row_player:
return new PlayerViewHolder();
case R.layout.row_log_play_add_player:
return new AddPlayerViewHolder(parent);
}
return null;
}
@Override
public void onBindViewHolder(PlayViewHolder holder, int position) {
if (holder.getItemViewType() == R.layout.row_player) {
((PlayerViewHolder) holder).bind(position - headerResources.size());
} else {
holder.bind();
}
}
public int getPlayerPosition(int adapterPosition) {
return adapterPosition - headerResources.size();
}
@Nullable
public Player getPlayer(int position) {
return (play == null || position < 0 || play.getPlayerCount() <= position) ?
null :
play.getPlayers().get(position);
}
public void refresh() {
buildLayoutMap();
notifyDataSetChanged();
}
public void notifyPlayersChanged() {
new Handler().post(
new Runnable() {
@Override
public void run() {
notifyLayoutChanged(R.layout.row_log_play_player_header);
notifyItemRangeChanged(headerResources.size(), play.getPlayerCount());
}
});
maybeShowNotification();
}
public void notifyPlayerChanged(final int playerPosition) {
new Handler().post(
new Runnable() {
@Override
public void run() {
notifyItemChanged(headerResources.size() + playerPosition);
}
});
}
public void notifyPlayerAdded(final int playerPosition) {
new Handler().post(
new Runnable() {
@Override
public void run() {
notifyLayoutChanged(R.layout.row_log_play_player_header);
final int position = headerResources.size() + playerPosition;
notifyItemInserted(position);
notifyItemRangeChanged(position + 1, getItemCount() - position);
}
});
maybeShowNotification();
}
public void notifyPlayerRemoved(final int playerPosition) {
new Handler().post(
new Runnable() {
@Override
public void run() {
notifyLayoutChanged(R.layout.row_log_play_player_header);
final int position = headerResources.size() + playerPosition;
notifyItemRemoved(position);
notifyItemRangeChanged(position, play.getPlayerCount() - playerPosition);
}
});
maybeShowNotification();
}
private void buildLayoutMap() {
headerResources.clear();
headerResources.add(R.layout.row_log_play_header);
headerResources.add(R.layout.row_log_play_date);
if (!shouldHideLocation()) headerResources.add(R.layout.row_log_play_location);
if (!shouldHideLength()) headerResources.add(R.layout.row_log_play_length);
if (!shouldHideQuantity()) headerResources.add(R.layout.row_log_play_quantity);
if (!shouldHideIncomplete()) headerResources.add(R.layout.row_log_play_incomplete);
if (!shouldHideNoWinStats()) headerResources.add(R.layout.row_log_play_no_win_stats);
if (!shouldHideComments()) headerResources.add(R.layout.row_log_play_comments);
if (!shouldHidePlayers()) headerResources.add(R.layout.row_log_play_player_header);
footerResources.clear();
if (!shouldHidePlayers()) footerResources.add(R.layout.row_log_play_add_player);
}
public void insertRow(@LayoutRes int layoutResId) {
final int position = findPositionOfNewItemType(layoutResId);
if (position != -1) {
new Handler().post(
new Runnable() {
@Override
public void run() {
notifyItemInserted(position);
}
});
recyclerView.smoothScrollToPosition(position);
// TODO focus field
}
}
private int findPositionOfNewItemType(@LayoutRes int layoutResId) {
if (!headerResources.contains(layoutResId) || footerResources.contains(layoutResId)) {
// call this because we know the field was just shown - this is confusing and needs to be refactored
buildLayoutMap();
for (int i = 0; i < headerResources.size(); i++) {
if (headerResources.get(i) == layoutResId) {
return i;
}
}
for (int i = 0; i < footerResources.size(); i++) {
if (footerResources.get(i) == layoutResId) {
return headerResources.size() + play.getPlayerCount() + i;
}
}
}
// it already exists, so it's not new
return -1;
}
private void notifyLayoutChanged(@LayoutRes int layoutResId) {
for (int i = 0; i < headerResources.size(); i++) {
if (headerResources.get(i) == layoutResId) {
final int position = i;
new Handler().post(
new Runnable() {
@Override
public void run() {
notifyItemChanged(position);
}
});
return;
}
}
for (int i = 0; i < footerResources.size(); i++) {
if (footerResources.get(i) == layoutResId) {
final int position = headerResources.size() + play.getPlayerCount() + i;
new Handler().post(
new Runnable() {
@Override
public void run() {
notifyItemChanged(position);
}
});
return;
}
}
}
abstract class PlayViewHolder extends RecyclerView.ViewHolder {
public PlayViewHolder(View itemView) {
super(itemView);
}
public abstract void bind();
}
public class HeaderViewHolder extends PlayViewHolder {
@BindView(R.id.header) TextView headerView;
@BindView(R.id.thumbnail) ImageView thumbnailView;
public HeaderViewHolder(ViewGroup parent) {
super(inflater.inflate(R.layout.row_log_play_header, parent, false));
ButterKnife.bind(this, itemView);
}
@Override
public void bind() {
headerView.setText(gameName);
fabColor = ContextCompat.getColor(LogPlayActivity.this, R.color.accent);
ImageUtils.safelyLoadImage(thumbnailView, imageUrl, new ImageUtils.Callback() {
@Override
public void onSuccessfulImageLoad(Palette palette) {
headerView.setBackgroundResource(R.color.black_overlay_light);
fabColor = PaletteUtils.getIconSwatch(palette).getRgb();
fab.setBackgroundTintList(ColorStateList.valueOf(fabColor));
fab.post(new Runnable() {
@Override
public void run() {
fab.show();
}
});
notifyLayoutChanged(R.layout.row_log_play_add_player);
}
@Override
public void onFailedImageLoad() {
fab.show();
}
});
}
}
public class DateViewHolder extends PlayViewHolder implements OnDateSetListener {
private static final String DATE_PICKER_DIALOG_TAG = "DATE_PICKER_DIALOG";
private DatePickerDialogFragment datePickerFragment;
@BindView(R.id.log_play_date) TextView dateButton;
public DateViewHolder(ViewGroup parent) {
super(inflater.inflate(R.layout.row_log_play_date, parent, false));
ButterKnife.bind(this, itemView);
datePickerFragment = (DatePickerDialogFragment) getSupportFragmentManager().findFragmentByTag(DATE_PICKER_DIALOG_TAG);
if (datePickerFragment != null) {
datePickerFragment.setOnDateSetListener(this);
}
}
@Override
public void bind() {
if (play == null) return;
dateButton.setText(play.getDateForDisplay(LogPlayActivity.this));
}
@OnClick(R.id.log_play_date)
public void onDateClick() {
final FragmentManager fragmentManager = getSupportFragmentManager();
datePickerFragment = (DatePickerDialogFragment) fragmentManager.findFragmentByTag(DATE_PICKER_DIALOG_TAG);
if (datePickerFragment == null) {
datePickerFragment = new DatePickerDialogFragment();
datePickerFragment.setOnDateSetListener(this);
}
fragmentManager.executePendingTransactions();
datePickerFragment.setCurrentDateInMillis(play.getDateInMillis());
datePickerFragment.show(fragmentManager, DATE_PICKER_DIALOG_TAG);
}
@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
if (play != null) {
play.setDate(year, month, dayOfMonth);
notifyLayoutChanged(R.layout.row_log_play_date);
refresh();
}
}
}
public class LocationViewHolder extends PlayViewHolder {
@BindView(R.id.log_play_location) AutoCompleteTextView locationView;
private boolean canEdit;
public LocationViewHolder(ViewGroup parent) {
super(inflater.inflate(R.layout.row_log_play_location, parent, false));
ButterKnife.bind(this, itemView);
locationView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void onTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
if (canEdit) {
play.location = s.toString().trim();
}
}
});
}
@Override
public void bind() {
if (locationView.getAdapter() == null) locationView.setAdapter(locationAdapter);
if (play != null) {
locationView.setTextKeepState(play.location);
canEdit = true;
}
}
}
public class LengthViewHolder extends PlayViewHolder {
@BindView(R.id.log_play_length) EditText lengthView;
@BindView(R.id.timer) Chronometer timerView;
@BindView(R.id.timer_toggle) ImageView timerToggleView;
private boolean canEdit;
public LengthViewHolder(ViewGroup parent) {
super(inflater.inflate(R.layout.row_log_play_length, parent, false));
ButterKnife.bind(this, itemView);
}
@Override
public void bind() {
if (play != null) {
lengthView.setTextKeepState((play.length == Play.LENGTH_DEFAULT) ? "" : String.valueOf(play.length));
UIUtils.startTimerWithSystemTime(timerView, play.startTime);
canEdit = true;
if (play.hasStarted()) {
lengthView.setVisibility(View.GONE);
timerView.setVisibility(View.VISIBLE);
} else {
lengthView.setVisibility(View.VISIBLE);
timerView.setVisibility(View.GONE);
}
if (play.hasStarted()) {
timerToggleView.setEnabled(true);
timerToggleView.setImageResource(R.drawable.ic_timer_off);
} else if (DateUtils.isToday(play.getDateInMillis() + play.length * 60 * 1000)) {
timerToggleView.setEnabled(true);
timerToggleView.setImageResource(R.drawable.ic_timer);
} else {
timerToggleView.setEnabled(false);
}
}
}
@OnFocusChange(R.id.log_play_length)
public void onLengthFocusChange(EditText v, boolean hasFocus) {
if (canEdit && !hasFocus) {
play.length = StringUtils.parseInt(v.getText().toString().trim());
}
}
@DebugLog
@OnClick(R.id.timer_toggle)
public void onTimer() {
if (play.hasStarted()) {
isRequestingToEndPlay = true;
Answers.getInstance().logCustom(new CustomEvent("LogPlayTimer").putCustomAttribute("State", "Off"));
play.end();
bind();
cancelNotification();
if (play.length > 0) {
UIUtils.finishingEditing(lengthView);
}
} else {
if (play.length == 0) {
startTimer();
} else {
DialogUtils.createConfirmationDialog(LogPlayActivity.this,
R.string.are_you_sure_timer_reset,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startTimer();
}
}).show();
}
}
}
@DebugLog
private void startTimer() {
Answers.getInstance().logCustom(new CustomEvent("LogPlayTimer").putCustomAttribute("State", "On"));
play.start();
bind();
maybeShowNotification();
}
}
public class QuantityViewHolder extends PlayViewHolder {
@BindView(R.id.log_play_quantity) EditText quantityView;
private boolean canEdit;
public QuantityViewHolder(ViewGroup parent) {
super(inflater.inflate(R.layout.row_log_play_quantity, parent, false));
ButterKnife.bind(this, itemView);
}
@Override
public void bind() {
if (play != null) {
quantityView.setTextKeepState((play.quantity == Play.QUANTITY_DEFAULT) ? "" : String.valueOf(play.quantity));
canEdit = true;
}
}
@OnFocusChange(R.id.log_play_quantity)
public void onQuantityFocusChange(EditText v, boolean hasFocus) {
if (canEdit && !hasFocus) {
play.quantity = StringUtils.parseInt(v.getText().toString().trim(), 1);
}
}
}
public class IncompleteViewHolder extends PlayViewHolder {
@BindView(R.id.log_play_incomplete) SwitchCompat incompleteView;
public IncompleteViewHolder(ViewGroup parent) {
super(inflater.inflate(R.layout.row_log_play_incomplete, parent, false));
ButterKnife.bind(this, itemView);
}
@Override
public void bind() {
if (play != null) {
incompleteView.setChecked(play.Incomplete());
}
}
@OnCheckedChanged(R.id.log_play_incomplete)
public void onIncompleteCheckedChanged() {
play.setIncomplete(incompleteView.isChecked());
}
}
public class NoWinStatsViewHolder extends PlayViewHolder {
@BindView(R.id.log_play_no_win_stats) SwitchCompat noWinStatsView;
public NoWinStatsViewHolder(ViewGroup parent) {
super(inflater.inflate(R.layout.row_log_play_no_win_stats, parent, false));
ButterKnife.bind(this, itemView);
}
@Override
public void bind() {
if (play != null) {
noWinStatsView.setChecked(play.NoWinStats());
}
}
@OnCheckedChanged(R.id.log_play_no_win_stats)
public void onIncompleteCheckedChanged() {
play.setNoWinStats(noWinStatsView.isChecked());
}
}
public class CommentsViewHolder extends PlayViewHolder {
@BindView(R.id.log_play_comments) EditText commentsView;
private boolean canEdit;
public CommentsViewHolder(ViewGroup parent) {
super(inflater.inflate(R.layout.row_log_play_comments, parent, false));
ButterKnife.bind(this, itemView);
}
@Override
public void bind() {
if (play != null) {
commentsView.setTextKeepState(play.comments);
canEdit = true;
}
}
@OnFocusChange(R.id.log_play_comments)
public void onCommentsFocusChange(EditText v, boolean hasFocus) {
if (canEdit && !hasFocus) {
play.comments = v.getText().toString().trim();
}
}
}
public class AddPlayerViewHolder extends PlayViewHolder {
@BindView(R.id.add_players_button) Button addPlayersButton;
public AddPlayerViewHolder(ViewGroup parent) {
super(inflater.inflate(R.layout.row_log_play_add_player, parent, false));
ButterKnife.bind(this, itemView);
}
@Override
public void bind() {
ViewCompat.setBackgroundTintList(addPlayersButton, ColorStateList.valueOf(fabColor));
}
@OnClick(R.id.add_players_button)
public void onAddPlayerClicked() {
if (PreferencesUtils.getEditPlayerPrompted(LogPlayActivity.this)) {
addPlayers(PreferencesUtils.getEditPlayer(LogPlayActivity.this));
} else {
promptToEditPlayers();
}
}
private void addPlayers(boolean editPlayer) {
if (editPlayer) {
if (!showPlayersToAddDialog()) {
addNewPlayer();
}
} else {
Player player = new Player();
if (!arePlayersCustomSorted) {
player.setSeat(play.getPlayerCount() + 1);
}
play.addPlayer(player);
playAdapter.notifyPlayerAdded(play.getPlayerCount());
recyclerView.smoothScrollToPosition(playAdapter.getItemCount());
}
}
private void promptToEditPlayers() {
new Builder(LogPlayActivity.this)
.setTitle(R.string.pref_edit_player_prompt_title)
.setMessage(R.string.pref_edit_player_prompt_message)
.setCancelable(true)
.setPositiveButton(R.string.pref_edit_player_prompt_positive, onPromptClickListener(true))
.setNegativeButton(R.string.pref_edit_player_prompt_negative, onPromptClickListener(false))
.create().show();
PreferencesUtils.putEditPlayerPrompted(LogPlayActivity.this);
}
@NonNull
private OnClickListener onPromptClickListener(final boolean value) {
return new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
PreferencesUtils.putEditPlayer(LogPlayActivity.this, value);
addPlayers(value);
}
};
}
}
public class PlayerHeaderViewHolder extends PlayViewHolder {
@BindView(R.id.assign_colors) View assignColorsButton;
@BindView(R.id.log_play_players_label) TextView playerLabelView;
private MenuBuilder fullSortMenu;
private MenuBuilder shortSortMenu;
public PlayerHeaderViewHolder(ViewGroup parent) {
super(inflater.inflate(R.layout.row_log_play_player_header, parent, false));
ButterKnife.bind(this, itemView);
}
@Override
public void bind() {
Resources r = getResources();
int playerCount = play.getPlayerCount();
if (playerCount <= 0) {
playerLabelView.setText(r.getString(R.string.title_players));
} else {
playerLabelView.setText(r.getString(R.string.title_players) + " - " + String.valueOf(playerCount));
}
assignColorsButton.setEnabled(play != null && play.getPlayerCount() > 0);
}
@OnClick(R.id.player_sort)
public void onPlayerSort(View view) {
MenuPopupHelper popup;
if (!arePlayersCustomSorted && play.getPlayerCount() > 1) {
if (fullSortMenu == null) {
fullSortMenu = new MenuBuilder(LogPlayActivity.this);
MenuItem mi = fullSortMenu.add(MenuBuilder.NONE, R.id.menu_custom_player_order, MenuBuilder.NONE, R.string.menu_custom_player_order);
mi.setCheckable(true);
fullSortMenu.add(MenuBuilder.NONE, R.id.menu_pick_start_player, 2, R.string.menu_pick_start_player);
fullSortMenu.add(MenuBuilder.NONE, R.id.menu_random_start_player, 3, R.string.menu_random_start_player);
fullSortMenu.add(MenuBuilder.NONE, R.id.menu_random_player_order, 4, R.string.menu_random_player_order);
fullSortMenu.setCallback(popupMenuCallback());
}
fullSortMenu.getItem(0).setChecked(arePlayersCustomSorted);
popup = new MenuPopupHelper(LogPlayActivity.this, fullSortMenu, view);
} else {
if (shortSortMenu == null) {
shortSortMenu = new MenuBuilder(LogPlayActivity.this);
MenuItem mi = shortSortMenu.add(MenuBuilder.NONE, R.id.menu_custom_player_order, MenuBuilder.NONE, R.string.menu_custom_player_order);
mi.setCheckable(true);
shortSortMenu.setCallback(popupMenuCallback());
}
shortSortMenu.getItem(0).setChecked(arePlayersCustomSorted);
popup = new MenuPopupHelper(LogPlayActivity.this, shortSortMenu, view);
}
popup.show();
}
@OnClick(R.id.assign_colors)
public void onAssignColors() {
if (play.hasColors()) {
Builder builder = new Builder(LogPlayActivity.this)
.setTitle(R.string.title_clear_colors)
.setMessage(R.string.msg_clear_colors)
.setCancelable(true)
.setNegativeButton(R.string.keep, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
TaskUtils.executeAsyncTask(new ColorAssignerTask(LogPlayActivity.this, play));
}
})
.setPositiveButton(R.string.clear, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
for (Player player : play.getPlayers()) {
player.color = "";
}
TaskUtils.executeAsyncTask(new ColorAssignerTask(LogPlayActivity.this, play));
}
});
builder.show();
} else {
TaskUtils.executeAsyncTask(new ColorAssignerTask(LogPlayActivity.this, play));
}
}
}
class PlayerViewHolder extends PlayViewHolder {
private final PlayerRow row;
private MenuBuilder moreMenu;
public PlayerViewHolder() {
super(new PlayerRow(LogPlayActivity.this));
row = (PlayerRow) itemView;
row.setLayoutParams(new RecyclerView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
}
@Override
public void bind() {
//no-op
}
public void bind(final int position) {
row.setAutoSort(!arePlayersCustomSorted);
row.setPlayer(getPlayer(position));
row.setNameListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
editPlayer(position);
}
});
row.setOnMoreListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final int groupId = MenuBuilder.FIRST;
final int newItemId = MenuBuilder.FIRST;
final int winItemId = MenuBuilder.FIRST + 1;
if (moreMenu == null) {
moreMenu = new MenuBuilder(LogPlayActivity.this);
moreMenu.add(groupId, newItemId, MenuBuilder.NONE, R.string.new_label);
moreMenu.add(groupId, winItemId, MenuBuilder.NONE, R.string.win);
moreMenu.setGroupCheckable(groupId, true, false);
moreMenu.setCallback(new Callback() {
@Override
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
if (play.getPlayers() == null || play.getPlayers().size() <= position) {
Timber.w("Unable to set new/win on selected player");
return false;
}
final Player player = play.getPlayers().get(position);
switch (item.getItemId()) {
case newItemId:
player.New(!item.isChecked());
bind(position);
return true;
case winItemId:
player.Win(!item.isChecked());
bind(position);
return true;
}
return false;
}
@Override
public void onMenuModeChange(MenuBuilder menu) {
}
});
}
final Player player = play.getPlayers().get(position);
moreMenu.findItem(newItemId).setChecked(player.New());
moreMenu.findItem(winItemId).setChecked(player.Win());
MenuPopupHelper popup = new MenuPopupHelper(LogPlayActivity.this, moreMenu, row.getMoreButton());
popup.show();
}
});
row.getDragHandle().setOnTouchListener(
new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
itemTouchHelper.startDrag(PlayerViewHolder.this);
}
return false;
}
});
row.setOnColorListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final Player player = play.getPlayers().get(position);
final ArrayList<String> usedColors = new ArrayList<>();
for (Player p : play.getPlayers()) {
if (p != player) usedColors.add(p.color);
}
ColorPickerDialogFragment fragment = ColorPickerDialogFragment.newInstance(0,
ColorUtils.getColorList(), gameColors, player.color, usedColors, null, 4);
fragment.setOnColorSelectedListener(new ColorPickerDialogFragment.OnColorSelectedListener() {
@Override
public void onColorSelected(String description, int color) {
player.color = description;
playAdapter.notifyPlayerChanged(position);
}
});
fragment.show(getSupportFragmentManager(), "color_picker");
}
});
row.setOnScoreListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
final Player player = play.getPlayers().get(position);
final NumberPadDialogFragment fragment = NumberPadDialogFragment.newInstance(player.getDescription(), player.score, player.color);
fragment.setOnDoneClickListener(new NumberPadDialogFragment.OnClickListener() {
@Override
public void onDoneClick(String output) {
player.score = output;
double highScore = play.getHighScore();
for (Player p : play.getPlayers()) {
double score = StringUtils.parseDouble(p.score, Double.MIN_VALUE);
p.Win(score == highScore);
}
playAdapter.notifyPlayersChanged();
}
});
DialogUtils.showFragment(LogPlayActivity.this, fragment, "score_dialog");
}
}
);
}
}
}
}