package com.boardgamegeek.util; import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.PluralsRes; import android.support.annotation.StringRes; import android.support.design.widget.FloatingActionButton; import android.text.Html; import android.text.SpannedString; import android.text.TextUtils; import android.text.format.DateUtils; import android.view.View; import android.widget.EditText; import android.widget.TextView; import com.boardgamegeek.R; import com.boardgamegeek.io.BggService; import com.boardgamegeek.model.Constants; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import hugo.weaving.DebugLog; /** * Methods to aid in presenting information in a consistent manner. */ public class PresentationUtils { private static final DecimalFormat RATING_FORMAT = new DecimalFormat("#0.0"); private static final DecimalFormat AVERAGE_RATING_FORMAT = new DecimalFormat("#0.000"); private static final DecimalFormat PERSONAL_RATING_FORMAT = new DecimalFormat("#0.#"); private static final DecimalFormat MONEY_FORMAT = setUpMoneyFormatter(); private PresentationUtils() { } @DebugLog @NonNull private static DecimalFormat setUpMoneyFormatter() { DecimalFormat format = (DecimalFormat) NumberFormat.getCurrencyInstance(); DecimalFormatSymbols symbols = format.getDecimalFormatSymbols(); symbols.setCurrencySymbol(""); format.setDecimalFormatSymbols(symbols); return format; } /** * Formats the date for display in the forums (based on the users selected preference. */ public static CharSequence formatTimestamp(Context context, long date, boolean isForumTimestamp, boolean includeTime) { int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_ABBREV_MONTH; if (includeTime) flags |= DateUtils.FORMAT_SHOW_TIME; if (isForumTimestamp && PreferencesUtils.getForumDates(context)) { return DateUtils.formatDateTime(context, date, flags); } else { if (date == 0) { return context.getString(R.string.text_unknown); } return DateUtils.getRelativeTimeSpanString(date, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, flags); } } @DebugLog public static CharSequence describePastDaySpan(long time) { if (time == 0) { return ""; } return DateUtils.getRelativeTimeSpanString(time, System.currentTimeMillis(), DateUtils.DAY_IN_MILLIS); } @DebugLog public static String describeYear(@Nullable Context context, int year) { if (context == null) { return ""; } if (year > 0) { return context.getString(R.string.year_positive, String.valueOf(year)); } else if (year == Constants.YEAR_UNKNOWN) { return context.getString(R.string.year_zero); } else { return context.getString(R.string.year_negative, String.valueOf(-year)); } } @DebugLog public static String describeWishlist(@Nullable Context context, int priority) { if (context == null) { return ""; } if (priority < 0 || priority > 5) { return context.getString(R.string.wishlist); } return context.getResources().getStringArray(R.array.wishlist_priority)[priority]; } @DebugLog public static String describeRating(Context context, double rating) { return describeRating(context, rating, RATING_FORMAT); } @DebugLog public static String describeAverageRating(Context context, double rating) { return describeRating(context, rating, AVERAGE_RATING_FORMAT); } @DebugLog public static String describePersonalRating(Context context, double rating) { return describeRating(context, rating, PERSONAL_RATING_FORMAT); } @DebugLog private static String describeRating(@NonNull Context context, double rating, DecimalFormat format) { if (rating > 0.0) { return format.format(rating); } else { return context.getString(R.string.unrated); } } @DebugLog public static String describeRank(int rank) { if (rank == 0 || rank == Integer.MAX_VALUE) { return ""; } else { return "#" + rank; } } @DebugLog public static String describeRankName(Context context, String type, String name) { if (name == null) { return ""; } if (type == null) { return name; } @StringRes int resId = R.string.title_game; if (BggService.RANK_TYPE_SUBTYPE.equals(type)) { switch (name) { case BggService.THING_SUBTYPE_BOARDGAME: resId = R.string.title_board_game; break; case BggService.THING_SUBTYPE_BOARDGAME_EXPANSION: resId = R.string.title_expansion; break; case BggService.THING_SUBTYPE_BOARDGAME_ACCESSORY: resId = R.string.title_accessory; break; default: return name; } } else if (BggService.RANK_TYPE_FAMILY.equals(type)) { switch (name) { case BggService.RANK_FAMILY_NAME_ABSTRACT_GAMES: resId = R.string.title_abstract; break; case BggService.RANK_FAMILY_NAME_CHILDRENS_GAMES: resId = R.string.title_childrens; break; case BggService.RANK_FAMILY_NAME_CUSTOMIZABLE_GAMES: resId = R.string.title_customizable; break; case BggService.RANK_FAMILY_NAME_FAMILY_GAMES: resId = R.string.title_family; break; case BggService.RANK_FAMILY_NAME_PARTY_GAMES: resId = R.string.title_party; break; case BggService.RANK_FAMILY_NAME_STRATEGY_GAMES: resId = R.string.title_strategy; break; case BggService.RANK_FAMILY_NAME_THEMATIC_GAMES: resId = R.string.title_thematic; break; case BggService.RANK_FAMILY_NAME_WAR_GAMES: resId = R.string.title_war; break; default: return name; } } return context.getString(resId); } @DebugLog public static CharSequence describeWeight(Context context, double weight) { @StringRes int resId = R.string.weight_1_text; if (weight >= 4.2) { resId = R.string.weight_5_text; } else if (weight >= 3.4) { resId = R.string.weight_4_text; } else if (weight >= 2.6) { resId = R.string.weight_3_text; } else if (weight >= 1.8) { resId = R.string.weight_2_text; } return getText(context, resId, weight); } @DebugLog public static CharSequence describeLanguageDependence(Context context, double value) { @StringRes int resId = R.string.language_1_text; if (value >= 4.2) { resId = R.string.language_5_text; } else if (value >= 3.4) { resId = R.string.language_4_text; } else if (value >= 2.6) { resId = R.string.language_3_text; } else if (value >= 1.8) { resId = R.string.language_2_text; } return getText(context, resId, value); } @DebugLog public static String describePlayCount(Context context, int playCount) { @StringRes int resId = 0; if (playCount >= 100) { resId = R.string.play_stat_dollar; } else if (playCount >= 50) { resId = R.string.play_stat_half_dollar; } else if (playCount >= 25) { resId = R.string.play_stat_quarter; } else if (playCount >= 10) { resId = R.string.play_stat_dime; } else if (playCount >= 5) { resId = R.string.play_stat_nickel; } if (resId != 0) { return context.getString(resId); } else { return ""; } } @NonNull public static String describeMoney(String currency, double amount) { if (TextUtils.isEmpty(currency) && amount == 0.0) { return ""; } return describeCurrency(currency) + MONEY_FORMAT.format(amount); } @NonNull public static String describeMoneyWithoutDecimals(String currency, double amount) { if (TextUtils.isEmpty(currency) && amount == 0.0) { return ""; } return describeCurrency(currency) + (int) amount; } private static String describeCurrency(@Nullable String currency) { if (currency == null) { return "$"; } switch (currency) { case "USD": case "CAD": case "AUD": return "$"; case "EUR": return "\u20AC"; case "GBP": return "\u00A3"; case "YEN": return "\u00A5"; } return ""; } /** * Build a displayable full name from the first and last name. */ @DebugLog @NonNull public static String buildFullName(@NonNull String firstName, @NonNull String lastName) { if (TextUtils.isEmpty(firstName) && TextUtils.isEmpty(lastName)) { return ""; } else if (TextUtils.isEmpty(firstName)) { return lastName.trim(); } else if (TextUtils.isEmpty(lastName)) { return firstName.trim(); } else { return firstName.trim() + " " + lastName.trim(); } } @DebugLog public static String describePlayer(String name, String username) { if (TextUtils.isEmpty(username)) { return name; } return name + " (" + username + ")"; } @DebugLog @NonNull public static String describePlayDetails(Context context, String date, String location, int quantity, int length, int playerCount) { String info = ""; if (quantity > 1) { info += quantity + " " + context.getString(R.string.times) + " "; } if (!TextUtils.isEmpty(date)) { info += context.getString(R.string.on) + " " + date + " "; } if (!TextUtils.isEmpty(location)) { info += context.getString(R.string.at) + " " + location + " "; } if (length > 0) { info += context.getString(R.string.for_) + " " + DateTimeUtils.formatMinutes(length) + " "; } if (playerCount > 0) { info += context.getResources().getQuantityString(R.plurals.player_description, playerCount, playerCount); } return info.trim(); } @DebugLog public static void setTextOrHide(@Nullable TextView view, CharSequence text) { if (view != null) { view.setText(text); view.setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE); } } public static void setAndSelectExistingText(@Nullable EditText view, @Nullable String existingText) { if (view != null && !TextUtils.isEmpty(existingText)) { view.setText(existingText); view.setSelection(0, existingText.length()); } } @DebugLog public static CharSequence getText(Context context, @StringRes int id, Object... args) { for (int i = 0; i < args.length; ++i) { args[i] = args[i] instanceof String ? TextUtils.htmlEncode((String) args[i]) : args[i]; } final String htmlString = String.format(Html.toHtml(new SpannedString(context.getText(id))), args); return trimTrailingWhitespace(Html.fromHtml(htmlString)); } @DebugLog public static CharSequence getQuantityText(Context context, @PluralsRes int id, int quantity, Object... args) { for (int i = 0; i < args.length; ++i) { args[i] = args[i] instanceof String ? TextUtils.htmlEncode((String) args[i]) : args[i]; } final String htmlString = String.format(Html.toHtml(new SpannedString(context.getResources().getQuantityText(id, quantity))), args); return trimTrailingWhitespace(Html.fromHtml(htmlString)); } @DebugLog public static CharSequence trimTrailingWhitespace(CharSequence source) { if (source == null) return ""; int i = source.length(); do { --i; } while (i >= 0 && Character.isWhitespace(source.charAt(i))); return source.subSequence(0, i + 1); } @DebugLog public static int[] getColorSchemeResources() { return new int[] { R.color.orange, R.color.light_blue, R.color.dark_blue, R.color.light_blue }; } @DebugLog public static void ensureFabIsShown(final FloatingActionButton fab) { fab.postDelayed(new Runnable() { @Override public void run() { fab.show(); } }, 2000); } }