package com.boardgamegeek.ui;
import android.content.ContentValues;
import android.content.Intent;
import android.content.res.ColorStateList;
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.AsyncTask;
import android.os.Bundle;
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.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.text.TextUtils;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import com.boardgamegeek.R;
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.ui.adapter.GameColorRecyclerViewAdapter;
import com.boardgamegeek.ui.adapter.GameColorRecyclerViewAdapter.Callback;
import com.boardgamegeek.ui.dialog.EditTextDialogFragment;
import com.boardgamegeek.ui.dialog.EditTextDialogFragment.EditTextDialogListener;
import com.boardgamegeek.util.ActivityUtils;
import com.boardgamegeek.util.AnimationUtils;
import com.boardgamegeek.util.DialogUtils;
import com.boardgamegeek.util.TaskUtils;
import com.boardgamegeek.util.UIUtils;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindDimen;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
import hugo.weaving.DebugLog;
import timber.log.Timber;
public class ColorsFragment extends Fragment implements LoaderCallbacks<Cursor> {
private static final int TOKEN = 0x20;
private int gameId;
private GameColorRecyclerViewAdapter adapter;
private EditTextDialogFragment editTextDialogFragment;
private ActionMode actionMode;
private Unbinder unbinder;
@BindView(R.id.root_container) CoordinatorLayout containerView;
@BindView(android.R.id.progress) View progressView;
@BindView(android.R.id.empty) View emptyView;
@BindView(android.R.id.list) RecyclerView recyclerView;
@BindView(R.id.fab) FloatingActionButton fab;
private final Paint swipePaint = new Paint();
private Bitmap deleteIcon;
@BindDimen(R.dimen.material_margin_horizontal) float horizontalPadding;
private int iconColor;
@DebugLog
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Nullable
@DebugLog
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_colors, container, false);
unbinder = ButterKnife.bind(this, rootView);
colorFab();
setUpRecyclerView();
return rootView;
}
private void setUpRecyclerView() {
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
swipePaint.setColor(ContextCompat.getColor(getContext(), R.color.medium_blue));
deleteIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_delete_white);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
return false;
}
@Override
public void onSwiped(ViewHolder viewHolder, int swipeDir) {
final String color = adapter.getColorName(viewHolder.getAdapterPosition());
int count = getActivity().getContentResolver().delete(Games.buildColorsUri(gameId, color), null, null);
if (count > 0) {
Snackbar.make(containerView, getString(R.string.msg_color_deleted, color), Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.undo, new OnClickListener() {
@Override
public void onClick(View v) {
addColor(color);
}
})
.show();
}
}
@Override
public int getSwipeDirs(RecyclerView recyclerView, ViewHolder viewHolder) {
if (actionMode != null) {
return 0;
}
return super.getSwipeDirs(recyclerView, viewHolder);
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
View itemView = viewHolder.itemView;
float verticalPadding = (itemView.getHeight() - deleteIcon.getHeight()) / 2;
RectF background;
Rect iconSrc;
RectF iconDst;
if (dX > 0) {
background = new RectF((float) itemView.getLeft(), (float) itemView.getTop(), dX, (float) itemView.getBottom());
iconSrc = new Rect(0, 0, (int) (dX - itemView.getLeft() - horizontalPadding), deleteIcon.getHeight());
iconDst = new RectF((float) itemView.getLeft() + horizontalPadding, (float) itemView.getTop() + verticalPadding, Math.min(itemView.getLeft() + horizontalPadding + deleteIcon.getWidth(), dX), (float) itemView.getBottom() - verticalPadding);
} else {
background = new RectF((float) itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom());
iconSrc = new Rect(Math.max(deleteIcon.getWidth() + (int) horizontalPadding + (int) dX, 0), 0, deleteIcon.getWidth(), deleteIcon.getHeight());
iconDst = new RectF(Math.max((float) itemView.getRight() + dX, (float) itemView.getRight() - horizontalPadding - deleteIcon.getWidth()), (float) itemView.getTop() + verticalPadding, (float) itemView.getRight() - horizontalPadding, (float) itemView.getBottom() - verticalPadding);
}
c.drawRect(background, swipePaint);
c.drawBitmap(deleteIcon, iconSrc, iconDst, swipePaint);
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
});
itemTouchHelper.attachToRecyclerView(recyclerView);
}
@Override
public void onDestroyView() {
unbinder.unbind();
super.onDestroyView();
}
@DebugLog
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final Intent intent = UIUtils.fragmentArgumentsToIntent(getArguments());
gameId = Games.getGameId(intent.getData());
iconColor = intent.getIntExtra(ActivityUtils.KEY_ICON_COLOR, 0);
colorFab();
getLoaderManager().restartLoader(TOKEN, getArguments(), this);
}
@DebugLog
@Override
public void onCreateOptionsMenu(Menu menu, @NonNull MenuInflater inflater) {
inflater.inflate(R.menu.game_colors, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@DebugLog
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_colors_generate:
TaskUtils.executeAsyncTask(new Task());
return true;
}
return super.onOptionsItemSelected(item);
}
@Nullable
@DebugLog
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle data) {
return new CursorLoader(getActivity(), Games.buildColorsUri(gameId), GameColorRecyclerViewAdapter.PROJECTION, null, null, null);
}
@DebugLog
@Override
public void onLoadFinished(@NonNull Loader<Cursor> loader, @NonNull Cursor cursor) {
if (getActivity() == null) {
return;
}
if (adapter == null) {
adapter = new GameColorRecyclerViewAdapter(cursor, R.layout.row_color, new Callback() {
@Override
public void onItemClick(int position) {
if (actionMode != null) {
toggleSelection(position);
}
}
@Override
public boolean onItemLongPress(int position) {
if (actionMode != null) {
return false;
}
actionMode = getActivity().startActionMode(new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.colors_context, menu);
fab.hide();
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_delete:
List<Integer> selectedItemPositions = adapter.getSelectedItems();
int count = 0;
for (int i = selectedItemPositions.size() - 1; i >= 0; i--) {
int position = selectedItemPositions.get(i);
String color = adapter.getColorName(position);
count += getActivity().getContentResolver().delete(Games.buildColorsUri(gameId, color), null, null);
}
Snackbar.make(containerView, getResources().getQuantityString(R.plurals.msg_colors_deleted, count, count), Snackbar.LENGTH_SHORT).show();
mode.finish();
return true;
}
mode.finish();
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
actionMode = null;
adapter.clearSelections();
fab.show();
}
});
toggleSelection(position);
return true;
}
private void toggleSelection(int position) {
adapter.toggleSelection(position);
int count = adapter.getSelectedItemCount();
if (count == 0) {
actionMode.finish();
} else {
actionMode.setTitle(getResources().getQuantityString(R.plurals.msg_colors_selected, count, count));
}
}
});
recyclerView.setAdapter(adapter);
}
int token = loader.getId();
if (token == TOKEN) {
if (cursor.getCount() == 0) {
AnimationUtils.fadeIn(emptyView);
} else {
AnimationUtils.fadeOut(emptyView);
}
adapter.changeCursor(cursor);
} else {
Timber.w("Query complete, Not Actionable: %s", token);
cursor.close();
}
AnimationUtils.fadeIn(getActivity(), recyclerView, isResumed());
fab.show();
AnimationUtils.fadeOut(progressView);
}
@DebugLog
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
if (adapter != null) {
adapter.changeCursor(null);
}
}
@OnClick(R.id.fab)
public void onFabClicked() {
if (editTextDialogFragment == null) {
editTextDialogFragment = EditTextDialogFragment.newInstance(R.string.title_add_color, null, new EditTextDialogListener() {
@Override
public void onFinishEditDialog(String inputText) {
if (!TextUtils.isEmpty(inputText)) {
addColor(inputText);
}
}
});
}
DialogUtils.showFragment(getActivity(), editTextDialogFragment, "edit_color");
}
@DebugLog
private void colorFab() {
if (fab != null && iconColor != 0) {
fab.setBackgroundTintList(ColorStateList.valueOf(iconColor));
}
}
@DebugLog
private void addColor(String color) {
ContentValues values = new ContentValues();
values.put(GameColors.COLOR, color);
getActivity().getContentResolver().insert(Games.buildColorsUri(gameId), values);
}
private class Task extends AsyncTask<Void, Void, Integer> {
@DebugLog
@Override
protected Integer doInBackground(Void... params) {
Integer count = 0;
Cursor cursor = null;
try {
cursor = getActivity().getContentResolver().query(Plays.buildPlayersByColor(),
new String[] { PlayPlayers.COLOR }, Plays.OBJECT_ID + "=?",
new String[] { String.valueOf(gameId) }, null);
if (cursor != null && cursor.moveToFirst()) {
List<ContentValues> values = new ArrayList<>();
do {
String color = cursor.getString(0);
if (!TextUtils.isEmpty(color)) {
ContentValues cv = new ContentValues();
cv.put(GameColors.COLOR, color);
values.add(cv);
}
} while (cursor.moveToNext());
if (values.size() > 0) {
ContentValues[] array = {};
count = getActivity().getContentResolver().bulkInsert(Games.buildColorsUri(gameId), values.toArray(array));
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return count;
}
@DebugLog
@Override
protected void onPostExecute(Integer result) {
if (result > 0) {
Snackbar.make(containerView, R.string.msg_colors_generated, Snackbar.LENGTH_SHORT).show();
}
}
}
}