improved status quote

This commit is contained in:
Mariotaku Lee 2015-04-08 00:36:54 +08:00
parent b181bbe38f
commit b966f0d04b
6 changed files with 228 additions and 65 deletions

View File

@ -106,6 +106,7 @@ import org.mariotaku.twidere.util.UserColorNameUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.Utils.OnMediaClickListener;
import org.mariotaku.twidere.view.CardMediaContainer;
import org.mariotaku.twidere.view.ForegroundColorView;
import org.mariotaku.twidere.view.ShapedImageView;
import org.mariotaku.twidere.view.StatusTextView;
import org.mariotaku.twidere.view.TwitterCardContainer;
@ -514,6 +515,8 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
private final ActionMenuView menuBar;
private final TextView nameView, screenNameView;
private final StatusTextView textView;
private final TextView quoteTextView;
private final TextView quotedNameView, quotedScreenNameView;
private final ShapedImageView profileImageView;
private final ImageView profileTypeView;
private final TextView timeSourceView;
@ -527,11 +530,18 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
private final View mediaPreviewLoad;
private final CardMediaContainer mediaPreview;
private final View quotedNameContainer;
private final ForegroundColorView quoteIndicator;
private final TextView locationView;
private final TwitterCardContainer twitterCard;
private final StatusLinkClickHandler linkClickHandler;
private final TwidereLinkify linkify;
public DetailStatusViewHolder(StatusAdapter adapter, View itemView) {
super(itemView);
this.linkClickHandler = new StatusLinkClickHandler(adapter.getContext(), null);
this.linkify = new TwidereLinkify(linkClickHandler, false);
this.adapter = adapter;
cardView = (CardView) itemView.findViewById(R.id.card);
menuBar = (ActionMenuView) itemView.findViewById(R.id.menu_bar);
@ -556,6 +566,12 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
profileContainer = itemView.findViewById(R.id.profile_container);
twitterCard = (TwitterCardContainer) itemView.findViewById(R.id.twitter_card);
quoteTextView = (TextView) itemView.findViewById(R.id.quote_text);
quotedNameView = (TextView) itemView.findViewById(R.id.quoted_name);
quotedScreenNameView = (TextView) itemView.findViewById(R.id.quoted_screen_name);
quotedNameContainer = itemView.findViewById(R.id.quoted_name_container);
quoteIndicator = (ForegroundColorView) itemView.findViewById(R.id.quote_indicator);
setIsRecyclable(false);
initViews();
}
@ -568,6 +584,8 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
final MediaLoaderWrapper loader = adapter.getImageLoader();
final boolean nameFirst = adapter.isNameFirst();
linkClickHandler.setStatus(status);
if (status.retweet_id > 0) {
final String retweetedBy = UserColorNameUtils.getDisplayName(context, status.retweeted_by_id,
status.retweeted_by_name, status.retweeted_by_screen_name, nameFirst);
@ -578,13 +596,36 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
retweetedByContainer.setVisibility(View.GONE);
}
nameView.setText(getUserNickname(context, status.user_id, status.user_name, true));
screenNameView.setText("@" + status.user_screen_name);
if (status.is_quote) {
quotedNameView.setText(getUserNickname(context, status.user_id, status.user_name, true));
quotedScreenNameView.setText("@" + status.user_screen_name);
nameView.setText(getUserNickname(context, status.quoted_by_user_id, status.quoted_by_user_name, true));
screenNameView.setText("@" + status.quoted_by_user_screen_name);
quoteTextView.setText(Html.fromHtml(status.quote_text_html));
linkify.applyAllLinks(quoteTextView, status.account_id, getLayoutPosition(),
status.is_possibly_sensitive, adapter.getLinkHighlightingStyle());
timeSourceView.setMovementMethod(LinkMovementMethod.getInstance());
loader.displayProfileImage(profileImageView, status.quoted_by_user_profile_image);
quotedNameContainer.setVisibility(View.VISIBLE);
quoteTextView.setVisibility(View.VISIBLE);
quoteIndicator.setVisibility(View.VISIBLE);
} else {
nameView.setText(getUserNickname(context, status.user_id, status.user_name, true));
screenNameView.setText("@" + status.user_screen_name);
loader.displayProfileImage(profileImageView, status.user_profile_image_url);
quotedNameContainer.setVisibility(View.GONE);
quoteTextView.setVisibility(View.GONE);
quoteIndicator.setVisibility(View.GONE);
}
textView.setText(Html.fromHtml(status.text_html));
final StatusLinkClickHandler linkClickHandler = new StatusLinkClickHandler(context, null);
linkClickHandler.setStatus(status);
final TwidereLinkify linkify = new TwidereLinkify(linkClickHandler, VALUE_LINK_HIGHLIGHT_OPTION_CODE_BOTH, false);
linkify.applyAllLinks(textView, status.account_id, getAdapterPosition(), status.is_possibly_sensitive);
ThemeUtils.applyParagraphSpacing(textView, 1.1f);
@ -621,8 +662,6 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
retweetsCountView.setText(getLocalizedNumber(locale, status.retweet_count));
favoritesCountView.setText(getLocalizedNumber(locale, status.favorite_count));
loader.displayProfileImage(profileImageView, status.user_profile_image_url);
final int typeIconRes = getUserTypeIconRes(status.user_is_verified, status.user_is_protected);
final int typeDescriptionRes = Utils.getUserTypeDescriptionRes(status.user_is_verified, status.user_is_protected);
if (typeIconRes != 0 && typeDescriptionRes != 0) {
@ -664,8 +703,6 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
ft.commit();
} else {
twitterCard.setVisibility(View.GONE);
final FragmentManager fm = fragment.getChildFragmentManager();
// final FragmentTransaction ft = fm.beginTransaction();
}
Utils.setMenuForStatus(context, menuBar.getMenu(), status, adapter.getStatusAccount());
@ -741,61 +778,76 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
final float defaultTextSize = adapter.getTextSize();
nameView.setTextSize(defaultTextSize * 1.25f);
quotedNameView.setTextSize(defaultTextSize * 1.25f);
textView.setTextSize(defaultTextSize * 1.25f);
quoteTextView.setTextSize(defaultTextSize * 1.25f);
screenNameView.setTextSize(defaultTextSize * 0.85f);
quotedScreenNameView.setTextSize(defaultTextSize * 0.85f);
locationView.setTextSize(defaultTextSize * 0.85f);
timeSourceView.setTextSize(defaultTextSize * 0.85f);
textView.setMovementMethod(StatusContentMovementMethod.getInstance());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
textView.setCustomSelectionActionModeCallback(new Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
final FragmentActivity activity = fragment.getActivity();
if (activity instanceof IThemedActivity) {
final int themeRes = ((IThemedActivity) activity).getCurrentThemeResourceId();
final int accentColor = ((IThemedActivity) activity).getCurrentThemeColor();
ThemeUtils.applySupportActionModeBackground(mode, fragment.getActivity(), themeRes, accentColor, true);
}
mode.getMenuInflater().inflate(R.menu.action_status_text_selection, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
final int start = textView.getSelectionStart(), end = textView.getSelectionEnd();
final SpannableString string = SpannableString.valueOf(textView.getText());
final URLSpan[] spans = string.getSpans(start, end, URLSpan.class);
final boolean avail = spans.length == 1 && URLUtil.isValidUrl(spans[0].getURL());
Utils.setMenuItemAvailability(menu, android.R.id.copyUrl, avail);
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case android.R.id.copyUrl: {
final int start = textView.getSelectionStart(), end = textView.getSelectionEnd();
final SpannableString string = SpannableString.valueOf(textView.getText());
final URLSpan[] spans = string.getSpans(start, end, URLSpan.class);
if (spans.length != 1) return true;
ClipboardUtils.setText(activity, spans[0].getURL());
mode.finish();
return true;
}
}
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
});
quoteTextView.setCustomSelectionActionModeCallback(new StatusActionModeCallback(quoteTextView, fragment, activity));
textView.setCustomSelectionActionModeCallback(new StatusActionModeCallback(textView, fragment, activity));
}
}
private static class StatusActionModeCallback implements Callback {
private final TextView textView;
private final StatusFragment fragment;
private final FragmentActivity activity;
public StatusActionModeCallback(TextView textView, StatusFragment fragment, FragmentActivity activity) {
this.textView = textView;
this.fragment = fragment;
this.activity = activity;
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
final FragmentActivity activity = fragment.getActivity();
if (activity instanceof IThemedActivity) {
final int themeRes = ((IThemedActivity) activity).getCurrentThemeResourceId();
final int accentColor = ((IThemedActivity) activity).getCurrentThemeColor();
ThemeUtils.applySupportActionModeBackground(mode, fragment.getActivity(), themeRes, accentColor, true);
}
mode.getMenuInflater().inflate(R.menu.action_status_text_selection, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
final int start = textView.getSelectionStart(), end = textView.getSelectionEnd();
final SpannableString string = SpannableString.valueOf(textView.getText());
final URLSpan[] spans = string.getSpans(start, end, URLSpan.class);
final boolean avail = spans.length == 1 && URLUtil.isValidUrl(spans[0].getURL());
Utils.setMenuItemAvailability(menu, android.R.id.copyUrl, avail);
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case android.R.id.copyUrl: {
final int start = textView.getSelectionStart(), end = textView.getSelectionEnd();
final SpannableString string = SpannableString.valueOf(textView.getText());
final URLSpan[] spans = string.getSpans(start, end, URLSpan.class);
if (spans.length != 1) return true;
ClipboardUtils.setText(activity, spans[0].getURL());
mode.finish();
return true;
}
}
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
}
}
static class LoadConversationTask extends AsyncTask<ParcelableStatus, ParcelableStatus,

View File

@ -110,6 +110,11 @@ public final class TwidereLinkify implements Constants {
this(listener, highlightOption, true);
}
public TwidereLinkify(final OnLinkClickListener listener, boolean addMovementMethod) {
this(listener, VALUE_LINK_HIGHLIGHT_OPTION_CODE_BOTH, addMovementMethod);
}
public TwidereLinkify(final OnLinkClickListener listener, final int highlightOption, boolean addMovementMethod) {
mOnLinkClickListener = listener;
mAddMovementMethod = addMovementMethod;

View File

@ -3,6 +3,7 @@ package org.mariotaku.twidere.view;
import android.content.Context;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.text.SpannableString;
import android.util.AttributeSet;
import android.view.MotionEvent;
@ -80,6 +81,31 @@ public class StatusTextView extends ThemedTextView implements IExtendedView {
}
}
@Override
public void setText(CharSequence text, BufferType type) {
if (text == null) {
super.setText(null, type);
return;
}
super.setText(new SafeSpannableStringWrapper(text), type);
}
private static class SafeSpannableStringWrapper extends SpannableString {
public SafeSpannableStringWrapper(CharSequence source) {
super(source);
}
@Override
public void setSpan(Object what, int start, int end, int flags) {
if (start < 0 || end < 0) {
// Silently ignore
return;
}
super.setSpan(what, start, end, flags);
}
}
@Override
public final boolean onTouchEvent(@NonNull final MotionEvent event) {
if (mTouchInterceptor != null) {

View File

@ -44,6 +44,7 @@ import java.util.Locale;
import twitter4j.TranslationResult;
import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
import static org.mariotaku.twidere.util.UserColorNameUtils.getUserNickname;
import static org.mariotaku.twidere.util.Utils.getUserTypeIconRes;
/**
@ -158,10 +159,10 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements Constan
}
if (status.is_quote) {
quotedNameView.setText(status.user_name);
quotedNameView.setText(getUserNickname(context, status.user_id, status.user_name, true));
quotedScreenNameView.setText("@" + status.user_screen_name);
timeView.setTime(status.quote_timestamp);
nameView.setText(status.quoted_by_user_name);
nameView.setText(getUserNickname(context, status.quoted_by_user_id, status.quoted_by_user_name, true));
screenNameView.setText("@" + status.quoted_by_user_screen_name);
if (adapter.getLinkHighlightingStyle() == VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) {
@ -192,7 +193,7 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements Constan
final int userColor = UserColorNameUtils.getUserColor(context, status.quoted_by_user_id);
itemContent.drawStart(userColor);
} else {
nameView.setText(status.user_name);
nameView.setText(getUserNickname(context, status.user_id, status.user_name, true));
screenNameView.setText("@" + status.user_screen_name);
timeView.setTime(status.timestamp);

View File

@ -17,7 +17,7 @@
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout
<RelativeLayout
android:id="@+id/card_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
@ -30,11 +30,11 @@
android:id="@+id/retweeted_by_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/element_spacing_minus_normal"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:paddingLeft="@dimen/element_spacing_small"
android:paddingRight="@dimen/element_spacing_small"
android:layout_marginBottom="@dimen/element_spacing_minus_normal"
tools:visiblity="visible">
<Space
@ -58,6 +58,7 @@
android:id="@+id/profile_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/retweeted_by_container"
android:background="?android:selectableItemBackground"
android:padding="@dimen/element_spacing_small"
app:ignorePadding="true">
@ -79,10 +80,10 @@
android:id="@+id/profile_type"
android:layout_width="@dimen/icon_size_profile_type_detail"
android:layout_height="@dimen/icon_size_profile_type_detail"
android:layout_marginRight="@dimen/element_spacing_minus_small"
android:layout_marginBottom="@dimen/element_spacing_minus_small"
android:layout_alignBottom="@id/profile_image"
android:layout_alignRight="@id/profile_image"
android:layout_marginBottom="@dimen/element_spacing_minus_small"
android:layout_marginRight="@dimen/element_spacing_minus_small"
android:scaleType="fitCenter"
tools:ignore="ContentDescription"/>
@ -161,15 +162,86 @@
</FrameLayout>
</org.mariotaku.twidere.view.ColorLabelRelativeLayout>
<org.mariotaku.twidere.view.StatusTextView
android:id="@+id/quote_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/profile_container"
android:layout_below="@id/profile_container"
android:layout_marginBottom="@dimen/element_spacing_small"
android:layout_marginTop="@dimen/element_spacing_normal"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorPrimary"
android:textIsSelectable="true"
android:visibility="gone"
tools:text="@string/sample_status_text"
tools:visibility="visible"/>
<org.mariotaku.twidere.view.ForegroundColorView
android:id="@+id/quote_indicator"
android:layout_width="@dimen/element_spacing_small"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/counts_container"
android:layout_alignTop="@+id/quoted_name_container"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:background="?android:dividerHorizontal"
android:visibility="gone"
tools:visibility="visible"/>
<LinearLayout
android:id="@+id/quoted_name_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/quote_text"
android:layout_toRightOf="@+id/quote_indicator"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal"
android:visibility="gone"
tools:visibility="visible">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/quoted_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:textColorPrimary"
tools:text="Name"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/quoted_screen_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_small"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
tools:text="\@username"/>
</LinearLayout>
<include
android:id="@+id/media_preview_container"
layout="@layout/layout_media_preview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/quoted_name_container"
android:layout_toRightOf="@+id/quote_indicator"
android:visibility="gone"/>
<org.mariotaku.twidere.view.StatusTextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/media_preview_container"
android:layout_margin="@dimen/element_spacing_normal"
android:layout_toRightOf="@+id/quote_indicator"
android:singleLine="false"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorPrimary"
@ -180,6 +252,8 @@
android:id="@+id/twitter_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/text"
android:layout_toRightOf="@+id/quote_indicator"
android:visibility="gone"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
@ -187,6 +261,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="@+id/twitter_card"
android:layout_toRightOf="@+id/quote_indicator"
android:background="?android:selectableItemBackground"
android:drawableLeft="@drawable/ic_indicator_location"
android:drawablePadding="4dp"
@ -194,14 +270,17 @@
android:padding="@dimen/element_spacing_normal"
android:singleLine="true"
android:text="@string/view_map"
android:textColor="?android:textColorSecondary"
android:textAppearance="?android:attr/textAppearanceSmall"/>
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
<LinearLayout
android:id="@+id/counts_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/location_view"
android:layout_marginLeft="@dimen/element_spacing_small"
android:layout_marginRight="@dimen/element_spacing_small"
android:layout_toRightOf="@+id/quote_indicator"
android:orientation="horizontal"
android:splitMotionEvents="false">
@ -300,6 +379,7 @@
<org.mariotaku.twidere.view.TwidereActionMenuView
android:id="@+id/menu_bar"
android:layout_width="match_parent"
android:layout_height="?android:actionBarSize"/>
android:layout_height="?android:actionBarSize"
android:layout_below="@+id/counts_container"/>
</LinearLayout>
</RelativeLayout>

View File

@ -19,7 +19,6 @@
-->
<FrameLayout
android:id="@+id/media_preview_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">