This commit is contained in:
Mariotaku Lee 2016-12-06 11:08:56 +08:00
parent 53662fb5b2
commit 918b2d27ed
15 changed files with 361 additions and 301 deletions

View File

@ -1,187 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 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.adapter;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.commonsware.cwac.layouts.AspectLockedFrameLayout;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter;
import org.mariotaku.twidere.graphic.like.LikeAnimationDrawable;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.util.ParcelableMediaUtils;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.view.MediaPreviewImageView;
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder;
/**
* Created by mariotaku on 14/11/19.
*/
public class StaggeredGridParcelableStatusesAdapter extends ParcelableStatusesAdapter {
public StaggeredGridParcelableStatusesAdapter(Context context) {
super(context);
}
@Override
protected int[] getProgressViewIds() {
return new int[]{R.id.media_image_progress};
}
@NonNull
@Override
protected IStatusViewHolder onCreateStatusViewHolder(ViewGroup parent) {
final View view = getInflater().inflate(R.layout.adapter_item_media_status, parent, false);
final MediaStatusViewHolder holder = new MediaStatusViewHolder(this, view);
holder.setOnClickListeners();
holder.setupViewOptions();
return holder;
}
public static class MediaStatusViewHolder extends RecyclerView.ViewHolder
implements IStatusViewHolder, View.OnClickListener, View.OnLongClickListener {
private final SimpleAspectRatioSource aspectRatioSource = new SimpleAspectRatioSource();
private final AspectLockedFrameLayout mediaImageContainer;
private final MediaPreviewImageView mediaImageView;
private final ImageView mediaProfileImageView;
private final TextView mediaTextView;
private final IStatusesAdapter<?> adapter;
private StatusClickListener listener;
public MediaStatusViewHolder(IStatusesAdapter<?> adapter, View itemView) {
super(itemView);
this.adapter = adapter;
mediaImageContainer = (AspectLockedFrameLayout) itemView.findViewById(R.id.media_image_container);
mediaImageContainer.setAspectRatioSource(aspectRatioSource);
mediaImageView = (MediaPreviewImageView) itemView.findViewById(R.id.media_image);
mediaProfileImageView = (ImageView) itemView.findViewById(R.id.media_profile_image);
mediaTextView = (TextView) itemView.findViewById(R.id.media_text);
}
@Override
public void displayStatus(@NonNull ParcelableStatus status, boolean displayInReplyTo, boolean shouldDisplayExtraType) {
final MediaLoaderWrapper loader = adapter.getMediaLoader();
final ParcelableMedia[] media = status.media;
if (media == null || media.length < 1) return;
final ParcelableMedia firstMedia = media[0];
mediaTextView.setText(status.text_unescaped);
if (firstMedia.width > 0 && firstMedia.height > 0) {
aspectRatioSource.setSize(firstMedia.width, firstMedia.height);
} else {
aspectRatioSource.setSize(100, 100);
}
mediaImageContainer.setTag(firstMedia);
mediaImageContainer.requestLayout();
mediaImageView.setHasPlayIcon(ParcelableMediaUtils.hasPlayIcon(firstMedia.type));
loader.displayProfileImage(mediaProfileImageView, status);
loader.displayPreviewImageWithCredentials(mediaImageView, firstMedia.preview_url,
status.account_key, adapter.getMediaLoadingHandler());
}
@Override
@Nullable
public ImageView getProfileImageView() {
return mediaProfileImageView;
}
@Override
@Nullable
public ImageView getProfileTypeView() {
return null;
}
@Override
public void onClick(View v) {
if (listener == null) return;
switch (v.getId()) {
case R.id.itemContent: {
listener.onStatusClick(this, getLayoutPosition());
break;
}
}
}
public boolean onLongClick(View v) {
return false;
}
@Override
public void onMediaClick(View view, ParcelableMedia media, UserKey accountKey, long extraId) {
}
@Override
public void setStatusClickListener(StatusClickListener listener) {
this.listener = listener;
itemView.findViewById(R.id.itemContent).setOnClickListener(this);
}
@Override
public void setTextSize(float textSize) {
}
@Override
public void playLikeAnimation(LikeAnimationDrawable.OnLikedListener listener) {
}
public void setOnClickListeners() {
setStatusClickListener(adapter.getStatusClickListener());
}
public void setupViewOptions() {
setTextSize(adapter.getTextSize());
}
private static class SimpleAspectRatioSource implements AspectLockedFrameLayout.AspectRatioSource {
private int width, height;
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
public void setSize(int width, int height) {
this.width = width;
this.height = height;
}
}
}
}

View File

@ -148,23 +148,23 @@ public abstract class DrawableHolder {
} }
public static class Builtin extends DrawableHolder { public static class Builtin extends DrawableHolder {
public static final Builtin HOME = new Builtin("home", R.drawable.ic_action_home); public static final DrawableHolder HOME = new Builtin("home", R.drawable.ic_action_home);
public static final Builtin HEART = new Builtin("heart", R.drawable.ic_action_heart); public static final DrawableHolder HEART = new Builtin("heart", R.drawable.ic_action_heart);
public static final Builtin HASHTAG = new Builtin("hashtag", R.drawable.ic_action_hashtag); public static final DrawableHolder HASHTAG = new Builtin("hashtag", R.drawable.ic_action_hashtag);
public static final Builtin ACCOUNTS = new Builtin("accounts", R.drawable.ic_action_accounts); public static final DrawableHolder ACCOUNTS = new Builtin("accounts", R.drawable.ic_action_accounts);
public static final Builtin LIST = new Builtin("list", R.drawable.ic_action_list); public static final DrawableHolder LIST = new Builtin("list", R.drawable.ic_action_list);
public static final Builtin MENTION = new Builtin("mention", R.drawable.ic_action_at); public static final DrawableHolder MENTION = new Builtin("mention", R.drawable.ic_action_at);
public static final Builtin NOTIFICATIONS = new Builtin("notifications", R.drawable.ic_action_notification); public static final DrawableHolder NOTIFICATIONS = new Builtin("notifications", R.drawable.ic_action_notification);
public static final Builtin GALLERY = new Builtin("gallery", R.drawable.ic_action_gallery); public static final DrawableHolder GALLERY = new Builtin("gallery", R.drawable.ic_action_gallery);
public static final Builtin MESSAGE = new Builtin("message", R.drawable.ic_action_message); public static final DrawableHolder MESSAGE = new Builtin("message", R.drawable.ic_action_message);
public static final Builtin QUOTE = new Builtin("quote", R.drawable.ic_action_quote); public static final DrawableHolder QUOTE = new Builtin("quote", R.drawable.ic_action_quote);
public static final Builtin SEARCH = new Builtin("search", R.drawable.ic_action_search); public static final DrawableHolder SEARCH = new Builtin("search", R.drawable.ic_action_search);
public static final Builtin STAGGERED = new Builtin("staggered", R.drawable.ic_action_view_quilt); public static final DrawableHolder STAGGERED = new Builtin("staggered", R.drawable.ic_action_view_quilt);
public static final Builtin STAR = new Builtin("star", R.drawable.ic_action_star); public static final DrawableHolder STAR = new Builtin("star", R.drawable.ic_action_star);
public static final Builtin TRENDS = new Builtin("trends", R.drawable.ic_action_trends); public static final DrawableHolder TRENDS = new Builtin("trends", R.drawable.ic_action_trends);
public static final Builtin TWIDERE = new Builtin("twidere", R.drawable.ic_action_twidere); public static final DrawableHolder TWIDERE = new Builtin("twidere", R.drawable.ic_action_twidere);
public static final Builtin TWITTER = new Builtin("twitter", R.drawable.ic_action_twitter); public static final DrawableHolder TWITTER = new Builtin("twitter", R.drawable.ic_action_twitter);
public static final Builtin USER = new Builtin("user", R.drawable.ic_action_user); public static final DrawableHolder USER = new Builtin("user", R.drawable.ic_action_user);
public static final DrawableHolder FAVORITE = new DrawableHolder() { public static final DrawableHolder FAVORITE = new DrawableHolder() {
@NonNull @NonNull
@Override @Override
@ -185,7 +185,7 @@ public abstract class DrawableHolder {
private final String key; private final String key;
private final int resId; private final int resId;
public Builtin(String key, int resId) { private Builtin(String key, int resId) {
this.key = key; this.key = key;
this.resId = resId; this.resId = resId;
setName(WordUtils.capitalize(key)); setName(WordUtils.capitalize(key));

View File

@ -5,9 +5,9 @@ import android.support.v4.app.Fragment;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.fragment.TrendsSuggestionsFragment; import org.mariotaku.twidere.fragment.TrendsSuggestionsFragment;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.DrawableHolder; import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.StringHolder; import org.mariotaku.twidere.model.tab.StringHolder;
import org.mariotaku.twidere.model.tab.TabConfiguration;
/** /**
* Created by mariotaku on 2016/11/27. * Created by mariotaku on 2016/11/27.
@ -23,7 +23,7 @@ public class TrendsTabConfiguration extends TabConfiguration {
@NonNull @NonNull
@Override @Override
public DrawableHolder getIcon() { public DrawableHolder getIcon() {
return DrawableHolder.Builtin.TRENDS; return DrawableHolder.Builtin.HASHTAG;
} }
@AccountFlags @AccountFlags

View File

@ -1,47 +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.holder;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.View.OnClickListener;
import org.mariotaku.twidere.adapter.iface.IGapSupportedAdapter;
/**
* Created by mariotaku on 14/12/3.
*/
public class GapViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
private final IGapSupportedAdapter adapter;
public GapViewHolder(IGapSupportedAdapter adapter, View itemView) {
super(itemView);
this.adapter = adapter;
itemView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
final IGapSupportedAdapter.GapClickListener listener = adapter.getGapClickListener();
if (listener == null) return;
listener.onGapClick(this, getLayoutPosition());
}
}

View File

@ -8,10 +8,7 @@ import org.mariotaku.twidere.TwidereConstants
import org.mariotaku.twidere.adapter.iface.* import org.mariotaku.twidere.adapter.iface.*
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition
import org.mariotaku.twidere.constant.SharedPreferenceConstants import org.mariotaku.twidere.constant.SharedPreferenceConstants
import org.mariotaku.twidere.model.ParcelableStatus import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.ParcelableUserList
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.getActivityStatus import org.mariotaku.twidere.model.util.getActivityStatus
import org.mariotaku.twidere.util.* import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
@ -179,6 +176,14 @@ class DummyItemAdapter @JvmOverloads constructor(
return false return false
} }
override fun addGapLoadingId(id: ObjectId) {
}
override fun removeGapLoadingId(id: ObjectId) {
}
fun updateOptions() { fun updateOptions() {
profileImageStyle = Utils.getProfileImageStyle(preferences.getString(SharedPreferenceConstants.KEY_PROFILE_IMAGE_STYLE, null)) profileImageStyle = Utils.getProfileImageStyle(preferences.getString(SharedPreferenceConstants.KEY_PROFILE_IMAGE_STYLE, null))
mediaPreviewStyle = Utils.getMediaPreviewStyle(preferences.getString(SharedPreferenceConstants.KEY_MEDIA_PREVIEW_STYLE, null)) mediaPreviewStyle = Utils.getMediaPreviewStyle(preferences.getString(SharedPreferenceConstants.KEY_MEDIA_PREVIEW_STYLE, null))

View File

@ -36,11 +36,9 @@ import org.mariotaku.twidere.adapter.iface.IGapSupportedAdapter
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
import org.mariotaku.twidere.annotation.Referral import org.mariotaku.twidere.annotation.Referral
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_NEW_DOCUMENT_API import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_NEW_DOCUMENT_API
import org.mariotaku.twidere.extension.model.id
import org.mariotaku.twidere.fragment.CursorActivitiesFragment import org.mariotaku.twidere.fragment.CursorActivitiesFragment
import org.mariotaku.twidere.model.ParcelableActivity import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.ParcelableActivityCursorIndices
import org.mariotaku.twidere.model.ParcelableMedia
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.ParcelableActivityUtils import org.mariotaku.twidere.model.util.ParcelableActivityUtils
import org.mariotaku.twidere.model.util.getActivityStatus import org.mariotaku.twidere.model.util.getActivityStatus
import org.mariotaku.twidere.util.IntentUtils import org.mariotaku.twidere.util.IntentUtils
@ -50,6 +48,7 @@ import org.mariotaku.twidere.util.TwidereLinkify
import org.mariotaku.twidere.view.holder.* import org.mariotaku.twidere.view.holder.*
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.*
/** /**
* Created by mariotaku on 15/1/3. * Created by mariotaku on 15/1/3.
@ -66,6 +65,8 @@ class ParcelableActivitiesAdapter(
private var data: List<ParcelableActivity>? = null private var data: List<ParcelableActivity>? = null
private var activityAdapterListener: ActivityAdapterListener? = null private var activityAdapterListener: ActivityAdapterListener? = null
private var filteredUserIds: Array<UserKey>? = null private var filteredUserIds: Array<UserKey>? = null
private val gapLoadingIds: MutableSet<ObjectId> = HashSet()
var followingOnly: Boolean = false var followingOnly: Boolean = false
set(value) { set(value) {
field = value field = value
@ -169,6 +170,7 @@ class ParcelableActivitiesAdapter(
filteredUserIds = data.filteredUserIds filteredUserIds = data.filteredUserIds
} }
this.data = data this.data = data
gapLoadingIds.clear()
notifyDataSetChanged() notifyDataSetChanged()
} }
@ -217,7 +219,7 @@ class ParcelableActivitiesAdapter(
return holder return holder
} }
ITEM_VIEW_TYPE_GAP -> { ITEM_VIEW_TYPE_GAP -> {
val view = inflater.inflate(R.layout.card_item_gap, parent, false) val view = inflater.inflate(GapViewHolder.layoutResource, parent, false)
return GapViewHolder(this, view) return GapViewHolder(this, view)
} }
ITEM_VIEW_TYPE_LOAD_INDICATOR -> { ITEM_VIEW_TYPE_LOAD_INDICATOR -> {
@ -248,6 +250,11 @@ class ParcelableActivitiesAdapter(
ITEM_VIEW_TYPE_STUB -> { ITEM_VIEW_TYPE_STUB -> {
(holder as StubViewHolder).displayActivity(getActivity(position)!!) (holder as StubViewHolder).displayActivity(getActivity(position)!!)
} }
ITEM_VIEW_TYPE_GAP -> {
val activity = getActivity(position)!!
val loading = gapLoadingIds.find { it.accountKey == activity.account_key && it.id == activity.id } != null
(holder as GapViewHolder).display(loading)
}
} }
} }
@ -299,6 +306,13 @@ class ParcelableActivitiesAdapter(
return ITEM_VIEW_TYPE_STUB return ITEM_VIEW_TYPE_STUB
} }
override fun addGapLoadingId(id: ObjectId) {
gapLoadingIds.add(id)
}
override fun removeGapLoadingId(id: ObjectId) {
gapLoadingIds.remove(id)
}
override fun getItemCount(): Int { override fun getItemCount(): Int {
val position = loadMoreIndicatorPosition val position = loadMoreIndicatorPosition
@ -388,16 +402,14 @@ class ParcelableActivitiesAdapter(
override fun onGapClick(holder: GapViewHolder, position: Int) { override fun onGapClick(holder: GapViewHolder, position: Int) {
val adapter = adapterRef.get() ?: return val adapter = adapterRef.get() ?: return
if (adapter.activityAdapterListener != null) { val activity = adapter.getActivity(position) ?: return
adapter.activityAdapterListener!!.onGapClick(holder, position) adapter.addGapLoadingId(ObjectId(activity.account_key, activity.id))
} adapter.activityAdapterListener?.onGapClick(holder, position)
} }
override fun onItemActionClick(holder: RecyclerView.ViewHolder, id: Int, position: Int) { override fun onItemActionClick(holder: RecyclerView.ViewHolder, id: Int, position: Int) {
val adapter = adapterRef.get() ?: return val adapter = adapterRef.get() ?: return
if (adapter.activityAdapterListener != null) { adapter.activityAdapterListener?.onStatusActionClick(holder as IStatusViewHolder, id, position)
adapter.activityAdapterListener!!.onStatusActionClick(holder as IStatusViewHolder, id, position)
}
} }
override fun onStatusLongClick(holder: IStatusViewHolder, position: Int): Boolean { override fun onStatusLongClick(holder: IStatusViewHolder, position: Int): Boolean {

View File

@ -29,10 +29,13 @@ import org.mariotaku.ktextension.safeMoveToPosition
import org.mariotaku.library.objectcursor.ObjectCursor import org.mariotaku.library.objectcursor.ObjectCursor
import org.mariotaku.twidere.R import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.iface.IGapSupportedAdapter import org.mariotaku.twidere.adapter.iface.IGapSupportedAdapter
import org.mariotaku.twidere.adapter.iface.IGapSupportedAdapter.Companion.ITEM_VIEW_TYPE_GAP
import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter 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.adapter.iface.IStatusesAdapter
import org.mariotaku.twidere.constant.SharedPreferenceConstants.* import org.mariotaku.twidere.constant.SharedPreferenceConstants.*
import org.mariotaku.twidere.model.ObjectId
import org.mariotaku.twidere.model.ParcelableStatus import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.ParcelableStatusCursorIndices import org.mariotaku.twidere.model.ParcelableStatusCursorIndices
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
@ -46,6 +49,7 @@ import org.mariotaku.twidere.view.holder.EmptyViewHolder
import org.mariotaku.twidere.view.holder.GapViewHolder import org.mariotaku.twidere.view.holder.GapViewHolder
import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder
import java.util.*
/** /**
* Created by mariotaku on 15/10/26. * Created by mariotaku on 15/10/26.
@ -72,10 +76,12 @@ abstract class ParcelableStatusesAdapter(
final override val sensitiveContentEnabled: Boolean final override val sensitiveContentEnabled: Boolean
final override val useStarsForLikes: Boolean final override val useStarsForLikes: Boolean
final override val isShowAbsoluteTime: Boolean final override val isShowAbsoluteTime: Boolean
override var statusClickListener: IStatusViewHolder.StatusClickListener? = null
private val showCardActions: Boolean private val showCardActions: Boolean
private val gapLoadingIds: MutableSet<ObjectId> = HashSet()
override var statusClickListener: IStatusViewHolder.StatusClickListener? = null
override val gapClickListener: IGapSupportedAdapter.GapClickListener? override val gapClickListener: IGapSupportedAdapter.GapClickListener?
get() = statusClickListener get() = statusClickListener
@ -245,14 +251,11 @@ abstract class ParcelableStatusesAdapter(
changed = data != data changed = data != data
} }
this.data = data this.data = data
gapLoadingIds.clear()
notifyDataSetChanged() notifyDataSetChanged()
return changed return changed
} }
fun getData(): List<ParcelableStatus>? {
return data
}
override fun isCardActionsShown(position: Int): Boolean { override fun isCardActionsShown(position: Int): Boolean {
if (position == RecyclerView.NO_POSITION) return showCardActions if (position == RecyclerView.NO_POSITION) return showCardActions
return showCardActions || showingActionCardId == getItemId(position) return showCardActions || showingActionCardId == getItemId(position)
@ -272,21 +275,16 @@ abstract class ParcelableStatusesAdapter(
} }
} }
fun isStatus(position: Int): Boolean {
return position < statusCount
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) { when (viewType) {
ITEM_VIEW_TYPE_STATUS -> { ITEM_VIEW_TYPE_STATUS -> {
return onCreateStatusViewHolder(parent) as RecyclerView.ViewHolder return onCreateStatusViewHolder(parent) as RecyclerView.ViewHolder
} }
IGapSupportedAdapter.ITEM_VIEW_TYPE_GAP -> { ITEM_VIEW_TYPE_GAP -> {
val view = inflater.inflate(R.layout.card_item_gap, parent, false) val view = inflater.inflate(GapViewHolder.layoutResource, parent, false)
return GapViewHolder(this, view) return GapViewHolder(this, view)
} }
ILoadMoreSupportAdapter.ITEM_VIEW_TYPE_LOAD_INDICATOR -> { ITEM_VIEW_TYPE_LOAD_INDICATOR -> {
val view = inflater.inflate(R.layout.card_item_load_indicator, parent, false) val view = inflater.inflate(R.layout.card_item_load_indicator, parent, false)
return LoadIndicatorViewHolder(view) return LoadIndicatorViewHolder(view)
} }
@ -297,7 +295,6 @@ abstract class ParcelableStatusesAdapter(
throw IllegalStateException("Unknown view type " + viewType) throw IllegalStateException("Unknown view type " + viewType)
} }
protected abstract fun onCreateStatusViewHolder(parent: ViewGroup): IStatusViewHolder
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemCountIndex(position)) { when (getItemCountIndex(position)) {
@ -306,31 +303,53 @@ abstract class ParcelableStatusesAdapter(
(holder as IStatusViewHolder).displayStatus(status, isShowInReplyTo) (holder as IStatusViewHolder).displayStatus(status, isShowInReplyTo)
} }
2 -> { 2 -> {
val status = data!![position - getItemStartPosition(2)]
when (holder.itemViewType) { when (holder.itemViewType) {
ITEM_VIEW_TYPE_STATUS -> { ITEM_VIEW_TYPE_STATUS -> {
val status = data!![position - getItemStartPosition(2)]
(holder as IStatusViewHolder).displayStatus(status, isShowInReplyTo) (holder as IStatusViewHolder).displayStatus(status, isShowInReplyTo)
} }
ITEM_VIEW_TYPE_GAP -> {
val loading = gapLoadingIds.find { it.accountKey == status.account_key && it.id == status.id } != null
(holder as GapViewHolder).display(loading)
}
} }
} }
} }
} }
protected abstract fun onCreateStatusViewHolder(parent: ViewGroup): IStatusViewHolder
override fun addGapLoadingId(id: ObjectId) {
gapLoadingIds.add(id)
}
override fun removeGapLoadingId(id: ObjectId) {
gapLoadingIds.remove(id)
}
fun getData(): List<ParcelableStatus>? {
return data
}
fun isStatus(position: Int): Boolean {
return position < statusCount
}
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
if (loadMoreIndicatorPosition and ILoadMoreSupportAdapter.START != 0L && position == 0) { if (loadMoreIndicatorPosition and ILoadMoreSupportAdapter.START != 0L && position == 0) {
return ILoadMoreSupportAdapter.ITEM_VIEW_TYPE_LOAD_INDICATOR return ITEM_VIEW_TYPE_LOAD_INDICATOR
} }
when (getItemCountIndex(position)) { when (getItemCountIndex(position)) {
0, 3 -> { 0, 3 -> {
return ILoadMoreSupportAdapter.ITEM_VIEW_TYPE_LOAD_INDICATOR return ITEM_VIEW_TYPE_LOAD_INDICATOR
} }
1 -> { 1 -> {
return ITEM_VIEW_TYPE_STATUS return ITEM_VIEW_TYPE_STATUS
} }
2 -> { 2 -> {
if (isGapItem(position)) { if (isGapItem(position)) {
return IGapSupportedAdapter.ITEM_VIEW_TYPE_GAP return ITEM_VIEW_TYPE_GAP
} else if (isFiltered(position)) { } else if (isFiltered(position)) {
return ITEM_VIEW_TYPE_EMPTY return ITEM_VIEW_TYPE_EMPTY
} else { } else {

View File

@ -0,0 +1,153 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 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.adapter
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import com.commonsware.cwac.layouts.AspectLockedFrameLayout
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter
import org.mariotaku.twidere.graphic.like.LikeAnimationDrawable
import org.mariotaku.twidere.model.ParcelableMedia
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.ParcelableMediaUtils
import org.mariotaku.twidere.view.MediaPreviewImageView
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder
/**
* Created by mariotaku on 14/11/19.
*/
class StaggeredGridParcelableStatusesAdapter(context: Context) : ParcelableStatusesAdapter(context) {
override val progressViewIds: IntArray
get() = intArrayOf(R.id.media_image_progress)
override fun onCreateStatusViewHolder(parent: ViewGroup): IStatusViewHolder {
val view = inflater.inflate(R.layout.adapter_item_media_status, parent, false)
val holder = MediaStatusViewHolder(this, view)
holder.setOnClickListeners()
holder.setupViewOptions()
return holder
}
class MediaStatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) : RecyclerView.ViewHolder(itemView), IStatusViewHolder, View.OnClickListener, View.OnLongClickListener {
private val aspectRatioSource = SimpleAspectRatioSource()
private val mediaImageContainer: AspectLockedFrameLayout
private val mediaImageView: MediaPreviewImageView
override val profileImageView: ImageView?
private val mediaTextView: TextView
private var listener: IStatusViewHolder.StatusClickListener? = null
init {
mediaImageContainer = itemView.findViewById(R.id.media_image_container) as AspectLockedFrameLayout
mediaImageContainer.setAspectRatioSource(aspectRatioSource)
mediaImageView = itemView.findViewById(R.id.media_image) as MediaPreviewImageView
profileImageView = itemView.findViewById(R.id.media_profile_image) as ImageView
mediaTextView = itemView.findViewById(R.id.media_text) as TextView
}
override fun displayStatus(status: ParcelableStatus, displayInReplyTo: Boolean, shouldDisplayExtraType: Boolean) {
val loader = adapter.mediaLoader
val media = status.media ?: return
if (media.isEmpty()) return
val firstMedia = media[0]
mediaTextView.text = status.text_unescaped
if (firstMedia.width > 0 && firstMedia.height > 0) {
aspectRatioSource.setSize(firstMedia.width, firstMedia.height)
} else {
aspectRatioSource.setSize(100, 100)
}
mediaImageContainer.tag = firstMedia
mediaImageContainer.requestLayout()
mediaImageView.setHasPlayIcon(ParcelableMediaUtils.hasPlayIcon(firstMedia.type))
loader.displayProfileImage(profileImageView, status)
loader.displayPreviewImageWithCredentials(mediaImageView, firstMedia.preview_url,
status.account_key, adapter.mediaLoadingHandler)
}
override val profileTypeView: ImageView?
get() = null
override fun onClick(v: View) {
if (listener == null) return
when (v.id) {
R.id.itemContent -> {
listener!!.onStatusClick(this, layoutPosition)
}
}
}
override fun onLongClick(v: View): Boolean {
return false
}
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey, extraId: Long) {
}
override fun setStatusClickListener(listener: IStatusViewHolder.StatusClickListener?) {
this.listener = listener
itemView.findViewById(R.id.itemContent).setOnClickListener(this)
}
override fun setTextSize(textSize: Float) {
}
override fun playLikeAnimation(listener: LikeAnimationDrawable.OnLikedListener) {
}
fun setOnClickListeners() {
setStatusClickListener(adapter.statusClickListener)
}
fun setupViewOptions() {
setTextSize(adapter.textSize)
}
private class SimpleAspectRatioSource : AspectLockedFrameLayout.AspectRatioSource {
private var width: Int = 0
private var height: Int = 0
override fun getWidth(): Int {
return width
}
override fun getHeight(): Int {
return height
}
fun setSize(width: Int, height: Int) {
this.width = width
this.height = height
}
}
}
}

View File

@ -19,6 +19,7 @@
package org.mariotaku.twidere.adapter.iface package org.mariotaku.twidere.adapter.iface
import org.mariotaku.twidere.model.ObjectId
import org.mariotaku.twidere.view.holder.GapViewHolder import org.mariotaku.twidere.view.holder.GapViewHolder
/** /**
@ -28,6 +29,10 @@ interface IGapSupportedAdapter {
fun isGapItem(position: Int): Boolean fun isGapItem(position: Int): Boolean
fun addGapLoadingId(id: ObjectId)
fun removeGapLoadingId(id: ObjectId)
val gapClickListener: GapClickListener? val gapClickListener: GapClickListener?
interface GapClickListener { interface GapClickListener {

View File

@ -0,0 +1,9 @@
package org.mariotaku.twidere.extension.model
import org.mariotaku.twidere.model.ParcelableActivity
/**
* Created by mariotaku on 2016/12/6.
*/
val ParcelableActivity.id: String
get() = "$min_position-$max_position"

View File

@ -397,12 +397,12 @@ abstract class AbsStatusesFragment protected constructor() :
override fun onGapClick(holder: GapViewHolder, position: Int) { override fun onGapClick(holder: GapViewHolder, position: Int) {
val adapter = adapter val adapter = this.adapter ?: return
val status = adapter!!.getStatus(position) val status = adapter.getStatus(position) ?: return
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Log.v(TwidereConstants.LOGTAG, "Load activity gap " + status!!) Log.v(TwidereConstants.LOGTAG, "Load activity gap " + status)
} }
if (status == null) return adapter.addGapLoadingId(ObjectId(status.account_key, status.id))
val accountIds = arrayOf(status.account_key) val accountIds = arrayOf(status.account_key)
val maxIds = arrayOf<String?>(status.id) val maxIds = arrayOf<String?>(status.id)
val maxSortIds = longArrayOf(status.sort_id) val maxSortIds = longArrayOf(status.sort_id)

View File

@ -1746,6 +1746,14 @@ class StatusFragment : BaseSupportFragment(), LoaderCallbacks<SingleResponse<Par
return getItemViewTypeByItemType(getItemType(position)) return getItemViewTypeByItemType(getItemType(position))
} }
override fun addGapLoadingId(id: ObjectId) {
}
override fun removeGapLoadingId(id: ObjectId) {
}
private fun getItemViewTypeByItemType(type: Int): Int { private fun getItemViewTypeByItemType(type: Int): Int {
when (type) { when (type) {
ITEM_IDX_CONVERSATION, ITEM_IDX_REPLY -> return VIEW_TYPE_LIST_STATUS ITEM_IDX_CONVERSATION, ITEM_IDX_REPLY -> return VIEW_TYPE_LIST_STATUS

View File

@ -0,0 +1,6 @@
package org.mariotaku.twidere.model
/**
* Created by mariotaku on 2016/12/6.
*/
data class ObjectId(val accountKey: UserKey, val id: String)

View File

@ -0,0 +1,62 @@
/*
* 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.holder
import android.support.v7.widget.RecyclerView
import android.view.View
import android.view.View.OnClickListener
import android.widget.ProgressBar
import android.widget.TextView
import kotlinx.android.synthetic.main.card_item_gap.view.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.iface.IGapSupportedAdapter
/**
* Created by mariotaku on 14/12/3.
*/
class GapViewHolder(
private val adapter: IGapSupportedAdapter,
itemView: View
) : RecyclerView.ViewHolder(itemView), OnClickListener {
private val gapText: TextView
private val gapProgress: ProgressBar
init {
itemView.setOnClickListener(this)
gapText = itemView.gapText
gapProgress = itemView.gapProgress
}
override fun onClick(v: View) {
adapter.gapClickListener?.onGapClick(this, layoutPosition)
display(true)
}
fun display(showProgress: Boolean) {
gapText.visibility = if (showProgress) View.GONE else View.VISIBLE
gapProgress.visibility = if (showProgress) View.VISIBLE else View.GONE
}
companion object {
const val layoutResource = R.layout.card_item_gap
}
}

View File

@ -18,15 +18,30 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>. ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<TextView <FrameLayout
android:id="@+id/gap_indicator" android:id="@+id/gapIndicator"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/element_size_normal" android:layout_height="@dimen/element_size_normal"
android:focusable="true">
<TextView
android:id="@+id/gapText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?selectableItemBackground" android:background="?selectableItemBackground"
android:gravity="center" android:gravity="center"
android:text="@string/load_more" android:text="@string/load_more"
android:focusable="true"
android:textAppearance="?android:textAppearanceMedium" android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorPrimary" android:textColor="?android:textColorPrimary"
android:textStyle="bold"/> android:textStyle="bold"/>
<ProgressBar
android:id="@+id/gapProgress"
style="?android:progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"/>
</FrameLayout>