implemented real aspect ratio option for media preview, close #700

This commit is contained in:
Mariotaku Lee 2017-02-06 15:24:33 +08:00
parent 23e5eecf1b
commit ab5562d985
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
22 changed files with 436 additions and 408 deletions

View File

@ -50,12 +50,9 @@ public interface SharedPreferenceConstants {
String VALUE_MEDIA_PREVIEW_STYLE_CROP = "crop";
String VALUE_MEDIA_PREVIEW_STYLE_SCALE = "scale";
String VALUE_MEDIA_PREVIEW_STYLE_REAL_SIZE = "real_size";
String VALUE_MEDIA_PREVIEW_STYLE_NONE = VALUE_NONE;
int VALUE_MEDIA_PREVIEW_STYLE_CODE_CROP = 1;
int VALUE_MEDIA_PREVIEW_STYLE_CODE_SCALE = 2;
int VALUE_MEDIA_PREVIEW_STYLE_CODE_NONE = 0;
int VALUE_NOTIFICATION_FLAG_NONE = 0x0;
int VALUE_NOTIFICATION_FLAG_RINGTONE = 0x1;
int VALUE_NOTIFICATION_FLAG_VIBRATION = 0x2;

View File

@ -0,0 +1,20 @@
package org.mariotaku.twidere.annotation;
import android.support.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Created by mariotaku on 2017/2/6.
*/
@IntDef({PreviewStyle.NONE, PreviewStyle.SCALE, PreviewStyle.CROP, PreviewStyle.REAL_SIZE})
@Retention(RetentionPolicy.SOURCE)
public @interface PreviewStyle {
int NONE = 0;
int CROP = 1;
int SCALE = 2;
int REAL_SIZE = 3;
}

View File

@ -115,7 +115,6 @@ import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries;
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
import org.mariotaku.twidere.view.CardMediaContainer.PreviewStyle;
import org.mariotaku.twidere.view.TabPagerIndicator;
import java.io.Closeable;
@ -640,14 +639,6 @@ public final class Utils implements Constants {
return url;
}
@PreviewStyle
public static int getMediaPreviewStyle(String style) {
if (VALUE_MEDIA_PREVIEW_STYLE_SCALE.equalsIgnoreCase(style)) {
return VALUE_MEDIA_PREVIEW_STYLE_CODE_SCALE;
}
return VALUE_MEDIA_PREVIEW_STYLE_CODE_CROP;
}
public static String getQuoteStatus(final Context context, final ParcelableStatus status) {
if (context == null) return null;
String quoteFormat = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE).getString(

View File

@ -1,341 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import org.apache.commons.lang3.ObjectUtils;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.util.ParcelableMediaUtils;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.MediaLoadingHandler;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
/**
* Dynamic layout for media preview
* Created by mariotaku on 14/12/17.
*/
public class CardMediaContainer extends ViewGroup implements Constants {
private static final float WIDTH_HEIGHT_RATIO = 0.5f;
private final int mHorizontalSpacing, mVerticalSpacing;
private int[] mTempIndices;
private int mMediaPreviewStyle;
public CardMediaContainer(Context context) {
this(context, null);
}
public CardMediaContainer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CardMediaContainer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CardMediaContainer);
mHorizontalSpacing = a.getDimensionPixelSize(R.styleable.CardMediaContainer_android_horizontalSpacing, 0);
mVerticalSpacing = a.getDimensionPixelSize(R.styleable.CardMediaContainer_android_verticalSpacing, 0);
a.recycle();
}
public void displayMedia(@NonNull final int... imageRes) {
for (int i = 0, j = getChildCount(), k = imageRes.length; i < j; i++) {
final View child = getChildAt(i);
final ImageView imageView = (ImageView) child.findViewById(R.id.mediaPreview);
final View progress = child.findViewById(R.id.media_preview_progress);
progress.setVisibility(GONE);
if (i < k) {
imageView.setImageResource(imageRes[i]);
child.setVisibility(VISIBLE);
} else {
imageView.setImageDrawable(null);
child.setVisibility(GONE);
}
}
}
public void displayMedia(@Nullable final ParcelableMedia[] mediaArray,
@NonNull final MediaLoaderWrapper loader,
final UserKey accountId, final long extraId,
@Nullable final OnMediaClickListener mediaClickListener,
@Nullable final MediaLoadingHandler loadingHandler) {
displayMedia(loader, mediaArray, accountId, mediaClickListener, loadingHandler, extraId, false);
}
public void displayMedia(@NonNull final MediaLoaderWrapper loader,
@Nullable final ParcelableMedia[] mediaArray, final UserKey accountId,
@Nullable final OnMediaClickListener mediaClickListener,
@Nullable final MediaLoadingHandler loadingHandler,
final long extraId, boolean withCredentials) {
if (mediaArray == null || mMediaPreviewStyle == VALUE_MEDIA_PREVIEW_STYLE_CODE_NONE) {
for (int i = 0, j = getChildCount(); i < j; i++) {
final View child = getChildAt(i);
child.setTag(null);
child.setVisibility(GONE);
}
return;
}
final View.OnClickListener clickListener = new ImageGridClickListener(mediaClickListener,
accountId, extraId);
for (int i = 0, j = getChildCount(), k = mediaArray.length; i < j; i++) {
final View child = getChildAt(i);
if (mediaClickListener != null) {
child.setOnClickListener(clickListener);
}
final ImageView imageView = (ImageView) child.findViewById(R.id.mediaPreview);
switch (mMediaPreviewStyle) {
case VALUE_MEDIA_PREVIEW_STYLE_CODE_CROP: {
imageView.setScaleType(ScaleType.CENTER_CROP);
break;
}
case VALUE_MEDIA_PREVIEW_STYLE_CODE_SCALE: {
imageView.setScaleType(ScaleType.FIT_CENTER);
break;
}
}
if (i < k) {
final ParcelableMedia media = mediaArray[i];
final String url = TextUtils.isEmpty(media.preview_url) ? media.media_url : media.preview_url;
if (ObjectUtils.notEqual(url, imageView.getTag()) || imageView.getDrawable() == null) {
if (withCredentials) {
loader.displayPreviewImageWithCredentials(imageView, url, accountId, loadingHandler);
} else {
loader.displayPreviewImage(imageView, url, loadingHandler);
}
}
imageView.setTag(url);
if (imageView instanceof MediaPreviewImageView) {
((MediaPreviewImageView) imageView).setHasPlayIcon(ParcelableMediaUtils.hasPlayIcon(media.type));
}
if (TextUtils.isEmpty(media.alt_text)) {
child.setContentDescription(getContext().getString(R.string.media));
} else {
child.setContentDescription(media.alt_text);
}
child.setTag(media);
child.setVisibility(VISIBLE);
} else {
loader.cancelDisplayTask(imageView);
imageView.setTag(null);
child.setVisibility(GONE);
}
}
}
public void setStyle(int style) {
mMediaPreviewStyle = style;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int[] childIndices = createChildIndices();
final int childCount = getChildIndicesInLayout(this, childIndices);
if (childCount > 0) {
if (childCount == 1) {
layout1Media(childIndices);
} else if (childCount == 3) {
layout3Media(mHorizontalSpacing, mVerticalSpacing, childIndices);
} else {
layoutGridMedia(childCount, 2, mHorizontalSpacing, mVerticalSpacing, childIndices);
}
}
}
private void measure1Media(int contentWidth, int[] childIndices, float ratioMultiplier) {
final View child = getChildAt(childIndices[0]);
final int childHeight = Math.round(contentWidth * WIDTH_HEIGHT_RATIO * ratioMultiplier);
final int widthSpec = MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY);
final int heightSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY);
child.measure(widthSpec, heightSpec);
}
private void layout1Media(int[] childIndices) {
final View child = getChildAt(childIndices[0]);
final int left = getPaddingLeft(), top = getPaddingTop();
final int right = left + child.getMeasuredWidth(), bottom = top + child.getMeasuredHeight();
child.layout(left, top, right, bottom);
}
private int measureGridMedia(int childCount, int columnCount, int contentWidth,
float widthHeightRatio, int horizontalSpacing, int verticalSpacing,
int[] childIndices) {
final int childWidth = (contentWidth - horizontalSpacing * (columnCount - 1)) / columnCount;
final int childHeight = Math.round(childWidth * widthHeightRatio);
final int widthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
final int heightSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY);
for (int i = 0; i < childCount; i++) {
getChildAt(childIndices[i]).measure(widthSpec, heightSpec);
}
final int rowsCount = (int) Math.ceil(childCount / (double) columnCount);
return rowsCount * childHeight + (rowsCount - 1) * verticalSpacing;
}
private void layoutGridMedia(int childCount, int columnCount, int horizontalSpacing,
int verticalSpacing, int[] childIndices) {
final int initialLeft = getPaddingLeft();
int left = initialLeft, top = getPaddingTop();
for (int i = 0; i < childCount; i++) {
final int colIdx = i % columnCount;
final View child = getChildAt(childIndices[i]);
child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
if (colIdx == columnCount - 1) {
// Last item in this row, set top of next row to last view bottom + verticalSpacing
top = child.getBottom() + verticalSpacing;
// And reset left to initial left
left = initialLeft;
} else {
// The left of next item is right + horizontalSpacing of previous item
left = child.getRight() + horizontalSpacing;
}
}
}
private void measure3Media(int contentWidth, int horizontalSpacing, int[] childIndices, float ratioMultiplier) {
final View child0 = getChildAt(childIndices[0]), child1 = getChildAt(childIndices[1]),
child2 = getChildAt(childIndices[2]);
final int childWidth = (contentWidth - horizontalSpacing) / 2;
final int childLeftHeightSpec = MeasureSpec.makeMeasureSpec(Math.round(childWidth * ratioMultiplier), MeasureSpec.EXACTLY);
final int widthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
child0.measure(widthSpec, childLeftHeightSpec);
final int childRightHeight = Math.round((childWidth - horizontalSpacing) / 2 * ratioMultiplier);
final int childRightHeightSpec = MeasureSpec.makeMeasureSpec(childRightHeight, MeasureSpec.EXACTLY);
child1.measure(widthSpec, childRightHeightSpec);
child2.measure(widthSpec, childRightHeightSpec);
}
private void layout3Media(int horizontalSpacing, int verticalSpacing, int[] childIndices) {
final int left = getPaddingLeft(), top = getPaddingTop();
final View child0 = getChildAt(childIndices[0]), child1 = getChildAt(childIndices[1]),
child2 = getChildAt(childIndices[2]);
child0.layout(left, top, left + child0.getMeasuredWidth(), top + child0.getMeasuredHeight());
final int rightColLeft = child0.getRight() + horizontalSpacing;
child1.layout(rightColLeft, top, rightColLeft + child1.getMeasuredWidth(),
top + child1.getMeasuredHeight());
final int child2Top = child1.getBottom() + verticalSpacing;
child2.layout(rightColLeft, child2Top, rightColLeft + child2.getMeasuredWidth(),
child2Top + child2.getMeasuredHeight());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int measuredWidth = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int contentWidth = measuredWidth - getPaddingLeft() - getPaddingRight();
float ratioMultiplier = 1;
int contentHeight = -1;
if (getLayoutParams().height != LayoutParams.WRAP_CONTENT) {
final int measuredHeight = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
ratioMultiplier = contentWidth > 0 ? measuredHeight / (contentWidth * WIDTH_HEIGHT_RATIO) : 1;
contentHeight = contentWidth;
}
final int[] childIndices = createChildIndices();
final int childCount = getChildIndicesInLayout(this, childIndices);
int heightSum = 0;
if (childCount > 0) {
if (childCount == 1) {
measure1Media(contentWidth, childIndices, ratioMultiplier);
heightSum = Math.round(contentWidth * WIDTH_HEIGHT_RATIO * ratioMultiplier);
} else if (childCount == 2) {
measureGridMedia(childCount, 2, contentWidth, ratioMultiplier, mHorizontalSpacing,
mVerticalSpacing, childIndices);
heightSum = Math.round(contentWidth * WIDTH_HEIGHT_RATIO * ratioMultiplier);
} else if (childCount == 3) {
measure3Media(contentWidth, mHorizontalSpacing, childIndices, ratioMultiplier);
heightSum = Math.round(contentWidth * WIDTH_HEIGHT_RATIO * ratioMultiplier);
} else {
heightSum = measureGridMedia(childCount, 2, contentWidth,
WIDTH_HEIGHT_RATIO * ratioMultiplier, mHorizontalSpacing, mVerticalSpacing, childIndices);
}
if (contentHeight > 0) {
heightSum = contentHeight;
}
} else if (contentHeight > 0) {
heightSum = contentHeight;
}
final int height = heightSum + getPaddingTop() + getPaddingBottom();
setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
private int[] createChildIndices() {
if (mTempIndices == null || mTempIndices.length < getChildCount()) {
return mTempIndices = new int[getChildCount()];
}
return mTempIndices;
}
private static int getChildIndicesInLayout(ViewGroup viewGroup, int[] indices) {
final int childCount = viewGroup.getChildCount();
int indicesCount = 0;
for (int i = 0; i < childCount; i++) {
if (viewGroup.getChildAt(i).getVisibility() != GONE) {
indices[indicesCount++] = i;
}
}
return indicesCount;
}
public interface OnMediaClickListener {
void onMediaClick(View view, ParcelableMedia media, UserKey accountKey, long id);
}
private static class ImageGridClickListener implements View.OnClickListener {
private final WeakReference<OnMediaClickListener> mListenerRef;
private final UserKey mAccountKey;
private final long mExtraId;
ImageGridClickListener(@Nullable final OnMediaClickListener listener, final UserKey accountKey,
final long extraId) {
mListenerRef = new WeakReference<>(listener);
mAccountKey = accountKey;
mExtraId = extraId;
}
@Override
public void onClick(final View v) {
final OnMediaClickListener listener = mListenerRef.get();
if (listener == null) return;
listener.onMediaClick(v, (ParcelableMedia) v.getTag(), mAccountKey, mExtraId);
}
}
@IntDef({VALUE_MEDIA_PREVIEW_STYLE_CODE_SCALE, VALUE_MEDIA_PREVIEW_STYLE_CODE_CROP})
@Retention(RetentionPolicy.SOURCE)
public @interface PreviewStyle {
}
}

View File

@ -26,8 +26,9 @@ import android.support.v4.widget.SimpleCursorAdapter
import android.text.TextUtils
import android.view.View
import android.view.ViewGroup
import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_MEDIA_PREVIEW_STYLE
import org.mariotaku.twidere.constant.mediaPreviewStyleKey
import org.mariotaku.twidere.extension.model.getActionName
import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.model.DraftCursorIndices
@ -56,7 +57,7 @@ class DraftsAdapter(context: Context) : SimpleCursorAdapter(context, R.layout.li
init {
GeneralComponentHelper.build(context).inject(this)
mediaLoadingHandler = MediaLoadingHandler(R.id.media_preview_progress)
mediaPreviewStyle = Utils.getMediaPreviewStyle(preferences.getString(KEY_MEDIA_PREVIEW_STYLE, null))
mediaPreviewStyle = preferences[mediaPreviewStyleKey]
}
override fun bindView(view: View, context: Context, cursor: Cursor) {
@ -68,14 +69,14 @@ class DraftsAdapter(context: Context) : SimpleCursorAdapter(context, R.layout.li
val timestamp = draft.timestamp
val actionType: String = draft.action_type ?: Draft.Action.UPDATE_STATUS
val actionName = draft.getActionName(context)
holder.media_preview_container.setStyle(mediaPreviewStyle)
holder.media_preview_container.style = mediaPreviewStyle
when (actionType) {
Draft.Action.UPDATE_STATUS, Draft.Action.UPDATE_STATUS_COMPAT_1,
Draft.Action.UPDATE_STATUS_COMPAT_2, Draft.Action.REPLY, Draft.Action.QUOTE -> {
val media = ParcelableMediaUtils.fromMediaUpdates(mediaUpdates)
holder.media_preview_container.visibility = View.VISIBLE
holder.media_preview_container.displayMedia(media, imageLoader, null, -1, null,
mediaLoadingHandler)
holder.media_preview_container.displayMedia(loader = imageLoader, media = media,
loadingHandler = mediaLoadingHandler)
}
else -> {
holder.media_preview_container.visibility = View.GONE

View File

@ -29,21 +29,17 @@ import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.iface.IDirectMessagesAdapter
import org.mariotaku.twidere.constant.*
import org.mariotaku.twidere.constant.displaySensitiveContentsKey
import org.mariotaku.twidere.constant.mediaPreviewStyleKey
import org.mariotaku.twidere.constant.newDocumentApiKey
import org.mariotaku.twidere.model.ParcelableDirectMessage
import org.mariotaku.twidere.model.ParcelableDirectMessageCursorIndices
import org.mariotaku.twidere.model.ParcelableMedia
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.util.DirectMessageOnLinkClickHandler
import org.mariotaku.twidere.util.IntentUtils
import org.mariotaku.twidere.util.MediaLoadingHandler
import org.mariotaku.twidere.util.ThemeUtils
import org.mariotaku.twidere.util.TwidereLinkify
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.view.CardMediaContainer
import org.mariotaku.twidere.view.holder.IncomingMessageViewHolder
import org.mariotaku.twidere.view.holder.MessageViewHolder
import java.lang.ref.WeakReference
class MessageConversationAdapter(context: Context) : BaseRecyclerViewAdapter<ViewHolder>(context), Constants, IDirectMessagesAdapter {
@ -51,7 +47,7 @@ class MessageConversationAdapter(context: Context) : BaseRecyclerViewAdapter<Vie
ThemeUtils.getThemeBackgroundOption(context), ThemeUtils.getUserThemeBackgroundAlpha(context))
private val incomingMessageColor: Int = ThemeUtils.getUserAccentColor(context)
override val mediaPreviewStyle: Int = Utils.getMediaPreviewStyle(preferences.getString(SharedPreferenceConstants.KEY_MEDIA_PREVIEW_STYLE, null))
override val mediaPreviewStyle: Int = preferences[mediaPreviewStyleKey]
private val inflater: LayoutInflater = LayoutInflater.from(context)
val mediaLoadingHandler: MediaLoadingHandler = MediaLoadingHandler(R.id.media_preview_progress)
@ -151,9 +147,9 @@ class MessageConversationAdapter(context: Context) : BaseRecyclerViewAdapter<Vie
this.adapterRef = WeakReference(adapter)
}
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey, extraId: Long) {
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey?, id: Long) {
val adapter = adapterRef.get()
IntentUtils.openMedia(adapter.context, adapter.getDirectMessage(extraId.toInt())!!, media,
IntentUtils.openMedia(adapter.context, adapter.getDirectMessage(id.toInt())!!, media,
adapter.preferences[newDocumentApiKey], adapter.preferences[displaySensitiveContentsKey])
}

View File

@ -37,6 +37,7 @@ import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.Companion.ITEM_VIEW_TYPE_LOAD_INDICATOR
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter
import org.mariotaku.twidere.annotation.PreviewStyle
import org.mariotaku.twidere.constant.*
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_DISPLAY_SENSITIVE_CONTENTS
import org.mariotaku.twidere.model.*
@ -63,7 +64,7 @@ abstract class ParcelableStatusesAdapter(
override final val mediaLoadingHandler: MediaLoadingHandler
final override val twidereLinkify: TwidereLinkify
@CardMediaContainer.PreviewStyle
@PreviewStyle
final override val mediaPreviewStyle: Int = preferences[mediaPreviewStyleKey]
final override val nameFirst: Boolean = preferences[nameFirstKey]
final override val useStarsForLikes: Boolean = preferences[iWantMyStarsBackKey]

View File

@ -107,7 +107,7 @@ class StaggeredGridParcelableStatusesAdapter(context: Context) : ParcelableStatu
return false
}
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey, extraId: Long) {
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey?, id: Long) {
}
override fun setStatusClickListener(listener: IStatusViewHolder.StatusClickListener?) {

View File

@ -19,6 +19,7 @@
package org.mariotaku.twidere.adapter.iface
import org.mariotaku.twidere.annotation.PreviewStyle
import org.mariotaku.twidere.model.ParcelableDirectMessage
import org.mariotaku.twidere.util.MediaLoaderWrapper
import org.mariotaku.twidere.view.CardMediaContainer
@ -35,7 +36,7 @@ interface IDirectMessagesAdapter {
@ShapedImageView.ShapeStyle
val profileImageStyle: Int
@CardMediaContainer.PreviewStyle
@PreviewStyle
val mediaPreviewStyle: Int
fun findItem(id: Long): ParcelableDirectMessage?

View File

@ -1,5 +1,6 @@
package org.mariotaku.twidere.adapter.iface
import org.mariotaku.twidere.annotation.PreviewStyle
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.util.MediaLoadingHandler
@ -17,7 +18,7 @@ interface IStatusesAdapter<in Data> : IContentAdapter, IGapSupportedAdapter {
val lightFont: Boolean
@CardMediaContainer.PreviewStyle
@PreviewStyle
val mediaPreviewStyle: Int
val statusCount: Int

View File

@ -9,6 +9,7 @@ import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.*
import org.mariotaku.twidere.TwidereConstants.KEY_MEDIA_PRELOAD
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.constant.SharedPreferenceConstants.VALUE_MEDIA_PREVIEW_STYLE_REAL_SIZE
import org.mariotaku.twidere.extension.getNonEmptyString
import org.mariotaku.twidere.model.CustomAPIConfig
import org.mariotaku.twidere.model.UserKey
@ -16,6 +17,7 @@ import org.mariotaku.twidere.model.account.cred.Credentials
import org.mariotaku.twidere.model.sync.SyncProviderInfo
import org.mariotaku.twidere.preference.ThemeBackgroundPreference
import org.mariotaku.twidere.util.sync.SyncProviderInfoFactory
import org.mariotaku.twidere.annotation.PreviewStyle
import org.mariotaku.twidere.view.ProfileImageView
/**
@ -88,14 +90,21 @@ object profileImageStyleKey : KSimpleKey<Int>(KEY_PROFILE_IMAGE_STYLE, ProfileIm
}
object mediaPreviewStyleKey : KSimpleKey<Int>(KEY_MEDIA_PREVIEW_STYLE, VALUE_MEDIA_PREVIEW_STYLE_CODE_CROP) {
object mediaPreviewStyleKey : KSimpleKey<Int>(KEY_MEDIA_PREVIEW_STYLE, PreviewStyle.CROP) {
override fun read(preferences: SharedPreferences): Int {
if (preferences.getString(key, null) == VALUE_MEDIA_PREVIEW_STYLE_SCALE) return VALUE_MEDIA_PREVIEW_STYLE_CODE_SCALE
return VALUE_MEDIA_PREVIEW_STYLE_CODE_CROP
when (preferences.getString(key, null)) {
VALUE_MEDIA_PREVIEW_STYLE_SCALE -> return PreviewStyle.SCALE
VALUE_MEDIA_PREVIEW_STYLE_REAL_SIZE -> return PreviewStyle.REAL_SIZE
else -> return PreviewStyle.CROP
}
}
override fun write(editor: SharedPreferences.Editor, value: Int): Boolean {
editor.putString(key, if (value == VALUE_MEDIA_PREVIEW_STYLE_CODE_SCALE) VALUE_MEDIA_PREVIEW_STYLE_SCALE else VALUE_MEDIA_PREVIEW_STYLE_CROP)
editor.putString(key, when (value) {
PreviewStyle.SCALE -> VALUE_MEDIA_PREVIEW_STYLE_SCALE
PreviewStyle.REAL_SIZE -> VALUE_MEDIA_PREVIEW_STYLE_REAL_SIZE
else -> VALUE_MEDIA_PREVIEW_STYLE_CROP
})
return true
}

View File

@ -18,4 +18,7 @@ fun parcelableMediaTypeString(@ParcelableMedia.Type type: Int): String? {
}
}
val ParcelableMedia.aspect_ratio: Double get() = this.width / this.height.toDouble()
val ParcelableMedia.aspect_ratio: Double get() {
if (this.height <= 0 || this.width <= 0) return Double.NaN
return this.width / this.height.toDouble()
}

View File

@ -342,7 +342,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
null)
}
override fun onMediaClick(view: View, media: ParcelableMedia?, accountKey: UserKey, extraId: Long) {
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey?, id: Long) {
val status = adapter.status
if (status == null || media == null) return
IntentUtils.openMediaDirectly(activity, accountKey, status, media, preferences.getBoolean(KEY_NEW_DOCUMENT_API),
@ -883,8 +883,8 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
} else if (adapter.isDetailMediaExpanded) {
itemView.quotedMediaLabel.visibility = View.GONE
itemView.quotedMediaPreview.visibility = View.VISIBLE
itemView.quotedMediaPreview.displayMedia(quotedMedia, loader, status.account_key, -1,
adapter.fragment, null)
itemView.quotedMediaPreview.displayMedia(loader = loader, media = quotedMedia,
accountId = status.account_key, mediaClickListener = adapter.fragment)
} else {
itemView.quotedMediaLabel.visibility = View.VISIBLE
itemView.quotedMediaPreview.visibility = View.GONE
@ -1014,8 +1014,9 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
itemView.mediaPreviewContainer.visibility = View.VISIBLE
itemView.mediaPreview.visibility = View.VISIBLE
itemView.mediaPreviewLoad.visibility = View.GONE
itemView.mediaPreview.displayMedia(media, loader, status.account_key, -1,
adapter.fragment, adapter.mediaLoadingHandler)
itemView.mediaPreview.displayMedia(loader = loader, media = media,
accountId = status.account_key, mediaClickListener = adapter.fragment,
loadingHandler = adapter.mediaLoadingHandler)
} else {
itemView.mediaPreviewContainer.visibility = View.VISIBLE
itemView.mediaPreview.visibility = View.GONE
@ -1182,8 +1183,8 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
itemView.name.nameFirst = adapter.nameFirst
itemView.quotedName.nameFirst = adapter.nameFirst
itemView.mediaPreview.setStyle(adapter.mediaPreviewStyle)
itemView.quotedMediaPreview.setStyle(adapter.mediaPreviewStyle)
itemView.mediaPreview.style = adapter.mediaPreviewStyle
itemView.quotedMediaPreview.style = adapter.mediaPreviewStyle
itemView.text.customSelectionActionModeCallback = StatusActionModeCallback(itemView.text, activity)
itemView.profileImage.style = adapter.profileImageStyle

View File

@ -0,0 +1,341 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view
import android.content.Context
import android.text.TextUtils
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ImageView.ScaleType
import org.apache.commons.lang3.ObjectUtils
import org.mariotaku.twidere.R
import org.mariotaku.twidere.annotation.PreviewStyle
import org.mariotaku.twidere.extension.model.aspect_ratio
import org.mariotaku.twidere.model.ParcelableMedia
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.ParcelableMediaUtils
import org.mariotaku.twidere.util.MediaLoaderWrapper
import org.mariotaku.twidere.util.MediaLoadingHandler
import java.lang.ref.WeakReference
/**
* Dynamic layout for media preview
* Created by mariotaku on 14/12/17.
*/
class CardMediaContainer(context: Context, attrs: AttributeSet? = null) : ViewGroup(context, attrs) {
private val horizontalSpacing: Int
private val verticalSpacing: Int
private var tempIndices: IntArray? = null
@PreviewStyle
var style: Int = PreviewStyle.NONE
init {
val a = context.obtainStyledAttributes(attrs, R.styleable.CardMediaContainer)
horizontalSpacing = a.getDimensionPixelSize(R.styleable.CardMediaContainer_android_horizontalSpacing, 0)
verticalSpacing = a.getDimensionPixelSize(R.styleable.CardMediaContainer_android_verticalSpacing, 0)
a.recycle()
}
fun displayMedia(vararg imageRes: Int) {
val k = imageRes.size
for (i in 0 until childCount) {
val child = getChildAt(i)
val imageView = child.findViewById(R.id.mediaPreview) as ImageView
val progress = child.findViewById(R.id.media_preview_progress)
progress.visibility = View.GONE
if (i < k) {
imageView.setImageResource(imageRes[i])
child.visibility = View.VISIBLE
} else {
imageView.setImageDrawable(null)
child.visibility = View.GONE
}
}
}
fun displayMedia(loader: MediaLoaderWrapper, media: Array<ParcelableMedia>?, accountId: UserKey? = null,
extraId: Long = -1, withCredentials: Boolean = false,
mediaClickListener: OnMediaClickListener? = null, loadingHandler: MediaLoadingHandler? = null) {
if (media == null || style == PreviewStyle.NONE) {
for (i in 0 until childCount) {
val child = getChildAt(i)
child.visibility = View.GONE
}
return
}
val clickListener = ImageGridClickListener(mediaClickListener, accountId, extraId)
val mediaSize = media.size
for (i in 0 until childCount) {
val child = getChildAt(i)
if (mediaClickListener != null) {
child.setOnClickListener(clickListener)
}
val imageView = child.findViewById(R.id.mediaPreview) as ImageView
when (style) {
PreviewStyle.REAL_SIZE, PreviewStyle.CROP -> {
imageView.scaleType = ScaleType.CENTER_CROP
}
PreviewStyle.SCALE -> {
imageView.scaleType = ScaleType.FIT_CENTER
}
}
if (i < mediaSize) {
val item = media[i]
val url = if (TextUtils.isEmpty(item.preview_url)) item.media_url else item.preview_url
if (ObjectUtils.notEqual(url, imageView.tag) || imageView.drawable == null) {
if (withCredentials) {
loader.displayPreviewImageWithCredentials(imageView, url, accountId, loadingHandler)
} else {
loader.displayPreviewImage(imageView, url, loadingHandler)
}
}
imageView.tag = url
if (imageView is MediaPreviewImageView) {
imageView.setHasPlayIcon(ParcelableMediaUtils.hasPlayIcon(item.type))
}
if (TextUtils.isEmpty(item.alt_text)) {
child.contentDescription = context.getString(R.string.media)
} else {
child.contentDescription = item.alt_text
}
(child.layoutParams as MediaLayoutParams).media = item
child.visibility = View.VISIBLE
} else {
loader.cancelDisplayTask(imageView)
imageView.tag = null
child.visibility = View.GONE
}
}
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
val childIndices = createChildIndices()
val childCount = getChildIndicesInLayout(this, childIndices)
if (childCount > 0) {
if (childCount == 1) {
layout1Media(childIndices)
} else if (childCount == 3) {
layout3Media(horizontalSpacing, verticalSpacing, childIndices)
} else {
layoutGridMedia(childCount, 2, horizontalSpacing, verticalSpacing, childIndices)
}
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val measuredWidth = View.resolveSize(suggestedMinimumWidth, widthMeasureSpec)
val contentWidth = measuredWidth - paddingLeft - paddingRight
var ratioMultiplier = 1f
var contentHeight = -1
if (layoutParams.height != ViewGroup.LayoutParams.WRAP_CONTENT) {
val measuredHeight = View.resolveSize(suggestedMinimumWidth, widthMeasureSpec)
ratioMultiplier = if (contentWidth > 0) measuredHeight / (contentWidth * WIDTH_HEIGHT_RATIO) else 1f
contentHeight = contentWidth
}
val childIndices = createChildIndices()
val childCount = getChildIndicesInLayout(this, childIndices)
var heightSum = 0
if (childCount > 0) {
if (childCount == 1) {
heightSum = measure1Media(contentWidth, childIndices, ratioMultiplier)
} else if (childCount == 2) {
heightSum = measureGridMedia(childCount, 2, contentWidth, ratioMultiplier, horizontalSpacing,
verticalSpacing, childIndices)
} else if (childCount == 3) {
heightSum = measure3Media(contentWidth, horizontalSpacing, childIndices, ratioMultiplier)
} else {
heightSum = measureGridMedia(childCount, 2, contentWidth,
WIDTH_HEIGHT_RATIO * ratioMultiplier, horizontalSpacing, verticalSpacing, childIndices)
}
if (contentHeight > 0) {
heightSum = contentHeight
}
} else if (contentHeight > 0) {
heightSum = contentHeight
}
val height = heightSum + paddingTop + paddingBottom
setMeasuredDimension(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))
}
override fun checkLayoutParams(p: LayoutParams?): Boolean {
return p is MediaLayoutParams
}
override fun generateDefaultLayoutParams(): LayoutParams {
return MediaLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
}
override fun generateLayoutParams(attrs: AttributeSet): LayoutParams {
return MediaLayoutParams(context, attrs)
}
override fun generateLayoutParams(p: LayoutParams): LayoutParams {
return MediaLayoutParams(p.width, p.height)
}
private fun measure1Media(contentWidth: Int, childIndices: IntArray, ratioMultiplier: Float): Int {
val child = getChildAt(childIndices[0])
var childHeight = Math.round(contentWidth.toFloat() * WIDTH_HEIGHT_RATIO * ratioMultiplier)
if (style == PreviewStyle.REAL_SIZE) {
val media = (child.layoutParams as? MediaLayoutParams)?.media
if (media != null) {
val aspectRatio = media.aspect_ratio
if (!aspectRatio.isNaN()) {
childHeight = Math.round(contentWidth / aspectRatio.coerceIn(0.3, 20.0)).toInt()
}
}
}
val widthSpec = View.MeasureSpec.makeMeasureSpec(contentWidth, View.MeasureSpec.EXACTLY)
val heightSpec = View.MeasureSpec.makeMeasureSpec(childHeight, View.MeasureSpec.EXACTLY)
child.measure(widthSpec, heightSpec)
return childHeight
}
private fun layout1Media(childIndices: IntArray) {
val child = getChildAt(childIndices[0])
val left = paddingLeft
val top = paddingTop
val right = left + child.measuredWidth
val bottom = top + child.measuredHeight
child.layout(left, top, right, bottom)
}
private fun measureGridMedia(childCount: Int, columnCount: Int, contentWidth: Int,
widthHeightRatio: Float, horizontalSpacing: Int, verticalSpacing: Int,
childIndices: IntArray): Int {
val childWidth = (contentWidth - horizontalSpacing * (columnCount - 1)) / columnCount
val childHeight = Math.round(childWidth * widthHeightRatio)
val widthSpec = View.MeasureSpec.makeMeasureSpec(childWidth, View.MeasureSpec.EXACTLY)
val heightSpec = View.MeasureSpec.makeMeasureSpec(childHeight, View.MeasureSpec.EXACTLY)
for (i in 0 until childCount) {
getChildAt(childIndices[i]).measure(widthSpec, heightSpec)
}
val rowsCount = Math.ceil(childCount / columnCount.toDouble()).toInt()
return rowsCount * childHeight + (rowsCount - 1) * verticalSpacing
}
private fun layoutGridMedia(childCount: Int, columnCount: Int, horizontalSpacing: Int,
verticalSpacing: Int, childIndices: IntArray) {
val initialLeft = paddingLeft
var left = initialLeft
var top = paddingTop
for (i in 0 until childCount) {
val colIdx = i % columnCount
val child = getChildAt(childIndices[i])
child.layout(left, top, left + child.measuredWidth, top + child.measuredHeight)
if (colIdx == columnCount - 1) {
// Last item in this row, set top of next row to last view bottom + verticalSpacing
top = child.bottom + verticalSpacing
// And reset left to initial left
left = initialLeft
} else {
// The left of next item is right + horizontalSpacing of previous item
left = child.right + horizontalSpacing
}
}
}
private fun measure3Media(contentWidth: Int, horizontalSpacing: Int, childIndices: IntArray,
ratioMultiplier: Float): Int {
val child0 = getChildAt(childIndices[0])
val child1 = getChildAt(childIndices[1])
val child2 = getChildAt(childIndices[2])
val childWidth = (contentWidth - horizontalSpacing) / 2
val childLeftHeightSpec = View.MeasureSpec.makeMeasureSpec(Math.round(childWidth * ratioMultiplier), View.MeasureSpec.EXACTLY)
val widthSpec = View.MeasureSpec.makeMeasureSpec(childWidth, View.MeasureSpec.EXACTLY)
child0.measure(widthSpec, childLeftHeightSpec)
val childRightHeight = Math.round((childWidth - horizontalSpacing) / 2 * ratioMultiplier)
val childRightHeightSpec = View.MeasureSpec.makeMeasureSpec(childRightHeight, View.MeasureSpec.EXACTLY)
child1.measure(widthSpec, childRightHeightSpec)
child2.measure(widthSpec, childRightHeightSpec)
return Math.round(contentWidth.toFloat() * WIDTH_HEIGHT_RATIO * ratioMultiplier)
}
private fun layout3Media(horizontalSpacing: Int, verticalSpacing: Int, childIndices: IntArray) {
val left = paddingLeft
val top = paddingTop
val child0 = getChildAt(childIndices[0])
val child1 = getChildAt(childIndices[1])
val child2 = getChildAt(childIndices[2])
child0.layout(left, top, left + child0.measuredWidth, top + child0.measuredHeight)
val rightColLeft = child0.right + horizontalSpacing
child1.layout(rightColLeft, top, rightColLeft + child1.measuredWidth,
top + child1.measuredHeight)
val child2Top = child1.bottom + verticalSpacing
child2.layout(rightColLeft, child2Top, rightColLeft + child2.measuredWidth,
child2Top + child2.measuredHeight)
}
private fun createChildIndices(): IntArray {
if (tempIndices == null || tempIndices!!.size < childCount) {
tempIndices = IntArray(childCount)
}
return tempIndices!!
}
class MediaLayoutParams : ViewGroup.LayoutParams {
var media: ParcelableMedia? = null
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(width: Int, height: Int) : super(width, height)
}
interface OnMediaClickListener {
fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey?, id: Long)
}
private class ImageGridClickListener(
listener: OnMediaClickListener?,
private val accountKey: UserKey?,
private val extraId: Long
) : View.OnClickListener {
private val weakListener = WeakReference<OnMediaClickListener>(listener)
override fun onClick(v: View) {
val listener = weakListener.get() ?: return
val media = (v.layoutParams as? MediaLayoutParams)?.media ?: return
listener.onMediaClick(v, media, accountKey, extraId)
}
}
companion object {
private const val WIDTH_HEIGHT_RATIO = 0.5f
private fun getChildIndicesInLayout(viewGroup: ViewGroup, indices: IntArray): Int {
val childCount = viewGroup.childCount
var indicesCount = 0
for (i in 0 until childCount) {
if (viewGroup.getChildAt(i).visibility != View.GONE) {
indices[indicesCount++] = i
}
}
return indicesCount
}
}
}

View File

@ -73,7 +73,7 @@ open class MessageViewHolder(
textView = itemView.findViewById(R.id.text) as TextView
time = itemView.findViewById(R.id.time) as TextView
mediaContainer = itemView.findViewById(R.id.media_preview_container) as CardMediaContainer
mediaContainer.setStyle(adapter.mediaPreviewStyle)
mediaContainer.style = adapter.mediaPreviewStyle
}
open fun displayMessage(cursor: Cursor, indices: ParcelableDirectMessageCursorIndices) {
@ -94,8 +94,10 @@ open class MessageViewHolder(
textView.text = text
time.text = Utils.formatToLongTimeString(context, timestamp)
mediaContainer.visibility = if (media != null && media.isNotEmpty()) View.VISIBLE else View.GONE
mediaContainer.displayMedia(loader, media, accountKey, adapter.onMediaClickListener, adapter.mediaLoadingHandler, layoutPosition.toLong(), true
)
mediaContainer.displayMedia(loader = loader, media = media, accountId = accountKey,
extraId = layoutPosition.toLong(), withCredentials = true,
mediaClickListener = adapter.onMediaClickListener,
loadingHandler = adapter.mediaLoadingHandler)
}
fun setMessageColor(color: Int) {

View File

@ -324,8 +324,9 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
mediaLabel.visibility = View.GONE
mediaPreview.visibility = View.VISIBLE
mediaPreview.displayMedia(status.media, loader, status.account_key, -1, this,
adapter.mediaLoadingHandler)
mediaPreview.displayMedia(loader = loader, media = status.media,
accountId = status.account_key, mediaClickListener = this,
loadingHandler = adapter.mediaLoadingHandler)
}
} else {
// No media, hide all related views
@ -431,8 +432,8 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
quotedMediaPreview.visibility = View.VISIBLE
quotedMediaLabel.visibility = View.GONE
quotedMediaPreview.displayMedia(status.quoted_media, loader, status.account_key, -1,
null, null)
quotedMediaPreview.displayMedia(loader = loader, media = status.quoted_media,
accountId = status.account_key)
}
} else {
// No media, hide all related views
@ -441,11 +442,10 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
}
}
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey, extraId: Long) {
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey?, id: Long) {
statusClickListener?.onMediaClick(this, view, media, layoutPosition)
}
fun setOnClickListeners() {
setStatusClickListener(adapter.statusClickListener)
}
@ -488,8 +488,8 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
setTextSize(adapter.textSize)
profileImageView.style = adapter.profileImageStyle
mediaPreview.setStyle(adapter.mediaPreviewStyle)
quotedMediaPreview.setStyle(adapter.mediaPreviewStyle)
mediaPreview.style = adapter.mediaPreviewStyle
quotedMediaPreview.style = adapter.mediaPreviewStyle
// profileImageView.setStyle(adapter.getProfileImageStyle());
val nameFirst = adapter.nameFirst

View File

@ -42,7 +42,7 @@ interface IStatusViewHolder : CardMediaContainer.OnMediaClickListener {
val profileTypeView: ImageView?
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey, extraId: Long)
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey?, id: Long)
fun setStatusClickListener(listener: StatusClickListener?)

View File

@ -55,6 +55,7 @@
<string-array name="entries_media_preview_style">
<item>@string/preview_style_crop</item>
<item>@string/preview_style_scale</item>
<item>@string/preview_style_real_size</item>
</string-array>
<string-array name="entries_image_sources">
<item>@string/source_camera</item>

View File

@ -58,6 +58,7 @@
<string-array name="values_media_preview_style">
<item>crop</item>
<item>scale</item>
<item>real_size</item>
</string-array>
<string-array name="value_image_sources">
<item>camera</item>

View File

@ -892,6 +892,7 @@
<string name="preview_images">Preview images</string>
<string name="preview_style_crop">Crop</string>
<string name="preview_style_scale">Scale</string>
<string name="preview_style_real_size">Real size</string>
<!-- Previous item (tweet, user etc.) -->
<string name="previous_item">Previous</string>

View File

@ -59,6 +59,18 @@
android:value="true"/>
</SwitchPreferenceCompat>
<org.mariotaku.twidere.preference.EntrySummaryListPreference
android:defaultValue="crop"
android:entries="@array/entries_media_preview_style"
android:entryValues="@array/values_media_preview_style"
android:key="media_preview_style"
android:order="26"
android:title="@string/media_preview_style">
<extra
android:name="should_recreate"
android:value="true"/>
</org.mariotaku.twidere.preference.EntrySummaryListPreference>
<org.mariotaku.twidere.preference.LinkHighlightPreference
android:defaultValue="none"
android:key="link_highlight_option"

View File

@ -21,16 +21,6 @@
<!--suppress AndroidElementNotAllowed -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<org.mariotaku.twidere.preference.EntrySummaryListPreference
android:defaultValue="crop"
android:entries="@array/entries_media_preview_style"
android:entryValues="@array/values_media_preview_style"
android:key="media_preview_style"
android:title="@string/media_preview_style">
<extra
android:name="should_recreate"
android:value="true"/>
</org.mariotaku.twidere.preference.EntrySummaryListPreference>
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="show_absolute_time"