package com.boardgamegeek.ui; import android.content.Intent; import android.content.res.Resources; import android.database.Cursor; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v7.graphics.Palette; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.boardgamegeek.R; import com.boardgamegeek.events.CollectionItemChangedEvent; import com.boardgamegeek.events.CollectionItemUpdatedEvent; import com.boardgamegeek.provider.BggContract; import com.boardgamegeek.provider.BggContract.Collection; import com.boardgamegeek.service.SyncService; import com.boardgamegeek.service.UpdateService; import com.boardgamegeek.tasks.UpdateCollectionItemPrivateInfoTask; import com.boardgamegeek.tasks.UpdateCollectionItemRatingTask; import com.boardgamegeek.tasks.UpdateCollectionItemStatusTask; import com.boardgamegeek.tasks.UpdateCollectionItemTextTask; import com.boardgamegeek.ui.dialog.CollectionStatusDialogFragment; import com.boardgamegeek.ui.dialog.CollectionStatusDialogFragment.CollectionStatusDialogListener; import com.boardgamegeek.ui.dialog.EditTextDialogFragment; import com.boardgamegeek.ui.dialog.EditTextDialogFragment.EditTextDialogListener; import com.boardgamegeek.ui.dialog.NumberPadDialogFragment; import com.boardgamegeek.ui.dialog.PrivateInfoDialogFragment; import com.boardgamegeek.ui.dialog.PrivateInfoDialogFragment.PrivateInfoDialogListener; import com.boardgamegeek.ui.model.PrivateInfo; import com.boardgamegeek.ui.widget.TextEditorCard; import com.boardgamegeek.ui.widget.TimestampView; import com.boardgamegeek.util.ActivityUtils; import com.boardgamegeek.util.ColorUtils; import com.boardgamegeek.util.DateTimeUtils; import com.boardgamegeek.util.DialogUtils; import com.boardgamegeek.util.MathUtils; import com.boardgamegeek.util.PaletteUtils; import com.boardgamegeek.util.PresentationUtils; import com.boardgamegeek.util.StringUtils; import com.boardgamegeek.util.TaskUtils; import com.boardgamegeek.util.UIUtils; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; import butterknife.BindView; import butterknife.BindViews; import butterknife.ButterKnife; import butterknife.OnClick; import butterknife.Unbinder; import hugo.weaving.DebugLog; import icepick.Icepick; import icepick.State; import timber.log.Timber; public class GameCollectionFragment extends Fragment implements LoaderCallbacks<Cursor> { private static final int AGE_IN_DAYS_TO_REFRESH = 7; private static final DecimalFormat RATING_EDIT_FORMAT = new DecimalFormat("0.#"); private Unbinder unbinder; @BindView(R.id.year) TextView year; @BindView(R.id.info_bar) View infoBar; @BindView(R.id.status_container) ViewGroup statusContainer; @BindView(R.id.status) TextView statusView; @BindView(R.id.last_modified) TimestampView lastModified; @BindView(R.id.rating_container) View ratingContainer; @BindView(R.id.rating) TextView rating; @BindView(R.id.rating_timestamp) TimestampView ratingTimestampView; @BindView(R.id.comment_container) ViewGroup commentContainer; @BindView(R.id.add_comment) View addCommentView; @BindView(R.id.comment) TextView comment; @BindView(R.id.comment_timestamp) TimestampView commentTimestampView; @BindView(R.id.private_info_container) ViewGroup privateInfoContainer; @BindView(R.id.private_info) TextView privateInfo; @BindView(R.id.private_info_comments) TextView privateInfoComments; @BindView(R.id.private_info_timestamp) TimestampView privateInfoTimestampView; @BindView(R.id.wishlist_card) TextEditorCard wishlistCard; @BindView(R.id.condition_card) TextEditorCard conditionCard; @BindView(R.id.want_parts_card) TextEditorCard wantPartsCard; @BindView(R.id.has_parts_card) TextEditorCard hasPartsCard; @BindView(R.id.collection_id) TextView id; @BindView(R.id.updated) TimestampView updated; @BindViews({ R.id.status, R.id.last_modified, R.id.year }) List<TextView> colorizedTextViews; @BindViews({ R.id.add_comment, R.id.card_header_private_info, }) List<TextView> colorizedHeaders; @BindViews({ R.id.wishlist_card, R.id.condition_card, R.id.want_parts_card, R.id.has_parts_card }) List<TextEditorCard> textEditorCards; private CollectionStatusDialogFragment statusDialogFragment; private EditTextDialogFragment commentDialogFragment; private PrivateInfoDialogFragment privateInfoDialogFragment; private int gameId = BggContract.INVALID_ID; private int collectionId = BggContract.INVALID_ID; private long internalId = 0; private boolean mightNeedRefreshing; private Palette palette; private boolean needsUploading; @State boolean isItemEditable; @DebugLog @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = UIUtils.fragmentArgumentsToIntent(getArguments()); gameId = intent.getIntExtra(ActivityUtils.KEY_GAME_ID, BggContract.INVALID_ID); collectionId = intent.getIntExtra(ActivityUtils.KEY_COLLECTION_ID, BggContract.INVALID_ID); Icepick.restoreInstanceState(this, savedInstanceState); } @DebugLog @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_game_collection, container, false); unbinder = ButterKnife.bind(this, rootView); colorize(palette); mightNeedRefreshing = true; getLoaderManager().restartLoader(CollectionItem._TOKEN, getArguments(), this); return rootView; } @DebugLog @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @DebugLog @Override public void onStop() { EventBus.getDefault().unregister(this); super.onStop(); } @DebugLog @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); Icepick.saveInstanceState(this, outState); } @DebugLog @Override public void onDestroyView() { super.onDestroyView(); unbinder.unbind(); } @DebugLog @Override public Loader<Cursor> onCreateLoader(int id, Bundle data) { if (id != CollectionItem._TOKEN || collectionId == BggContract.INVALID_ID) { return null; } if (collectionId != 0) { return new CursorLoader(getActivity(), Collection.CONTENT_URI, new CollectionItem().PROJECTION, Collection.COLLECTION_ID + "=?", new String[] { String.valueOf(collectionId) }, null); } else { return new CursorLoader(getActivity(), Collection.CONTENT_URI, new CollectionItem().PROJECTION, "collection." + Collection.GAME_ID + "=? AND " + Collection.COLLECTION_ID + " IS NULL", new String[] { String.valueOf(gameId) }, null); } } @DebugLog @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { if (getActivity() == null) { return; } if (loader.getId() == CollectionItem._TOKEN) { if (cursor == null || !cursor.moveToFirst()) { if (mightNeedRefreshing) { triggerRefresh(); } return; } CollectionItem item = new CollectionItem(cursor); internalId = item.internalId; updateUi(item); if (mightNeedRefreshing) { long u = cursor.getLong(new CollectionItem().UPDATED); if (DateTimeUtils.howManyDaysOld(u) > AGE_IN_DAYS_TO_REFRESH) { triggerRefresh(); } } mightNeedRefreshing = false; } else { Timber.d("Query complete, Not Actionable: %s", loader.getId()); if (cursor != null) { cursor.close(); } } } @DebugLog @Override public void onLoaderReset(Loader<Cursor> loader) { } public void enableEditMode(boolean enable) { boolean clickable = enable && isItemEditable; statusContainer.setClickable(clickable); commentContainer.setClickable(clickable); ratingContainer.setClickable(clickable); privateInfoContainer.setClickable(clickable); wishlistCard.enableEditMode(clickable); conditionCard.enableEditMode(clickable); wantPartsCard.enableEditMode(clickable); hasPartsCard.enableEditMode(clickable); if (!enable && needsUploading) { SyncService.sync(getActivity(), SyncService.FLAG_SYNC_COLLECTION_UPLOAD); needsUploading = false; } } @SuppressWarnings({ "unused", "UnusedParameters" }) @DebugLog @Subscribe public void onEvent(CollectionItemUpdatedEvent event) { needsUploading = true; } @DebugLog public void onPaletteGenerated(Palette palette) { this.palette = palette; colorize(palette); } @DebugLog private void colorize(Palette palette) { if (palette == null || !isAdded()) { return; } @SuppressWarnings("deprecation") Palette.Swatch swatch = PaletteUtils.getInverseSwatch(palette, getResources().getColor(R.color.info_background)); infoBar.setBackgroundColor(swatch.getRgb()); ButterKnife.apply(colorizedTextViews, PaletteUtils.colorTextViewOnBackgroundSetter, swatch); swatch = PaletteUtils.getHeaderSwatch(palette); ButterKnife.apply(colorizedHeaders, PaletteUtils.colorTextViewSetter, swatch); ButterKnife.apply(textEditorCards, TextEditorCard.headerColorSetter, swatch); } @OnClick(R.id.status_container) public void onStatusClick() { ensureCollectionStatusDialogFragment(); //noinspection unchecked statusDialogFragment.setSelectedStatuses((List<String>) statusView.getTag(R.id.status)); statusDialogFragment.setWishlistPriority((int) statusView.getTag(R.id.wishlist_priority)); DialogUtils.showFragment(getActivity(), statusDialogFragment, "status_dialog"); } @DebugLog private void ensureCollectionStatusDialogFragment() { if (statusDialogFragment == null) { statusDialogFragment = CollectionStatusDialogFragment.newInstance( statusContainer, new CollectionStatusDialogListener() { @Override public void onSelectStatuses(List<String> selectedStatuses, int wishlistPriority) { UpdateCollectionItemStatusTask task = new UpdateCollectionItemStatusTask(getActivity(), gameId, collectionId, internalId, selectedStatuses, wishlistPriority); TaskUtils.executeAsyncTask(task); } } ); } } @DebugLog @OnClick(R.id.wishlist_card) public void onWishlistCommentClick() { onTextEditorClick(wishlistCard, Collection.WISHLIST_COMMENT, Collection.WISHLIST_COMMENT_DIRTY_TIMESTAMP); } @DebugLog @OnClick(R.id.condition_card) public void onConditionClick() { onTextEditorClick(conditionCard, Collection.CONDITION, Collection.TRADE_CONDITION_DIRTY_TIMESTAMP); } @DebugLog @OnClick(R.id.want_parts_card) public void onWantPartsClick() { onTextEditorClick(wantPartsCard, Collection.WANTPARTS_LIST, Collection.WANT_PARTS_DIRTY_TIMESTAMP); } @DebugLog @OnClick(R.id.has_parts_card) public void onHasPartsClick() { onTextEditorClick(hasPartsCard, Collection.HASPARTS_LIST, Collection.HAS_PARTS_DIRTY_TIMESTAMP); } @DebugLog private void onTextEditorClick(TextEditorCard card, final String textColumn, final String timestampColumn) { EditTextDialogFragment dialogFragment = EditTextDialogFragment.newLongFormInstance( card.getHeaderText(), card, new EditTextDialogListener() { @Override public void onFinishEditDialog(String inputText) { UpdateCollectionItemTextTask task = new UpdateCollectionItemTextTask(getActivity(), gameId, collectionId, internalId, inputText, textColumn, timestampColumn); TaskUtils.executeAsyncTask(task); } } ); dialogFragment.setText(card.getContentText()); DialogUtils.showFragment(getActivity(), dialogFragment, card.toString()); } @DebugLog @OnClick(R.id.comment_container) public void onCommentClick() { ensureCommentDialogFragment(); commentDialogFragment.setText(comment.getText().toString()); DialogUtils.showFragment(getActivity(), commentDialogFragment, "comment_dialog"); } @DebugLog private void ensureCommentDialogFragment() { if (commentDialogFragment == null) { commentDialogFragment = EditTextDialogFragment.newLongFormInstance( R.string.title_comments, commentContainer, new EditTextDialogListener() { @Override public void onFinishEditDialog(String inputText) { UpdateCollectionItemTextTask task = new UpdateCollectionItemTextTask(getActivity(), gameId, collectionId, internalId, inputText, Collection.COMMENT, Collection.COMMENT_DIRTY_TIMESTAMP); TaskUtils.executeAsyncTask(task); } } ); } } @DebugLog @OnClick(R.id.rating_container) public void onRatingClick() { String output = RATING_EDIT_FORMAT.format((double) rating.getTag()); if ("0".equals(output)) { output = ""; } final NumberPadDialogFragment fragment = NumberPadDialogFragment.newInstance(getString(R.string.rating), output); fragment.setMinValue(1.0); fragment.setMaxValue(10.0); fragment.setMaxMantissa(6); fragment.setOnDoneClickListener(new NumberPadDialogFragment.OnClickListener() { @Override public void onDoneClick(String output) { double rating = StringUtils.parseDouble(output); UpdateCollectionItemRatingTask task = new UpdateCollectionItemRatingTask(getActivity(), gameId, collectionId, internalId, rating); TaskUtils.executeAsyncTask(task); } }); DialogUtils.showFragment(getActivity(), fragment, "rating_dialog"); } @DebugLog @OnClick(R.id.private_info_container) public void onPrivateInfoClick() { ensurePrivateInfoDialogFragment(); privateInfoDialogFragment.setPriceCurrency(String.valueOf(privateInfo.getTag(R.id.price_currency))); privateInfoDialogFragment.setPrice(getDoubleFromTag(privateInfo, R.id.price)); privateInfoDialogFragment.setCurrentValueCurrency(String.valueOf(privateInfo.getTag(R.id.current_value_currency))); privateInfoDialogFragment.setCurrentValue(getDoubleFromTag(privateInfo, R.id.current_value)); privateInfoDialogFragment.setQuantity(getIntFromTag(privateInfo, R.id.quantity)); privateInfoDialogFragment.setAcquisitionDate(String.valueOf(privateInfo.getTag(R.id.acquisition_date))); privateInfoDialogFragment.setAcquiredFrom(String.valueOf(privateInfo.getTag(R.id.acquired_from))); privateInfoDialogFragment.setComment(privateInfoComments.getText().toString()); DialogUtils.showFragment(getActivity(), privateInfoDialogFragment, "private_info_dialog"); } private double getDoubleFromTag(View textView, int key) { final Object tag = textView.getTag(key); if (tag == null) return 0.0; return (double) tag; } private int getIntFromTag(View textView, int key) { final Object tag = textView.getTag(key); if (tag == null) return 1; return (int) tag; } @DebugLog private void ensurePrivateInfoDialogFragment() { if (privateInfoDialogFragment == null) { privateInfoDialogFragment = PrivateInfoDialogFragment.newInstance( privateInfoContainer, new PrivateInfoDialogListener() { @Override public void onFinishEditDialog(PrivateInfo privateInfo) { UpdateCollectionItemPrivateInfoTask task = new UpdateCollectionItemPrivateInfoTask(getActivity(), gameId, collectionId, internalId, privateInfo); TaskUtils.executeAsyncTask(task); } } ); } } @DebugLog public void triggerRefresh() { mightNeedRefreshing = false; if (gameId != BggContract.INVALID_ID) { UpdateService.start(getActivity(), UpdateService.SYNC_TYPE_GAME_COLLECTION, gameId); } } @DebugLog private void notifyChange(CollectionItem item) { CollectionItemChangedEvent event = new CollectionItemChangedEvent(item.name, item.imageUrl, item.imageUrl); EventBus.getDefault().post(event); } @DebugLog private void updateUi(CollectionItem item) { notifyChange(item); isItemEditable = true; year.setText(item.getYearDescription()); lastModified.setTimestamp(item.dirtyTimestamp > 0 ? item.dirtyTimestamp : item.statusTimestamp > 0 ? item.statusTimestamp : item.lastModifiedDateTime); rating.setText(item.getRatingDescription()); rating.setTag(MathUtils.constrain(item.rating, 0.0, 10.0)); ColorUtils.setViewBackground(rating, ColorUtils.getRatingColor(item.rating)); ratingTimestampView.setTimestamp(item.ratingTimestamp); statusView.setText(item.getStatusDescription()); statusView.setTag(R.id.status, item.getStatuses()); statusView.setTag(R.id.wishlist_priority, item.getWishlistPriority()); addCommentView.setVisibility(TextUtils.isEmpty(item.comment) ? View.VISIBLE : View.GONE); PresentationUtils.setTextOrHide(comment, item.comment); commentTimestampView.setTimestamp(item.commentTimestamp); privateInfo.setVisibility(item.hasPrivateInfo() ? View.VISIBLE : View.GONE); privateInfo.setText(item.getPrivateInfo()); privateInfo.setTag(R.id.price_currency, item.getPriceCurrency()); privateInfo.setTag(R.id.price, item.getPrice()); privateInfo.setTag(R.id.current_value_currency, item.getCurrentValueCurrency()); privateInfo.setTag(R.id.current_value, item.getCurrentValue()); privateInfo.setTag(R.id.quantity, item.getQuantity()); privateInfo.setTag(R.id.acquisition_date, item.getAcquisitionDate()); privateInfo.setTag(R.id.acquired_from, item.getAcquiredFrom()); PresentationUtils.setTextOrHide(privateInfoComments, item.getPrivateComment()); privateInfoTimestampView.setTimestamp(item.privateInfoTimestamp); wishlistCard.setContentText(item.wishlistComment); wishlistCard.setTimestamp(item.wishlistCommentDirtyTimestamp); conditionCard.setContentText(item.condition); conditionCard.setTimestamp(item.tradeConditionDirtyTimestamp); wantPartsCard.setContentText(item.wantParts); wantPartsCard.setTimestamp(item.wantPartsDirtyTimestamp); hasPartsCard.setContentText(item.hasParts); hasPartsCard.setTimestamp(item.hasPartsDirtyTimestamp); id.setText(String.valueOf(item.id)); id.setVisibility(item.id == 0 ? View.INVISIBLE : View.VISIBLE); updated.setTimestamp(item.updated); } private class CollectionItem { static final int _TOKEN = 0x31; final String[] PROJECTION = { Collection._ID, Collection.COLLECTION_ID, Collection.COLLECTION_NAME, Collection.COLLECTION_SORT_NAME, Collection.COMMENT, Collection.PRIVATE_INFO_PRICE_PAID_CURRENCY, Collection.PRIVATE_INFO_PRICE_PAID, Collection.PRIVATE_INFO_CURRENT_VALUE_CURRENCY, Collection.PRIVATE_INFO_CURRENT_VALUE, Collection.PRIVATE_INFO_QUANTITY, Collection.PRIVATE_INFO_ACQUISITION_DATE, Collection.PRIVATE_INFO_ACQUIRED_FROM, Collection.PRIVATE_INFO_COMMENT, Collection.LAST_MODIFIED, Collection.COLLECTION_THUMBNAIL_URL, Collection.COLLECTION_IMAGE_URL, Collection.COLLECTION_YEAR_PUBLISHED, Collection.CONDITION, Collection.HASPARTS_LIST, Collection.WANTPARTS_LIST, Collection.WISHLIST_COMMENT, Collection.RATING, Collection.UPDATED, Collection.STATUS_OWN, Collection.STATUS_PREVIOUSLY_OWNED, Collection.STATUS_FOR_TRADE, Collection.STATUS_WANT, Collection.STATUS_WANT_TO_BUY, Collection.STATUS_WISHLIST, Collection.STATUS_WANT_TO_PLAY, Collection.STATUS_PREORDERED, Collection.STATUS_WISHLIST_PRIORITY, Collection.NUM_PLAYS, Collection.RATING_DIRTY_TIMESTAMP, Collection.COMMENT_DIRTY_TIMESTAMP, Collection.PRIVATE_INFO_DIRTY_TIMESTAMP, Collection.STATUS_DIRTY_TIMESTAMP, Collection.COLLECTION_DIRTY_TIMESTAMP, Collection.WISHLIST_COMMENT_DIRTY_TIMESTAMP, Collection.TRADE_CONDITION_DIRTY_TIMESTAMP, Collection.WANT_PARTS_DIRTY_TIMESTAMP, Collection.HAS_PARTS_DIRTY_TIMESTAMP }; final int _ID = 0; final int COLLECTION_ID = 1; final int COLLECTION_NAME = 2; // int COLLECTION_SORT_NAME = 3; final int COMMENT = 4; final int PRIVATE_INFO_PRICE_PAID_CURRENCY = 5; final int PRIVATE_INFO_PRICE_PAID = 6; final int PRIVATE_INFO_CURRENT_VALUE_CURRENCY = 7; final int PRIVATE_INFO_CURRENT_VALUE = 8; final int PRIVATE_INFO_QUANTITY = 9; final int PRIVATE_INFO_ACQUISITION_DATE = 10; final int PRIVATE_INFO_ACQUIRED_FROM = 11; final int PRIVATE_INFO_COMMENT = 12; final int LAST_MODIFIED = 13; // int COLLECTION_THUMBNAIL_URL = 14; final int COLLECTION_IMAGE_URL = 15; final int COLLECTION_YEAR_PUBLISHED = 16; final int CONDITION = 17; final int HAS_PARTS_LIST = 18; final int WANT_PARTS_LIST = 19; final int WISHLIST_COMMENT = 20; final int RATING = 21; final int UPDATED = 22; final int STATUS_OWN = 23; // int STATUS_PREVIOUSLY_OWNED = 24; // int STATUS_FOR_TRADE = 25; // int STATUS_WANT = 26; // int STATUS_WANT_TO_BUY = 27; final int STATUS_WISHLIST = 28; // int STATUS_WANT_TO_PLAY = 29; final int STATUS_PRE_ORDERED = 30; final int STATUS_WISHLIST_PRIORITY = 31; final int NUM_PLAYS = 32; final int RATING_DIRTY_TIMESTAMP = 33; final int COMMENT_DIRTY_TIMESTAMP = 34; final int PRIVATE_INFO_DIRTY_TIMESTAMP = 35; final int STATUS_DIRTY_TIMESTAMP = 36; final int COLLECTION_DIRTY_TIMESTAMP = 37; final int WISHLIST_COMMENT_DIRTY_TIMESTAMP = 38; final int TRADE_CONDITION_DIRTY_TIMESTAMP = 39; final int WANT_PARTS_DIRTY_TIMESTAMP = 40; final int HAS_PARTS_DIRTY_TIMESTAMP = 41; Resources r; int id; private long internalId; String name; // String sortName; private String comment; private long commentTimestamp; private long lastModifiedDateTime; private double rating; private long ratingTimestamp; private long updated; private String priceCurrency; private double price; private String currentValueCurrency; private double currentValue; int quantity; private String acquiredFrom; private String acquisitionDate; String privateComment; private long privateInfoTimestamp; private long statusTimestamp; String imageUrl; private int year; String condition; String wantParts; String hasParts; int wishlistPriority; String wishlistComment; int numPlays; private ArrayList<String> statusDescriptions; private ArrayList<String> statuses; private long dirtyTimestamp; private long wishlistCommentDirtyTimestamp; private long tradeConditionDirtyTimestamp; private long wantPartsDirtyTimestamp; private long hasPartsDirtyTimestamp; public CollectionItem() { // TODO: delete this, here just to get the projection; gotta be a better way } public CollectionItem(Cursor cursor) { r = getResources(); id = cursor.getInt(COLLECTION_ID); internalId = cursor.getLong(_ID); name = cursor.getString(COLLECTION_NAME); // sortName = cursor.getString(COLLECTION_SORT_NAME); comment = cursor.getString(COMMENT); commentTimestamp = cursor.getLong(COMMENT_DIRTY_TIMESTAMP); rating = cursor.getDouble(RATING); ratingTimestamp = cursor.getLong(RATING_DIRTY_TIMESTAMP); lastModifiedDateTime = cursor.getLong(LAST_MODIFIED); updated = cursor.getLong(UPDATED); priceCurrency = cursor.getString(PRIVATE_INFO_PRICE_PAID_CURRENCY); price = cursor.getDouble(PRIVATE_INFO_PRICE_PAID); currentValueCurrency = cursor.getString(PRIVATE_INFO_CURRENT_VALUE_CURRENCY); currentValue = cursor.getDouble(PRIVATE_INFO_CURRENT_VALUE); quantity = cursor.getInt(PRIVATE_INFO_QUANTITY); privateComment = cursor.getString(PRIVATE_INFO_COMMENT); acquiredFrom = cursor.getString(PRIVATE_INFO_ACQUIRED_FROM); acquisitionDate = cursor.getString(PRIVATE_INFO_ACQUISITION_DATE); privateInfoTimestamp = cursor.getLong(PRIVATE_INFO_DIRTY_TIMESTAMP); statusTimestamp = cursor.getLong(STATUS_DIRTY_TIMESTAMP); imageUrl = cursor.getString(COLLECTION_IMAGE_URL); year = cursor.getInt(COLLECTION_YEAR_PUBLISHED); wishlistPriority = cursor.getInt(STATUS_WISHLIST_PRIORITY); wishlistComment = cursor.getString(WISHLIST_COMMENT); condition = cursor.getString(CONDITION); wantParts = cursor.getString(WANT_PARTS_LIST); hasParts = cursor.getString(HAS_PARTS_LIST); numPlays = cursor.getInt(NUM_PLAYS); dirtyTimestamp = cursor.getLong(COLLECTION_DIRTY_TIMESTAMP); wishlistCommentDirtyTimestamp = cursor.getLong(WISHLIST_COMMENT_DIRTY_TIMESTAMP); tradeConditionDirtyTimestamp = cursor.getLong(TRADE_CONDITION_DIRTY_TIMESTAMP); wantPartsDirtyTimestamp = cursor.getLong(WANT_PARTS_DIRTY_TIMESTAMP); hasPartsDirtyTimestamp = cursor.getLong(HAS_PARTS_DIRTY_TIMESTAMP); statuses = new ArrayList<>(); statusDescriptions = new ArrayList<>(); for (int i = STATUS_OWN; i <= STATUS_PRE_ORDERED; i++) { if (cursor.getInt(i) == 1) { statuses.add(r.getStringArray(R.array.collection_status_filter_values)[i - STATUS_OWN]); if (i == STATUS_WISHLIST) { statusDescriptions.add(getWishlistPriorityDescription()); } else { statusDescriptions.add(r.getStringArray(R.array.collection_status_filter_entries)[i - STATUS_OWN]); } } } } String getStatusDescription() { String status = StringUtils.formatList(statusDescriptions); if (TextUtils.isEmpty(status)) { if (numPlays > 0) { return r.getString(R.string.played); } return r.getString(R.string.invalid_collection_status); } return status; } List<String> getStatuses() { return statuses; } String getRatingDescription() { return PresentationUtils.describePersonalRating(getActivity(), rating); } String getYearDescription() { return PresentationUtils.describeYear(getActivity(), year); } int getWishlistPriority() { return wishlistPriority; } String getWishlistPriorityDescription() { return PresentationUtils.describeWishlist(getActivity(), wishlistPriority); } String getPriceDescription() { return PresentationUtils.describeMoney(priceCurrency, price); } String getCurrentValueDescription() { return PresentationUtils.describeMoney(currentValueCurrency, currentValue); } boolean hasPrivateInfo() { return hasQuantity() || hasAcquisitionDate() || hasAcquiredFrom() || hasPrice() || hasValue(); } int getQuantity() { return quantity; } boolean hasQuantity() { return quantity > 1; } String getAcquisitionDate() { return acquisitionDate == null ? "" : acquisitionDate; } boolean hasAcquisitionDate() { return !TextUtils.isEmpty(acquisitionDate); } String getAcquiredFrom() { return acquiredFrom == null ? "" : acquiredFrom; } boolean hasAcquiredFrom() { return !TextUtils.isEmpty(acquiredFrom); } String getPrivateComment() { return privateComment == null ? "" : privateComment; } String getPriceCurrency() { return priceCurrency; } double getPrice() { return price; } boolean hasPrice() { return price > 0.0; } String getCurrentValueCurrency() { return currentValueCurrency; } double getCurrentValue() { return currentValue; } boolean hasValue() { return currentValue > 0.0; } CharSequence getPrivateInfo() { String initialText = r.getString(R.string.acquired); SpannableStringBuilder sb = new SpannableStringBuilder(); sb.append(initialText); if (hasQuantity()) { sb.append(" "); StringUtils.appendBold(sb, String.valueOf(quantity)); } if (hasAcquisitionDate()) { String date = null; try { date = DateUtils.formatDateTime(getContext(), DateTimeUtils.getMillisFromApiDate(acquisitionDate, 0), DateUtils.FORMAT_SHOW_DATE); } catch (Exception e) { Timber.w(e, "Could find a date in here: %s", acquisitionDate); } if (!TextUtils.isEmpty(date)) { sb.append(" ").append(r.getString(R.string.on)).append(" "); StringUtils.appendBold(sb, date); } } if (hasAcquiredFrom()) { sb.append(" ").append(r.getString(R.string.from)).append(" "); StringUtils.appendBold(sb, acquiredFrom); } if (hasPrice()) { sb.append(" ").append(r.getString(R.string.for_)).append(" "); StringUtils.appendBold(sb, getPriceDescription()); } if (hasValue()) { sb.append(" (").append(r.getString(R.string.currently_worth)).append(" "); StringUtils.appendBold(sb, getCurrentValueDescription()); sb.append(")"); } if (sb.toString().equals(initialText)) { // shouldn't happen return null; } return sb; } } }