mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-17 04:00:48 +01:00
implemented real aspect ratio option for media preview, close #700
This commit is contained in:
parent
23e5eecf1b
commit
ab5562d985
@ -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;
|
||||
|
@ -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;
|
||||
}
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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])
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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?) {
|
||||
|
@ -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?
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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?)
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user