package com.kickstarter.ui.viewholders;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Html;
import android.text.TextUtils;
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.kickstarter.KSApplication;
import com.kickstarter.R;
import com.kickstarter.libs.BaseActivity;
import com.kickstarter.libs.KSCurrency;
import com.kickstarter.libs.KSString;
import com.kickstarter.libs.transformations.CircleTransformation;
import com.kickstarter.libs.utils.I18nUtils;
import com.kickstarter.libs.utils.NumberUtils;
import com.kickstarter.libs.utils.ProgressBarUtils;
import com.kickstarter.libs.utils.ProjectUtils;
import com.kickstarter.libs.utils.SocialUtils;
import com.kickstarter.libs.utils.ViewUtils;
import com.kickstarter.models.Category;
import com.kickstarter.models.Location;
import com.kickstarter.models.Photo;
import com.kickstarter.models.Project;
import com.kickstarter.ui.IntentKey;
import com.kickstarter.ui.activities.ProjectSocialActivity;
import com.kickstarter.ui.views.IconButton;
import com.squareup.picasso.Picasso;
import org.joda.time.DateTime;
import java.math.RoundingMode;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.BindColor;
import butterknife.BindDimen;
import butterknife.BindDrawable;
import butterknife.BindString;
import butterknife.ButterKnife;
import butterknife.OnClick;
import static com.kickstarter.libs.utils.DateTimeUtils.mediumDate;
import static com.kickstarter.libs.utils.DateTimeUtils.mediumDateShortTime;
import static com.kickstarter.libs.utils.ObjectUtils.coalesce;
import static com.kickstarter.libs.utils.ObjectUtils.requireNonNull;
import static com.kickstarter.libs.utils.ViewUtils.getScreenDensity;
import static com.kickstarter.libs.utils.ViewUtils.getScreenHeightDp;
import static com.kickstarter.libs.utils.ViewUtils.getScreenWidthDp;
public final class ProjectViewHolder extends KSViewHolder {
private Project project;
private String configCountry;
private Context context;
private final Delegate delegate;
protected @Bind(R.id.avatar) ImageView avatarImageView;
protected @Bind(R.id.avatar_name) TextView avatarNameTextView;
protected @Bind(R.id.backers_count) TextView backersCountTextView;
protected @Bind(R.id.backer_label) LinearLayout backerLabelLinearLayout;
protected @Bind(R.id.back_project_button) @Nullable Button backProjectButton;
protected @Bind(R.id.blurb) TextView blurbTextView;
protected @Bind(R.id.category) TextView categoryTextView;
protected @Bind(R.id.comments_count) TextView commentsCountTextView;
protected @Bind(R.id.creator_name) TextView creatorNameTextView;
protected @Bind(R.id.deadline_countdown_text_view) TextView deadlineCountdownTextView;
protected @Bind(R.id.deadline_countdown_unit_text_view) TextView deadlineCountdownUnitTextView;
protected @Bind(R.id.project_disclaimer_text_view) TextView projectDisclaimerTextView;
protected @Bind(R.id.goal) TextView goalTextView;
protected @Bind(R.id.land_overlay_text) @Nullable ViewGroup landOverlayTextViewGroup;
protected @Bind(R.id.location) TextView locationTextView;
protected @Bind(R.id.manage_pledge_button) @Nullable Button managePledgeButton;
protected @Bind(R.id.name_creator_view) @Nullable ViewGroup nameCreatorViewGroup;
protected @Bind(R.id.percentage_funded) ProgressBar percentageFundedProgressBar;
protected @Bind(R.id.project_photo) ImageView photoImageView;
protected @Bind(R.id.play_button_overlay) IconButton playButton;
protected @Bind(R.id.pledged) TextView pledgedTextView;
protected @Bind(R.id.project_name) TextView projectNameTextView;
protected @Bind(R.id.project_social_image) ImageView projectSocialImageView;
protected @Bind(R.id.project_social_text) TextView projectSocialTextView;
protected @Bind(R.id.project_stats_view) ViewGroup projectStatsViewGroup;
protected @Bind(R.id.project_social_view) ViewGroup projectSocialViewGroup;
protected @Bind(R.id.project_state_header_text_view) TextView projectStateHeaderTextView;
protected @Bind(R.id.project_state_subhead_text_view) TextView projectStateSubheadTextView;
protected @Bind(R.id.project_state_view_group) ViewGroup projectStateViewGroup;
protected @Bind(R.id.view_pledge_button) @Nullable Button viewPledgeButton;
protected @Bind(R.id.updates_count) TextView updatesCountTextView;
protected @Bind(R.id.usd_conversion_text_view) TextView usdConversionTextView;
protected @BindColor(R.color.green_alpha_20) int greenAlpha50Color;
protected @BindColor(R.color.medium_gray) int mediumGrayColor;
protected @BindDimen(R.dimen.grid_1) int grid1Dimen;
protected @BindDimen(R.dimen.grid_2) int grid2Dimen;
protected @BindDimen(R.dimen.grid_3) int grid3Dimen;
protected @BindDimen(R.dimen.grid_4) int grid4Dimen;
protected @BindDrawable(R.drawable.click_indicator_light_masked) Drawable clickIndicatorLightMaskedDrawable;
protected @BindDrawable(R.drawable.gray_gradient) Drawable grayGradientDrawable;
protected @BindString(R.string.project_creator_by_creator_html) String byCreatorString;
protected @BindString(R.string.discovery_baseball_card_blurb_read_more) String blurbReadMoreString;
protected @BindString(R.string.discovery_baseball_card_stats_convert_from_pledged_of_goal) String convertedFromString;
protected @BindString(R.string.project_disclaimer_goal_not_reached) String projectDisclaimerGoalNotReachedString;
protected @BindString(R.string.project_disclaimer_goal_reached) String projectDisclaimerGoalReachedString;
protected @BindString(R.string.project_status_funding_canceled) String fundingCanceledString;
protected @BindString(R.string.project_status_funding_project_canceled_by_creator) String fundingCanceledByCreatorString;
protected @BindString(R.string.project_status_project_was_successfully_funded_on_deadline) String successfullyFundedOnDeadlineString;
protected @BindString(R.string.project_status_funding_suspended) String fundingSuspendedString;
protected @BindString(R.string.project_status_funding_project_suspended) String fundingProjectSuspendedString;
protected @BindString(R.string.project_status_funding_unsuccessful) String fundingUnsuccessfulString;
protected @BindString(R.string.project_status_project_funding_goal_not_reached) String fundingGoalNotReachedString;
protected @BindString(R.string.project_status_funded) String fundedString;
protected @BindString(R.string.discovery_baseball_card_stats_pledged_of_goal) String pledgedOfGoalString;
protected @BindString(R.string.discovery_baseball_card_stats_pledged_of_goal_short) String ofGoalString;
protected @BindString(R.string.discovery_baseball_card_stats_backers) String backersString;
protected @Inject KSCurrency ksCurrency;
protected @Inject KSString ksString;
public interface Delegate {
void projectViewHolderBackProjectClicked(ProjectViewHolder viewHolder);
void projectViewHolderBlurbClicked(ProjectViewHolder viewHolder);
void projectViewHolderCommentsClicked(ProjectViewHolder viewHolder);
void projectViewHolderCreatorClicked(ProjectViewHolder viewHolder);
void projectViewHolderManagePledgeClicked(ProjectViewHolder viewHolder);
void projectViewHolderUpdatesClicked(ProjectViewHolder viewHolder);
void projectViewHolderVideoStarted(ProjectViewHolder viewHolder);
void projectViewHolderViewPledgeClicked(ProjectViewHolder viewHolder);
}
public ProjectViewHolder(final @NonNull View view, final @NonNull Delegate delegate) {
super(view);
this.delegate = delegate;
this.context = view.getContext();
((KSApplication) context.getApplicationContext()).component().inject(this);
ButterKnife.bind(this, view);
}
@Override
public void bindData(final @Nullable Object data) throws Exception {
@SuppressWarnings("unchecked")
final Pair<Project, String> projectAndCountry = requireNonNull((Pair<Project, String>) data);
project = requireNonNull(projectAndCountry.first, Project.class);
configCountry = requireNonNull(projectAndCountry.second, String.class);
}
public void onBind() {
final Photo photo = project.photo();
if (photo != null) {
final int targetImageWidth = (int) (getScreenWidthDp(context) * getScreenDensity(context));
final int targetImageHeight = ProjectUtils.photoHeightFromWidthRatio(targetImageWidth);
photoImageView.setMaxHeight(targetImageHeight);
Picasso.with(context)
.load(photo.full())
.resize(targetImageWidth, targetImageHeight)
.centerCrop()
.placeholder(grayGradientDrawable)
.into(photoImageView);
}
if (project.hasVideo()) {
playButton.setVisibility(View.VISIBLE);
} else {
playButton.setVisibility(View.GONE);
}
/* Project */
blurbTextView.setText(Html.fromHtml(ksString.format(blurbReadMoreString,
"blurb", TextUtils.htmlEncode(project.blurb()),
"space", "\u00A0"
)));
creatorNameTextView.setText(Html.fromHtml(ksString.format(byCreatorString,
"creator_name", TextUtils.htmlEncode(project.creator().name()))));
if (project.isBacking()) {
backerLabelLinearLayout.setVisibility(View.VISIBLE);
} else {
backerLabelLinearLayout.setVisibility(View.GONE);
}
projectNameTextView.setText(project.name());
final Category category = project.category();
if (category != null) {
categoryTextView.setText(category.name());
}
final Location location = project.location();
if (location != null) {
locationTextView.setText(location.displayableName());
}
percentageFundedProgressBar.setProgress(ProgressBarUtils.progress(project.percentageFunded()));
deadlineCountdownTextView.setText(NumberUtils.format(ProjectUtils.deadlineCountdownValue(project)));
deadlineCountdownUnitTextView.setText(ProjectUtils.deadlineCountdownDetail(project, context, ksString));
backersCountTextView.setText(NumberUtils.format(project.backersCount()));
/* Creator */
Picasso.with(context).load(project.creator().avatar()
.medium())
.transform(new CircleTransformation())
.into(avatarImageView);
avatarNameTextView.setText(project.creator().name());
final Integer updatesCount = project.updatesCount();
updatesCountTextView.setText(updatesCount != null ? NumberUtils.format(updatesCount) : null);
final Integer commentsCount = project.commentsCount();
commentsCountTextView.setText(commentsCount != null ? NumberUtils.format(commentsCount) : null);
setConvertedUsdView();
setLandscapeActionButton();
setLandscapeOverlayText();
setPledgedOfGoalView();
setProjectDisclaimerView();
setProjectSocialClick();
setProjectStateView();
setSocialView();
setStatsContentDescription();
}
@Nullable @OnClick(R.id.back_project_button)
public void backProjectButtonOnClick() {
delegate.projectViewHolderBackProjectClicked(this);
}
@OnClick({R.id.blurb, R.id.campaign})
public void blurbClick() {
delegate.projectViewHolderBlurbClicked(this);
}
@OnClick(R.id.comments)
public void commentsClick() {
delegate.projectViewHolderCommentsClicked(this);
}
@OnClick({R.id.creator_name, R.id.creator_info})
public void creatorNameClick() {
delegate.projectViewHolderCreatorClicked(this);
}
@Nullable @OnClick(R.id.manage_pledge_button)
public void managePledgeOnClick() {
delegate.projectViewHolderManagePledgeClicked(this);
}
@OnClick(R.id.play_button_overlay)
public void playButtonClick() {
delegate.projectViewHolderVideoStarted(this);
}
@Nullable @OnClick(R.id.view_pledge_button)
public void viewPledgeOnClick() {
delegate.projectViewHolderViewPledgeClicked(this);
}
public void setConvertedUsdView() {
if (I18nUtils.isCountryUS(configCountry) && !I18nUtils.isCountryUS(project.country())) {
usdConversionTextView.setVisibility(View.VISIBLE);
usdConversionTextView.setText(ksString.format(
convertedFromString,
"pledged",
ksCurrency.format(project.pledged(), project),
"goal",
ksCurrency.format(project.goal(), project)
));
} else {
usdConversionTextView.setVisibility(View.GONE);
}
}
/**
* Set landscape project action buttons in the ViewHolder rather than Activity.
*/
public void setLandscapeActionButton() {
if (backProjectButton != null && managePledgeButton != null && viewPledgeButton != null) {
ProjectUtils.setActionButton(project, backProjectButton, managePledgeButton, viewPledgeButton);
}
}
/**
* Set top margin of overlay text based on landscape screen height, scaled by screen density.
*/
public void setLandscapeOverlayText() {
if (landOverlayTextViewGroup != null && nameCreatorViewGroup != null) {
final int screenHeight = getScreenHeightDp(context);
final float densityOffset = context.getResources().getDisplayMetrics().density;
final float topMargin = ((screenHeight / 3 * 2) * densityOffset) - grid4Dimen; // offset for toolbar
ViewUtils.setRelativeViewGroupMargins(landOverlayTextViewGroup, grid4Dimen, (int) topMargin, grid4Dimen, 0);
if (!project.hasVideo()) {
ViewUtils.setRelativeViewGroupMargins(nameCreatorViewGroup, 0, 0, 0, grid2Dimen);
} else {
ViewUtils.setRelativeViewGroupMargins(nameCreatorViewGroup, 0, 0, 0, grid1Dimen);
}
}
}
public void setPledgedOfGoalView() {
pledgedTextView.setText(ksCurrency.format(project.pledged(), project, false, true, RoundingMode.DOWN));
/* a11y */
final String goalString = ksCurrency.format(project.goal(), project, false, true, RoundingMode.DOWN);
final String goalText = ViewUtils.isFontScaleLarge(context) ?
ksString.format(ofGoalString, "goal", goalString) : ksString.format(pledgedOfGoalString, "goal", goalString);
goalTextView.setText(goalText);
}
public void setProjectDisclaimerView() {
final DateTime deadline = project.deadline();
if (deadline == null) {
projectDisclaimerTextView.setVisibility(View.GONE);
} else if (!project.isLive()) {
projectDisclaimerTextView.setVisibility(View.GONE);
} else if (project.isFunded()) {
projectDisclaimerTextView.setVisibility(View.VISIBLE);
projectDisclaimerTextView.setText(ksString.format(
projectDisclaimerGoalReachedString,
"deadline",
mediumDateShortTime(deadline)
));
} else {
projectDisclaimerTextView.setVisibility(View.VISIBLE);
projectDisclaimerTextView.setText(ksString.format(
projectDisclaimerGoalNotReachedString,
"goal_currency",
ksCurrency.format(project.goal(), project, true),
"deadline",
mediumDateShortTime(deadline)
));
}
}
public void setProjectSocialClick() {
if (project.isFriendBacking()) {
if (project.friends().size() > 2) {
projectSocialViewGroup.setBackground(clickIndicatorLightMaskedDrawable);
projectSocialViewGroup.setOnClickListener(view -> {
final BaseActivity activity = (BaseActivity) context;
final Intent intent = new Intent(activity, ProjectSocialActivity.class)
.putExtra(IntentKey.PROJECT, project);
activity.startActivity(intent);
activity.overridePendingTransition(R.anim.slide_in_right, R.anim.fade_out_slide_out_left);
});
}
}
}
public void setProjectStateView() {
final DateTime stateChangedAt = coalesce(project.stateChangedAt(), new DateTime());
switch(project.state()) {
case Project.STATE_SUCCESSFUL:
percentageFundedProgressBar.setVisibility(View.GONE);
projectStateViewGroup.setVisibility(View.VISIBLE);
projectStateViewGroup.setBackgroundColor(greenAlpha50Color);
projectStateHeaderTextView.setText(fundedString);
projectStateSubheadTextView.setText(ksString.format(successfullyFundedOnDeadlineString,
"deadline", mediumDate(stateChangedAt)
));
break;
case Project.STATE_CANCELED:
percentageFundedProgressBar.setVisibility(View.GONE);
projectStateViewGroup.setVisibility(View.VISIBLE);
projectStateViewGroup.setBackgroundColor(mediumGrayColor);
projectStateHeaderTextView.setText(fundingCanceledString);
projectStateSubheadTextView.setText(fundingCanceledByCreatorString);
break;
case Project.STATE_FAILED:
percentageFundedProgressBar.setVisibility(View.GONE);
projectStateViewGroup.setVisibility(View.VISIBLE);
projectStateViewGroup.setBackgroundColor(mediumGrayColor);
projectStateHeaderTextView.setText(fundingUnsuccessfulString);
projectStateSubheadTextView.setText(ksString.format(fundingGoalNotReachedString,
"deadline", mediumDate(stateChangedAt)
));
break;
case Project.STATE_SUSPENDED:
percentageFundedProgressBar.setVisibility(View.GONE);
projectStateViewGroup.setVisibility(View.VISIBLE);
projectStateViewGroup.setBackgroundColor(mediumGrayColor);
projectStateHeaderTextView.setText(fundingSuspendedString);
projectStateSubheadTextView.setText(fundingProjectSuspendedString);
break;
default:
percentageFundedProgressBar.setVisibility(View.VISIBLE);
projectStateViewGroup.setVisibility(View.GONE);
break;
}
}
public void setSocialView() {
if (project.isFriendBacking()) {
projectSocialViewGroup.setVisibility(View.VISIBLE);
ViewUtils.setLinearViewGroupMargins(projectStatsViewGroup, 0, grid3Dimen, 0, grid2Dimen);
projectSocialImageView.setVisibility(View.VISIBLE);
Picasso.with(context).load(project.friends().get(0).avatar()
.small())
.transform(new CircleTransformation())
.into(projectSocialImageView);
projectSocialTextView.setText(SocialUtils.projectCardFriendNamepile(project.friends(), ksString));
} else {
projectSocialViewGroup.setVisibility(View.GONE);
ViewUtils.setLinearViewGroupMargins(projectStatsViewGroup, 0, grid3Dimen, 0, grid4Dimen);
}
}
public void setStatsContentDescription() {
final String backersCountContentDescription = NumberUtils.format(project.backersCount()) + " " + backersString;
final String pledgedContentDescription = pledgedTextView.getText() + " " + goalTextView.getText();
final String deadlineCountdownContentDescription = deadlineCountdownTextView.getText() + " " + deadlineCountdownUnitTextView.getText();
backersCountTextView.setContentDescription(backersCountContentDescription);
pledgedTextView.setContentDescription(pledgedContentDescription);
deadlineCountdownTextView.setContentDescription(deadlineCountdownContentDescription);
}
@OnClick(R.id.updates)
public void updatesClick() {
delegate.projectViewHolderUpdatesClicked(this);
}
}