package org.wikipedia.page.leadimages;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.Typeface;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DimenRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.PopupMenu;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import org.wikipedia.R;
import org.wikipedia.media.AvPlayer;
import org.wikipedia.media.DefaultAvPlayer;
import org.wikipedia.media.MediaPlayerImplementation;
import org.wikipedia.richtext.AudioUrlSpan;
import org.wikipedia.richtext.LeadingSpan;
import org.wikipedia.richtext.ParagraphSpan;
import org.wikipedia.richtext.RichTextUtil;
import org.wikipedia.util.DimenUtil;
import org.wikipedia.util.FeedbackUtil;
import org.wikipedia.views.AppTextView;
import org.wikipedia.views.FaceAndColorDetectImageView;
import org.wikipedia.views.ObservableWebView;
import org.wikipedia.views.StatusBarBlankView;
import org.wikipedia.views.ViewUtil;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import static org.apache.commons.lang3.StringUtils.defaultIfEmpty;
import static org.wikipedia.util.DimenUtil.leadImageHeightForDevice;
import static org.wikipedia.util.L10nUtil.isLangRTL;
import static org.wikipedia.util.ResourceUtil.getThemedAttributeId;
public class PageHeaderView extends FrameLayout implements ObservableWebView.OnScrollChangeListener {
@BindView(R.id.view_page_header_image)
PageHeaderImageView image;
@BindView(R.id.view_page_header_image_gradient) View gradient;
@BindView(R.id.view_page_title_text) AppTextView titleText;
@BindView(R.id.view_page_subtitle_text) AppTextView subtitleText;
@BindView(R.id.view_page_header_divider) View divider;
@BindView(R.id.view_page_header_container) LinearLayout container;
@BindView(R.id.view_page_header_status_bar_placeholder) StatusBarBlankView statusBarPlaceholder;
@BindView(R.id.view_page_header_edit_pencil) ImageView editPencil;
@Nullable private Callback callback;
@VisibleForTesting @NonNull CharSequence title = "";
@VisibleForTesting @NonNull CharSequence subtitle = "";
@VisibleForTesting @Nullable String pronunciationUrl;
private boolean allowDescriptionEdit;
@NonNull private final AvPlayer avPlayer = new DefaultAvPlayer(new MediaPlayerImplementation());
@VisibleForTesting @NonNull final ClickableSpan descriptionClickSpan = new DescriptionClickableSpan();
public interface Callback {
void onDescriptionClicked();
void onEditDescription();
void onEditLeadSection();
}
public PageHeaderView(Context context) {
super(context);
init();
}
public PageHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PageHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public PageHeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
public void hide() {
setVisibility(View.GONE);
}
public void showText() {
setVisibility(View.VISIBLE);
setTopOffset();
updateText();
setTextColor(getColor(getThemedAttributeId(getContext(),
R.attr.lead_text_color)));
}
public void showTextImage() {
setVisibility(View.VISIBLE);
unsetTopOffset();
updateText();
setTextColor(getColor(getThemedAttributeId(getContext(),
R.attr.lead_text_color)));
setImageHeight(leadImageHeightForDevice());
}
// TODO: remove.
@NonNull public ImageView getImage() {
return image.getImage();
}
public void setOnImageLoadListener(@Nullable FaceAndColorDetectImageView.OnImageLoadListener listener) {
image.setLoadListener(listener);
}
public void setCallback(@Nullable Callback callback) {
this.callback = callback;
}
public void loadImage(@Nullable String url) {
image.load(url);
int height = url == null ? 0 : leadImageHeightForDevice();
setMinimumHeight(height);
}
public void setAnimationPaused(boolean paused) {
image.setAnimationPaused(paused);
}
@NonNull
public Bitmap copyBitmap() {
return ViewUtil.getBitmapFromView(image.getImage());
}
public void setImageFocus(PointF focusPoint) {
image.setFocusPoint(focusPoint);
updateScroll();
}
public void setTitle(@Nullable CharSequence text) {
title = defaultIfEmpty(text, "");
updateText();
}
public void setSubtitle(@Nullable CharSequence text) {
subtitle = defaultIfEmpty(text, "");
updateText();
}
public boolean hasSubtitle() {
return !TextUtils.isEmpty(subtitle);
}
public void setProtected(boolean isProtected) {
editPencil.setImageDrawable(ContextCompat.getDrawable(getContext(), isProtected
? R.drawable.ic_edit_lock_24px : R.drawable.ic_mode_edit_white_24dp));
}
public void setLocale(@NonNull String locale) {
titleText.setLocale(locale);
subtitleText.setLocale(locale);
LinearLayout.LayoutParams dividerParams = (LinearLayout.LayoutParams) divider.getLayoutParams();
dividerParams.gravity = isLangRTL(locale) ? Gravity.RIGHT : Gravity.LEFT;
divider.setLayoutParams(dividerParams);
FrameLayout.LayoutParams pencilParams = (FrameLayout.LayoutParams) editPencil.getLayoutParams();
pencilParams.gravity = Gravity.BOTTOM | (isLangRTL(locale) ? Gravity.LEFT : Gravity.RIGHT);
int subtitlePadding = editPencil.getWidth();
subtitleText.setPadding(isLangRTL(locale) ? subtitlePadding : 0,
subtitleText.getPaddingTop(),
isLangRTL(locale) ? 0 : subtitlePadding,
subtitleText.getPaddingBottom());
if (TextUtils.isEmpty(subtitle)) {
subtitleText.setCompoundDrawablesWithIntrinsicBounds(
isLangRTL(locale) ? 0 : R.drawable.ic_short_text,
0,
isLangRTL(locale) ? R.drawable.ic_short_text : 0,
0);
} else {
subtitleText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
}
public void setPronunciation(@Nullable String url) {
pronunciationUrl = url;
updateText();
}
public boolean hasPronunciation() {
return pronunciationUrl != null;
}
public void setAllowDescriptionEdit(boolean allow) {
allowDescriptionEdit = allow;
updateText();
}
@Override
public void onScrollChanged(int oldScrollY, int scrollY, boolean isHumanScroll) {
updateScroll(scrollY);
}
@OnClick(R.id.view_page_header_edit_pencil) void onEditClick() {
if (allowDescriptionEdit) {
PopupMenu menu = new PopupMenu(editPencil.getContext(), editPencil, Gravity.END, 0,
R.style.PagePopupMenu);
menu.getMenuInflater().inflate(R.menu.menu_page_header_edit, menu.getMenu());
menu.setOnMenuItemClickListener(new EditMenuClickListener());
menu.show();
} else {
if (callback != null) {
callback.onEditLeadSection();
}
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
avPlayer.init();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
updateScroll();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
avPlayer.deinit();
}
private void setTextColor(@ColorInt int color) {
titleText.setTextColor(color);
}
private void updateScroll() {
updateScroll((int) -getTranslationY());
}
private void updateScroll(int scrollY) {
int offset = Math.min(getHeight(), scrollY);
image.getImage().setTranslationY(offset / 2);
setTranslationY(-offset);
}
private void updateText() {
avPlayer.stop();
SpannableStringBuilder builder = new SpannableStringBuilder(title);
builder.setSpan(new TypefaceSpan("serif"), 0, title.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
if (hasPronunciation()) {
builder.append(" ");
builder.append(pronunciationSpanned());
}
titleText.setMovementMethod(new LinkMovementMethod());
titleText.setText(builder);
if (hasSubtitle() || allowDescriptionEdit) {
subtitleText.setMovementMethod(hasSubtitle() ? null : new LinkMovementMethod());
subtitleText.setText(subtitleSpanned());
subtitleText.setVisibility(VISIBLE);
} else {
subtitleText.setVisibility(GONE);
}
}
private Spanned pronunciationSpanned() {
AudioUrlSpan pronunciationSpan = new AudioUrlSpan(titleText, avPlayer, pronunciationUrl,
AudioUrlSpan.ALIGN_BASELINE);
pronunciationSpan.setTint(getColor(getThemedAttributeId(getContext(),
R.attr.window_inverse_color)));
return RichTextUtil.setSpans(new SpannableString(" "),
0,
1,
Spannable.SPAN_INCLUSIVE_EXCLUSIVE,
pronunciationSpan,
new PronunciationClickableSpan(pronunciationSpan));
}
private Spanned subtitleSpanned() {
final float leadingScalar = DimenUtil.getFloat(R.dimen.lead_subtitle_leading_scalar);
final float paragraphScalar = DimenUtil.getFloat(R.dimen.lead_subtitle_paragraph_scalar);
String description = TextUtils.isEmpty(subtitle)
? getResources().getString(R.string.description_edit_add_description)
: subtitle.toString();
return RichTextUtil.setSpans(new SpannableString(description),
0,
description.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE,
new LeadingSpan(leadingScalar),
new ParagraphSpan(paragraphScalar),
TextUtils.isEmpty(subtitle) ? descriptionClickSpan : new ForegroundColorSpan(getColor(R.color.dark_gray)),
TextUtils.isEmpty(subtitle) ? new StyleSpan(Typeface.ITALIC) : null);
}
private void setImageHeight(int height) {
final float oneThird = 1 / 3;
DimenUtil.setViewHeight(image, height);
DimenUtil.setViewHeight(gradient, (int) oneThird * height);
}
private void init() {
inflate(getContext(), R.layout.view_page_header, this);
ButterKnife.bind(this);
FeedbackUtil.setToolbarButtonLongPressToast(editPencil);
hide();
}
@ColorInt
private int getColor(@ColorRes int id) {
return ContextCompat.getColor(getContext(), id);
}
private int getDimensionPixelSize(@DimenRes int id) {
return getResources().getDimensionPixelSize(id);
}
private void setTopOffset() {
setTopOffset(true);
}
private void unsetTopOffset() {
setTopOffset(false);
}
private void setTopOffset(boolean noImage) {
statusBarPlaceholder.setVisibility(noImage ? View.VISIBLE : View.GONE);
int offset = noImage ? getDimensionPixelSize(R.dimen.lead_no_image_top_offset_dp) : 0;
// Offset is a resolved pixel dimension, not a resource id
//noinspection ResourceType
setPadding(0, offset, 0, 0);
}
private class DescriptionClickableSpan extends ClickableSpan {
@Override
public void onClick(View view) {
if (callback != null) {
callback.onDescriptionClicked();
}
}
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setColor(getColor(TextUtils.isEmpty(subtitle)
? R.color.foundation_blue : R.color.dark_gray));
ds.setUnderlineText(false);
}
}
private class PronunciationClickableSpan extends ClickableSpan {
@NonNull private AudioUrlSpan audioSpan;
PronunciationClickableSpan(@NonNull AudioUrlSpan audioSpan) {
this.audioSpan = audioSpan;
}
@Override
public void onClick(View view) {
audioSpan.toggle();
}
}
private class EditMenuClickListener implements PopupMenu.OnMenuItemClickListener {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_page_header_edit_description:
if (callback != null) {
callback.onEditDescription();
}
return true;
case R.id.menu_page_header_edit_lead_section:
if (callback != null) {
callback.onEditLeadSection();
}
return true;
default:
return false;
}
}
}
}