diff --git a/.gitmodules b/.gitmodules index aa5f1cc91..59853ecf8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "libraries/RefreshNow"] path = libraries/RefreshNow url = https://github.com/mariotaku/RefreshNow-Android +[submodule "libraries/PullToRefresh"] + path = libraries/PullToRefresh + url = https://github.com/mariotaku/Android-PullToRefresh-Gradle.git diff --git a/build.gradle b/build.gradle index a75fc8180..ecab9d40c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,12 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. +apply plugin: 'idea' buildscript { repositories { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:0.12.+' + classpath 'com.android.tools.build:gradle:0.13.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -17,3 +18,10 @@ allprojects { jcenter() } } + +idea { + module { + downloadJavadoc = true + downloadSources = true + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1e61d1fd3..0b2fb9548 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Apr 10 15:27:10 PDT 2013 +#Thu Nov 20 21:01:22 CST 2014 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip diff --git a/libraries/PullToRefresh b/libraries/PullToRefresh new file mode 160000 index 000000000..5ca6a6cfb --- /dev/null +++ b/libraries/PullToRefresh @@ -0,0 +1 @@ +Subproject commit 5ca6a6cfb871ad4a273abeee8b97d8cc046b7f19 diff --git a/settings.gradle b/settings.gradle index c5baa09ad..264784608 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,8 @@ include ':twidere', ':twidere.wear' -include ':SlidingMenu', ':DragSortListView', ':MenuComponent', ':RefreshNow' +include ':SlidingMenu', ':DragSortListView', ':MenuComponent', ':RefreshNow', ':PullToRefresh' project(':SlidingMenu').projectDir = file('libraries/SlidingMenu/library') project(':DragSortListView').projectDir = file('libraries/DragSortListView/library') project(':MenuComponent').projectDir = file('libraries/MenuComponent/library') -project(':RefreshNow').projectDir = file('libraries/RefreshNow/library') \ No newline at end of file +project(':RefreshNow').projectDir = file('libraries/RefreshNow/library') +project(':PullToRefresh').projectDir = file('libraries/PullToRefresh/library') \ No newline at end of file diff --git a/twidere/build.gradle b/twidere/build.gradle index 79863bac0..18285c268 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -93,7 +93,7 @@ dependencies { compile 'ch.acra:acra:4.5.0' compile 'com.google.android.apps.dashclock:dashclock-api:2.0.0' compile 'me.grantland:autofittextview:0.2.0' - compile 'com.pkmmte.view:circularimageview:1.1' +// compile 'com.pkmmte.view:circularimageview:1.1' googleCompile 'com.google.android.gms:play-services:6.1.71' fdroidCompile 'org.osmdroid:osmdroid-android:4.2' fdroidCompile 'org.slf4j:slf4j-simple:1.6.1' @@ -101,5 +101,6 @@ dependencies { compile project(':DragSortListView') compile project(':MenuComponent') compile project(':RefreshNow') + compile project(':PullToRefresh') compile fileTree(dir: 'libs', include: ['*.jar']) } diff --git a/twidere/src/main/java/org/mariotaku/twidere/Constants.java b/twidere/src/main/java/org/mariotaku/twidere/Constants.java index 312a74e97..831a75dd9 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/Constants.java +++ b/twidere/src/main/java/org/mariotaku/twidere/Constants.java @@ -28,7 +28,7 @@ package org.mariotaku.twidere; public interface Constants extends TwidereConstants { public static final String DATABASES_NAME = "twidere.sqlite"; - public static final int DATABASES_VERSION = 70; + public static final int DATABASES_VERSION = 71; public static final int MENU_GROUP_STATUS_EXTENSION = 10; public static final int MENU_GROUP_COMPOSE_EXTENSION = 11; diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java index d480553ad..a5beb5990 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java @@ -108,7 +108,7 @@ import org.mariotaku.twidere.util.accessor.ViewAccessor; import org.mariotaku.twidere.view.ColorLabelFrameLayout; import org.mariotaku.twidere.view.StatusTextCountView; import org.mariotaku.twidere.view.TwidereMenuBar; -import org.mariotaku.twidere.view.holder.StatusViewHolder; +import org.mariotaku.twidere.view.holder.StatusListViewHolder; import java.io.File; import java.io.FileOutputStream; @@ -1187,7 +1187,7 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa public static class ViewStatusDialogFragment extends BaseSupportDialogFragment { - private StatusViewHolder mHolder; + private StatusListViewHolder mHolder; public ViewStatusDialogFragment() { setStyle(STYLE_NO_TITLE, 0); @@ -1268,7 +1268,7 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup parent, final Bundle savedInstanceState) { final ScrollView view = (ScrollView) inflater.inflate(R.layout.dialog_scrollable_status, parent, false); - mHolder = new StatusViewHolder(view.getChildAt(0)); + mHolder = new StatusListViewHolder(view.getChildAt(0)); return view; } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsStatusesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsStatusesAdapter.java new file mode 100644 index 000000000..c2ac05483 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsStatusesAdapter.java @@ -0,0 +1,111 @@ +package org.mariotaku.twidere.adapter; + +import android.content.Context; +import android.support.v7.widget.RecyclerView.Adapter; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.mariotaku.twidere.R; +import org.mariotaku.twidere.adapter.iface.IStatusesAdapter; +import org.mariotaku.twidere.app.TwidereApplication; +import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder; +import org.mariotaku.twidere.util.ImageLoaderWrapper; +import org.mariotaku.twidere.util.ImageLoadingHandler; +import org.mariotaku.twidere.view.holder.StatusViewHolder; + +/** +* Created by mariotaku on 14/11/19. +*/ +public abstract class AbsStatusesAdapter extends Adapter implements IStatusesAdapter { + + private static final int ITEM_VIEW_TYPE_STATUS = 1; + private static final int ITEM_VIEW_TYPE_LOAD_INDICATOR = 2; + + private final Context mContext; + private final LayoutInflater mInflater; + private final ImageLoaderWrapper mImageLoader; + private final ImageLoadingHandler mLoadingHandler; + private final int mCardLayoutResource; + private boolean mLoadMoreIndicatorEnabled; + + public AbsStatusesAdapter(Context context, boolean compact) { + mContext = context; + mInflater = LayoutInflater.from(context); + mImageLoader = TwidereApplication.getInstance(context).getImageLoaderWrapper(); + mLoadingHandler = new ImageLoadingHandler(R.id.media_preview_progress); + if (compact) { + mCardLayoutResource = R.layout.card_item_list_status_compat; + } else { + mCardLayoutResource = R.layout.card_item_list_status; + } + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + switch (viewType) { + case ITEM_VIEW_TYPE_STATUS: { + final View view = mInflater.inflate(mCardLayoutResource, parent, false); + return new StatusViewHolder(this, view); + } + case ITEM_VIEW_TYPE_LOAD_INDICATOR: { + final View view = mInflater.inflate(R.layout.card_item_load_indicator, parent, false); + return new LoadIndicatorViewHolder(view); + } + } + throw new IllegalStateException("Unknown view type " + viewType); + } + + public void setLoadMoreIndicatorEnabled(boolean enabled) { + if (mLoadMoreIndicatorEnabled == enabled) return; + mLoadMoreIndicatorEnabled = enabled; + notifyDataSetChanged(); + } + + public boolean hasLoadMoreIndicator() { + return mLoadMoreIndicatorEnabled; + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + switch (holder.getItemViewType()) { + case ITEM_VIEW_TYPE_STATUS: { + bindStatus(((StatusViewHolder) holder), position); + break; + } + } + } + + protected abstract void bindStatus(StatusViewHolder holder, int position); + + @Override + public int getItemViewType(int position) { + if (position == getItemCount() - 1) { + return ITEM_VIEW_TYPE_LOAD_INDICATOR; + } + return ITEM_VIEW_TYPE_STATUS; + } + + @Override + public final int getItemCount() { + return getStatusCount() + (mLoadMoreIndicatorEnabled ? 1 : 0); + } + + @Override + public ImageLoaderWrapper getImageLoader() { + return mImageLoader; + } + + @Override + public Context getContext() { + return mContext; + } + + @Override + public ImageLoadingHandler getImageLoadingHandler() { + return mLoadingHandler; + } + + +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/BaseParcelableActivitiesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/BaseParcelableActivitiesAdapter.java index d81866049..0d8fcbc06 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/BaseParcelableActivitiesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/BaseParcelableActivitiesAdapter.java @@ -38,7 +38,7 @@ import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableUser; import org.mariotaku.twidere.util.ImageLoaderWrapper; import org.mariotaku.twidere.util.MultiSelectManager; -import org.mariotaku.twidere.view.holder.ActivityViewHolder; +import org.mariotaku.twidere.view.holder.ActivityListViewHolder; import java.util.List; @@ -67,7 +67,7 @@ public abstract class BaseParcelableActivitiesAdapter extends BaseArrayAdapter

0; holder.activity_profile_images_container.setVisibility(shouldDisplayImages ? View.VISIBLE : View.GONE); diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesAdapter.java index 3a328631f..bd0f50b3c 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesAdapter.java @@ -43,7 +43,7 @@ import org.mariotaku.twidere.util.ImageLoadingHandler; import org.mariotaku.twidere.util.MultiSelectManager; import org.mariotaku.twidere.util.TwidereLinkify; import org.mariotaku.twidere.util.Utils; -import org.mariotaku.twidere.view.holder.StatusViewHolder; +import org.mariotaku.twidere.view.holder.StatusListViewHolder; import org.mariotaku.twidere.view.iface.ICardItemView.OnOverflowIconClickListener; import java.util.Locale; @@ -105,7 +105,7 @@ public class CursorStatusesAdapter extends BaseCursorAdapter implements IStatuse @Override public void bindView(final View view, final Context context, final Cursor cursor) { final int position = cursor.getPosition(); - final StatusViewHolder holder = (StatusViewHolder) view.getTag(); + final StatusListViewHolder holder = (StatusListViewHolder) view.getTag(); final boolean isGap = cursor.getShort(mIndices.is_gap) == 1; final boolean showGap = isGap && !mGapDisallowed && position != getCount() - 1; @@ -263,7 +263,7 @@ public class CursorStatusesAdapter extends BaseCursorAdapter implements IStatuse } @Override - public int getActualCount() { + public int getStatusCount() { return super.getCount(); } @@ -293,6 +293,16 @@ public class CursorStatusesAdapter extends BaseCursorAdapter implements IStatuse } } + @Override + public Context getContext() { + return mContext; + } + + @Override + public ImageLoadingHandler getImageLoadingHandler() { + return mImageLoadingHandler; + } + @Override public ParcelableStatus getStatus(final int position) { final Cursor c = getCursor(); @@ -312,9 +322,9 @@ public class CursorStatusesAdapter extends BaseCursorAdapter implements IStatuse final View view = super.getView(position, convertView, parent); final Object tag = view.getTag(); // animate the item - if (tag instanceof StatusViewHolder && position > mMaxAnimationPosition) { + if (tag instanceof StatusListViewHolder && position > mMaxAnimationPosition) { if (mAnimationEnabled) { - view.startAnimation(((StatusViewHolder) tag).item_animation); + view.startAnimation(((StatusListViewHolder) tag).item_animation); } mMaxAnimationPosition = position; } @@ -330,8 +340,8 @@ public class CursorStatusesAdapter extends BaseCursorAdapter implements IStatuse public View newView(final Context context, final Cursor cursor, final ViewGroup parent) { final View view = super.newView(context, cursor, parent); final Object tag = view.getTag(); - if (!(tag instanceof StatusViewHolder)) { - final StatusViewHolder holder = new StatusViewHolder(view); + if (!(tag instanceof StatusListViewHolder)) { + final StatusListViewHolder holder = new StatusListViewHolder(view); holder.profile_image.setOnClickListener(this); holder.my_profile_image.setOnClickListener(this); holder.image_preview.setOnClickListener(this); @@ -374,8 +384,8 @@ public class CursorStatusesAdapter extends BaseCursorAdapter implements IStatuse public void onOverflowIconClick(final View view) { if (mMultiSelectManager.isActive()) return; final Object tag = view.getTag(); - if (tag instanceof StatusViewHolder) { - final StatusViewHolder holder = (StatusViewHolder) tag; + if (tag instanceof StatusListViewHolder) { + final StatusListViewHolder holder = (StatusListViewHolder) tag; final int position = holder.position; if (position == -1 || mListener == null) return; mListener.onMenuButtonClick(view, position, getItemId(position)); diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesAboutMeAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesAboutMeAdapter.java index f759067aa..5b29c29d9 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesAboutMeAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesAboutMeAdapter.java @@ -38,7 +38,7 @@ import org.mariotaku.twidere.model.ParcelableUserList; import org.mariotaku.twidere.util.ImageLoaderWrapper; import org.mariotaku.twidere.util.ImageLoadingHandler; import org.mariotaku.twidere.util.TwidereLinkify; -import org.mariotaku.twidere.view.holder.ActivityViewHolder; +import org.mariotaku.twidere.view.holder.ActivityListViewHolder; public class ParcelableActivitiesAboutMeAdapter extends BaseParcelableActivitiesAdapter { @@ -56,7 +56,7 @@ public class ParcelableActivitiesAboutMeAdapter extends BaseParcelableActivities } @Override - public void bindView(final int position, final ActivityViewHolder holder, final ParcelableActivity item) { + public void bindView(final int position, final ActivityListViewHolder holder, final ParcelableActivity item) { if (item == null) return; final ParcelableUser[] sources = item.sources; if (sources == null || sources.length == 0) return; @@ -196,7 +196,7 @@ public class ParcelableActivitiesAboutMeAdapter extends BaseParcelableActivities } } - private void displayStatus(final ParcelableStatus status, final ActivityViewHolder holder, final int position) { + private void displayStatus(final ParcelableStatus status, final ActivityListViewHolder holder, final int position) { final boolean showGap = status.is_gap && !mGapDisallowed && position != getCount() - 1; final boolean displayProfileImage = isDisplayProfileImage(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesByFriendsAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesByFriendsAdapter.java index 3c7ea6296..52c947ece 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesByFriendsAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesByFriendsAdapter.java @@ -30,7 +30,7 @@ import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableUser; import org.mariotaku.twidere.model.ParcelableUserList; import org.mariotaku.twidere.util.TwidereLinkify; -import org.mariotaku.twidere.view.holder.ActivityViewHolder; +import org.mariotaku.twidere.view.holder.ActivityListViewHolder; public class ParcelableActivitiesByFriendsAdapter extends BaseParcelableActivitiesAdapter { @@ -40,7 +40,7 @@ public class ParcelableActivitiesByFriendsAdapter extends BaseParcelableActiviti } @Override - public void bindView(final int position, final ActivityViewHolder holder, final ParcelableActivity item) { + public void bindView(final int position, final ActivityListViewHolder holder, final ParcelableActivity item) { if (item == null) return; final ParcelableUser[] sources = item.sources; final ParcelableStatus[] targetStatuses = item.target_statuses; diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.java index 53b291e09..4af765e07 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.java @@ -1,445 +1,46 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 Mariotaku Lee - * - * 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 . - */ - package org.mariotaku.twidere.adapter; -import android.app.Activity; import android.content.Context; -import android.content.res.Resources; -import android.database.sqlite.SQLiteDatabase; -import android.text.Html; -import android.text.TextUtils; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.ImageView.ScaleType; -import org.mariotaku.twidere.R; -import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter; -import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.model.ParcelableStatus; -import org.mariotaku.twidere.model.ParcelableUserMention; -import org.mariotaku.twidere.util.ImageLoaderWrapper; -import org.mariotaku.twidere.util.ImageLoadingHandler; -import org.mariotaku.twidere.util.MultiSelectManager; -import org.mariotaku.twidere.util.TwidereLinkify; -import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.view.holder.StatusViewHolder; -import org.mariotaku.twidere.view.iface.ICardItemView.OnOverflowIconClickListener; import java.util.List; -import java.util.Locale; -import static org.mariotaku.twidere.model.ParcelableLocation.isValidLocation; -import static org.mariotaku.twidere.util.UserColorNicknameUtils.getUserColor; -import static org.mariotaku.twidere.util.UserColorNicknameUtils.getUserNickname; -import static org.mariotaku.twidere.util.Utils.configBaseCardAdapter; -import static org.mariotaku.twidere.util.Utils.getAccountColor; -import static org.mariotaku.twidere.util.Utils.getCardHighlightColor; -import static org.mariotaku.twidere.util.Utils.getCardHighlightOptionInt; -import static org.mariotaku.twidere.util.Utils.isFiltered; -import static org.mariotaku.twidere.util.Utils.openImage; -import static org.mariotaku.twidere.util.Utils.openUserProfile; +/** + * Created by mariotaku on 14/11/19. + */ +public class ParcelableStatusesAdapter extends AbsStatusesAdapter { -public class ParcelableStatusesAdapter extends BaseArrayAdapter implements - IStatusesListAdapter>, OnClickListener, OnOverflowIconClickListener { + private List mData; - private final Context mContext; - private final MultiSelectManager mMultiSelectManager; - private final SQLiteDatabase mDatabase; - private final ImageLoadingHandler mImageLoadingHandler; - - private MenuButtonClickListener mListener; - - private final boolean mPlainList; - - private boolean mDisplayImagePreview, mGapDisallowed, mMentionsHighlightDisabled, mFavoritesHighlightDisabled, - mDisplaySensitiveContents, mIndicateMyStatusDisabled, mIsLastItemFiltered, mFiltersEnabled, - mAnimationEnabled; - private boolean mFilterIgnoreUser, mFilterIgnoreSource, mFilterIgnoreTextHtml, mFilterIgnoreTextPlain, - mFilterRetweetedById; - private int mMaxAnimationPosition, mCardHighlightOption; - private ScaleType mImagePreviewScaleType; - private String[] mHighlightKeywords; - - public ParcelableStatusesAdapter(final Context context) { - this(context, Utils.isCompactCards(context), Utils.isPlainListStyle(context)); - } - - public ParcelableStatusesAdapter(final Context context, final boolean compactCards, final boolean plainList) { - super(context, getItemResource(compactCards)); - mPlainList = plainList; - mContext = context; - final TwidereApplication app = TwidereApplication.getInstance(context); - mMultiSelectManager = app.getMultiSelectManager(); - mDatabase = app.getSQLiteDatabase(); - mImageLoadingHandler = new ImageLoadingHandler(); - configBaseCardAdapter(context, this); - setMaxAnimationPosition(-1); + public ParcelableStatusesAdapter(Context context, boolean compact) { + super(context, compact); } @Override - public int findPositionByStatusId(final long status_id) { - for (int i = 0, count = getCount(); i < count; i++) { - if (getItem(i).id == status_id) return i; - } - return -1; + protected void bindStatus(StatusViewHolder holder, int position) { + holder.displayStatus(getStatus(position)); } @Override - public long getAccountId(final int position) { - if (position >= 0 && position < getCount()) { - final ParcelableStatus status = getItem(position); - return status != null ? status.account_id : -1; - } - return -1; + public ParcelableStatus getStatus(int position) { + if (hasLoadMoreIndicator() && position == getStatusCount() - 1) return null; + return mData.get(position); } @Override - public int getActualCount() { - return super.getCount(); + public int getStatusCount() { + if (mData == null) return 0; + return mData.size(); } - @Override - public int getCount() { - final int count = super.getCount(); - return mFiltersEnabled && mIsLastItemFiltered && count > 0 ? count - 1 : count; - } - - @Override - public long getItemId(final int position) { - final ParcelableStatus item = getItem(position); - return item != null ? item.id : -1; - } - - @Override - public ParcelableStatus getLastStatus() { - if (super.getCount() == 0) return null; - return getItem(super.getCount() - 1); - } - - @Override - public long getLastStatusId() { - if (super.getCount() == 0) return -1; - return getItem(super.getCount() - 1).id; - } - - @Override - public ParcelableStatus getStatus(final int position) { - return getItem(position); - } - - @Override - public long getStatusId(final int position) { - if (position >= 0 && position < getCount()) { - final ParcelableStatus status = getItem(position); - return status != null ? status.id : -1; - } - return -1; - } - - @Override - public View getView(final int position, final View convertView, final ViewGroup parent) { - final View view = super.getView(position, convertView, parent); - final Object tag = view.getTag(); - final StatusViewHolder holder; - - if (tag instanceof StatusViewHolder) { - holder = (StatusViewHolder) tag; - } else { - holder = new StatusViewHolder(view); - holder.profile_image.setOnClickListener(this); - holder.my_profile_image.setOnClickListener(this); - holder.image_preview.setOnClickListener(this); - holder.content.setOnOverflowIconClickListener(this); - if (mPlainList) { - ((View) holder.content).setPadding(0, 0, 0, 0); - holder.content.setItemBackground(null); - } - view.setTag(holder); - } - - final ParcelableStatus status = getItem(position); - - final boolean showGap = status.is_gap && !mGapDisallowed && position != getCount() - 1; - - holder.position = position; - holder.setShowAsGap(showGap); - holder.setDisplayProfileImage(isDisplayProfileImage()); - holder.setCardHighlightOption(mCardHighlightOption); - - final ImageLoaderWrapper loader = getImageLoader(); - if (!showGap) { - final TwidereLinkify linkify = getLinkify(); - final int highlightOption = getLinkHighlightOption(); - final boolean mShowAccountColor = isShowAccountColor(); - - holder.setAccountColorEnabled(mShowAccountColor); - - if (highlightOption != VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) { - holder.text.setText(Utils.getKeywordBoldedText(Html.fromHtml(status.text_html), mHighlightKeywords)); - linkify.applyAllLinks(holder.text, status.account_id, status.is_possibly_sensitive); - holder.text.setMovementMethod(null); - } else { - if (mHighlightKeywords == null || mHighlightKeywords.length == 0) { - holder.text.setText(status.text_unescaped); - } else { - holder.text.setText(Utils.getKeywordBoldedText(status.text_unescaped, mHighlightKeywords)); - } - } - - if (mShowAccountColor) { - holder.setAccountColor(getAccountColor(mContext, status.account_id)); - } - - final boolean isMention = ParcelableUserMention.hasMention(status.mentions, status.account_id); - final boolean isMyStatus = status.account_id == status.user_id; - final boolean hasMedia = status.first_media != null; - if (status.is_retweet) { - holder.setUserColor(getUserColor(mContext, status.user_id), - getUserColor(mContext, status.retweeted_by_id)); - } else { - holder.setUserColor(getUserColor(mContext, status.user_id)); - } - holder.setHighlightColor(getCardHighlightColor(!mMentionsHighlightDisabled && isMention, - !mFavoritesHighlightDisabled && status.is_favorite, status.is_retweet)); - holder.setTextSize(getTextSize()); - - holder.setIsMyStatus(isMyStatus && !mIndicateMyStatusDisabled); - - holder.setUserType(status.user_is_verified, status.user_is_protected); - holder.setDisplayNameFirst(isDisplayNameFirst()); - holder.setNicknameOnly(isNicknameOnly()); - final String nick = getUserNickname(mContext, status.user_id); - holder.name.setText(TextUtils.isEmpty(nick) ? status.user_name : isNicknameOnly() ? nick : mContext - .getString(R.string.name_with_nickname, status.user_name, nick)); - holder.screen_name.setText("@" + status.user_screen_name); - if (highlightOption != VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) { - linkify.applyUserProfileLinkNoHighlight(holder.name, status.account_id, status.user_id, - status.user_screen_name); - linkify.applyUserProfileLinkNoHighlight(holder.screen_name, status.account_id, status.user_id, - status.user_screen_name); - holder.name.setMovementMethod(null); - holder.screen_name.setMovementMethod(null); - } - holder.time.setTime(status.retweet_timestamp > 0 ? status.retweet_timestamp : status.timestamp); - holder.setStatusType(!mFavoritesHighlightDisabled && status.is_favorite, isValidLocation(status.location), - hasMedia, status.is_possibly_sensitive); - holder.setIsReplyRetweet(status.in_reply_to_status_id > 0, status.is_retweet); - if (status.is_retweet) { - holder.setRetweetedBy(status.retweet_count, status.retweeted_by_id, status.retweeted_by_name, - status.retweeted_by_screen_name); - } else if (status.in_reply_to_status_id > 0) { - holder.setReplyTo(status.in_reply_to_user_id, status.in_reply_to_name, status.in_reply_to_screen_name); - } - if (isDisplayProfileImage()) { - loader.displayProfileImage(holder.my_profile_image, status.user_profile_image_url); - loader.displayProfileImage(holder.profile_image, status.user_profile_image_url); - holder.profile_image.setTag(position); - holder.my_profile_image.setTag(position); - } else { - loader.cancelDisplayTask(holder.profile_image); - loader.cancelDisplayTask(holder.my_profile_image); - holder.profile_image.setVisibility(View.GONE); - holder.my_profile_image.setVisibility(View.GONE); - } - final boolean hasPreview = mDisplayImagePreview && hasMedia; - holder.image_preview_container.setVisibility(hasPreview ? View.VISIBLE : View.GONE); - if (hasPreview) { - if (mImagePreviewScaleType != null) { - holder.image_preview.setScaleType(mImagePreviewScaleType); - } - if (status.is_possibly_sensitive && !mDisplaySensitiveContents) { - holder.image_preview.setImageDrawable(null); - holder.image_preview.setBackgroundResource(R.drawable.image_preview_nsfw); - holder.image_preview_progress.setVisibility(View.GONE); - } else if (!status.first_media.equals(mImageLoadingHandler.getLoadingUri(holder.image_preview))) { - holder.image_preview.setBackgroundResource(0); - loader.displayPreviewImage(holder.image_preview, status.first_media, mImageLoadingHandler); - } - final Resources res = mContext.getResources(); - final int count = status.media.length; - holder.image_preview_count.setText(res.getQuantityString(R.plurals.N_media, count, count)); - holder.image_preview.setTag(position); - } else { - loader.cancelDisplayTask(holder.image_preview); - } - } else { - loader.cancelDisplayTask(holder.profile_image); - loader.cancelDisplayTask(holder.my_profile_image); - loader.cancelDisplayTask(holder.image_preview); - } - if (position > mMaxAnimationPosition) { - if (mAnimationEnabled) { - view.startAnimation(holder.item_animation); - } - mMaxAnimationPosition = position; - } - return view; - } - - @Override - public boolean isLastItemFiltered() { - return mIsLastItemFiltered; - } - - @Override - public void onClick(final View view) { - if (mMultiSelectManager.isActive()) return; - final Object tag = view.getTag(); - final int position = tag instanceof Integer ? (Integer) tag : -1; - if (position == -1) return; - switch (view.getId()) { - case R.id.image_preview: { - final ParcelableStatus status = getStatus(position); - if (status == null || status.first_media == null) return; - openImage(mContext, status.account_id, status.first_media, status.is_possibly_sensitive); - break; - } - case R.id.my_profile_image: - case R.id.profile_image: { - final ParcelableStatus status = getStatus(position); - if (status == null) return; - if (mContext instanceof Activity) { - openUserProfile((Activity) mContext, status.account_id, status.user_id, status.user_screen_name); - } - break; - } - } - } - - @Override - public void onOverflowIconClick(final View view) { - if (mMultiSelectManager.isActive()) return; - final Object tag = view.getTag(); - if (tag instanceof StatusViewHolder) { - final StatusViewHolder holder = (StatusViewHolder) tag; - final int position = holder.position; - if (position == -1 || mListener == null) return; - mListener.onMenuButtonClick(view, position, getItemId(position)); - } - } - - @Override - public void setAnimationEnabled(final boolean anim) { - mAnimationEnabled = anim; - } - - @Override - public void setCardHighlightOption(final String option) { - mCardHighlightOption = getCardHighlightOptionInt(option); - } - - @Override - public void setData(final List data) { - clear(); - if (data != null && !data.isEmpty()) { - addAll(data); - } - rebuildFilterInfo(); - } - - @Override - public void setDisplayImagePreview(final boolean display) { - mDisplayImagePreview = display; - } - - @Override - public void setDisplaySensitiveContents(final boolean display) { - mDisplaySensitiveContents = display; - } - - @Override - public void setFavoritesHightlightDisabled(final boolean disable) { - mFavoritesHighlightDisabled = disable; - } - - @Override - public void setFiltersEnabled(final boolean enabled) { - if (mFiltersEnabled == enabled) return; - mFiltersEnabled = enabled; - rebuildFilterInfo(); - } - - @Override - public void setGapDisallowed(final boolean disallowed) { - mGapDisallowed = disallowed; - } - - @Override - public void setHighlightKeyword(final String... keywords) { - mHighlightKeywords = keywords; - } - - @Override - public void setIgnoredFilterFields(final boolean user, final boolean textPlain, final boolean textHtml, - final boolean source, final boolean retweetedById) { - mFilterIgnoreTextPlain = textPlain; - mFilterIgnoreTextHtml = textHtml; - mFilterIgnoreUser = user; - mFilterIgnoreSource = source; - mFilterRetweetedById = retweetedById; - rebuildFilterInfo(); - } - - @Override - public void setImagePreviewScaleType(final String scaleTypeString) { - final ScaleType scaleType = ScaleType.valueOf(scaleTypeString.toUpperCase(Locale.US)); - mImagePreviewScaleType = scaleType; - } - - @Override - public void setIndicateMyStatusDisabled(final boolean disable) { - mIndicateMyStatusDisabled = disable; - } - - @Override - public void setMaxAnimationPosition(final int position) { - mMaxAnimationPosition = position; - } - - @Override - public void setMentionsHightlightDisabled(final boolean disable) { - mMentionsHighlightDisabled = disable; - } - - @Override - public void setMenuButtonClickListener(final MenuButtonClickListener listener) { - mListener = listener; - } - - private void rebuildFilterInfo() { - if (!isEmpty()) { - final ParcelableStatus last = getItem(super.getCount() - 1); - final long user_id = mFilterIgnoreUser ? -1 : last.user_id; - final String text_plain = mFilterIgnoreTextPlain ? null : last.text_plain; - final String text_html = mFilterIgnoreTextHtml ? null : last.text_html; - final String source = mFilterIgnoreSource ? null : last.source; - final long retweeted_by_id = mFilterRetweetedById ? -1 : last.retweeted_by_id; - mIsLastItemFiltered = isFiltered(mDatabase, user_id, text_plain, text_html, source, retweeted_by_id); - } else { - mIsLastItemFiltered = false; - } + public void setData(List data) { + mData = data; notifyDataSetChanged(); } - private static int getItemResource(final boolean compactCards) { - return compactCards ? R.layout.card_item_status_compact : R.layout.card_item_status; + public List getData() { + return mData; } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableStatusesListAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableStatusesListAdapter.java new file mode 100644 index 000000000..6ea9bb714 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableStatusesListAdapter.java @@ -0,0 +1,450 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2014 Mariotaku Lee + * + * 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 . + */ + +package org.mariotaku.twidere.adapter; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.database.sqlite.SQLiteDatabase; +import android.text.Html; +import android.text.TextUtils; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageView.ScaleType; + +import org.mariotaku.twidere.R; +import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter; +import org.mariotaku.twidere.app.TwidereApplication; +import org.mariotaku.twidere.model.ParcelableStatus; +import org.mariotaku.twidere.model.ParcelableUserMention; +import org.mariotaku.twidere.util.ImageLoaderWrapper; +import org.mariotaku.twidere.util.ImageLoadingHandler; +import org.mariotaku.twidere.util.MultiSelectManager; +import org.mariotaku.twidere.util.TwidereLinkify; +import org.mariotaku.twidere.util.Utils; +import org.mariotaku.twidere.view.holder.StatusListViewHolder; +import org.mariotaku.twidere.view.iface.ICardItemView.OnOverflowIconClickListener; + +import java.util.List; +import java.util.Locale; + +import static org.mariotaku.twidere.model.ParcelableLocation.isValidLocation; +import static org.mariotaku.twidere.util.UserColorNicknameUtils.getUserColor; +import static org.mariotaku.twidere.util.UserColorNicknameUtils.getUserNickname; +import static org.mariotaku.twidere.util.Utils.configBaseCardAdapter; +import static org.mariotaku.twidere.util.Utils.getAccountColor; +import static org.mariotaku.twidere.util.Utils.getCardHighlightColor; +import static org.mariotaku.twidere.util.Utils.getCardHighlightOptionInt; +import static org.mariotaku.twidere.util.Utils.isFiltered; +import static org.mariotaku.twidere.util.Utils.openImage; +import static org.mariotaku.twidere.util.Utils.openUserProfile; + +public class ParcelableStatusesListAdapter extends BaseArrayAdapter implements + IStatusesListAdapter>, OnClickListener, OnOverflowIconClickListener { + + private final Context mContext; + private final MultiSelectManager mMultiSelectManager; + private final SQLiteDatabase mDatabase; + private final ImageLoadingHandler mImageLoadingHandler; + + private MenuButtonClickListener mListener; + + private final boolean mPlainList; + + private boolean mDisplayImagePreview, mGapDisallowed, mMentionsHighlightDisabled, mFavoritesHighlightDisabled, + mDisplaySensitiveContents, mIndicateMyStatusDisabled, mIsLastItemFiltered, mFiltersEnabled, + mAnimationEnabled; + private boolean mFilterIgnoreUser, mFilterIgnoreSource, mFilterIgnoreTextHtml, mFilterIgnoreTextPlain, + mFilterRetweetedById; + private int mMaxAnimationPosition, mCardHighlightOption; + private ScaleType mImagePreviewScaleType; + private String[] mHighlightKeywords; + + public ParcelableStatusesListAdapter(final Context context) { + this(context, Utils.isCompactCards(context), Utils.isPlainListStyle(context)); + } + + public ParcelableStatusesListAdapter(final Context context, final boolean compactCards, final boolean plainList) { + super(context, getItemResource(compactCards)); + mPlainList = plainList; + mContext = context; + final TwidereApplication app = TwidereApplication.getInstance(context); + mMultiSelectManager = app.getMultiSelectManager(); + mDatabase = app.getSQLiteDatabase(); + mImageLoadingHandler = new ImageLoadingHandler(); + configBaseCardAdapter(context, this); + setMaxAnimationPosition(-1); + } + + @Override + public int findPositionByStatusId(final long status_id) { + for (int i = 0, count = getCount(); i < count; i++) { + if (getItem(i).id == status_id) return i; + } + return -1; + } + + @Override + public long getAccountId(final int position) { + if (position >= 0 && position < getCount()) { + final ParcelableStatus status = getItem(position); + return status != null ? status.account_id : -1; + } + return -1; + } + + @Override + public int getStatusCount() { + return super.getCount(); + } + + @Override + public int getCount() { + final int count = super.getCount(); + return mFiltersEnabled && mIsLastItemFiltered && count > 0 ? count - 1 : count; + } + + @Override + public long getItemId(final int position) { + final ParcelableStatus item = getItem(position); + return item != null ? item.id : -1; + } + + @Override + public ParcelableStatus getLastStatus() { + if (super.getCount() == 0) return null; + return getItem(super.getCount() - 1); + } + + @Override + public long getLastStatusId() { + if (super.getCount() == 0) return -1; + return getItem(super.getCount() - 1).id; + } + + @Override + public ImageLoadingHandler getImageLoadingHandler() { + return mImageLoadingHandler; + } + + @Override + public ParcelableStatus getStatus(final int position) { + return getItem(position); + } + + @Override + public long getStatusId(final int position) { + if (position >= 0 && position < getCount()) { + final ParcelableStatus status = getItem(position); + return status != null ? status.id : -1; + } + return -1; + } + + @Override + public View getView(final int position, final View convertView, final ViewGroup parent) { + final View view = super.getView(position, convertView, parent); + final Object tag = view.getTag(); + final StatusListViewHolder holder; + + if (tag instanceof StatusListViewHolder) { + holder = (StatusListViewHolder) tag; + } else { + holder = new StatusListViewHolder(view); + holder.profile_image.setOnClickListener(this); + holder.my_profile_image.setOnClickListener(this); + holder.image_preview.setOnClickListener(this); + holder.content.setOnOverflowIconClickListener(this); + if (mPlainList) { + ((View) holder.content).setPadding(0, 0, 0, 0); + holder.content.setItemBackground(null); + } + view.setTag(holder); + } + + final ParcelableStatus status = getItem(position); + + final boolean showGap = status.is_gap && !mGapDisallowed && position != getCount() - 1; + + holder.position = position; + holder.setShowAsGap(showGap); + holder.setDisplayProfileImage(isDisplayProfileImage()); + holder.setCardHighlightOption(mCardHighlightOption); + + final ImageLoaderWrapper loader = getImageLoader(); + if (!showGap) { + final TwidereLinkify linkify = getLinkify(); + final int highlightOption = getLinkHighlightOption(); + final boolean mShowAccountColor = isShowAccountColor(); + + holder.setAccountColorEnabled(mShowAccountColor); + + if (highlightOption != VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) { + holder.text.setText(Utils.getKeywordBoldedText(Html.fromHtml(status.text_html), mHighlightKeywords)); + linkify.applyAllLinks(holder.text, status.account_id, status.is_possibly_sensitive); + holder.text.setMovementMethod(null); + } else { + if (mHighlightKeywords == null || mHighlightKeywords.length == 0) { + holder.text.setText(status.text_unescaped); + } else { + holder.text.setText(Utils.getKeywordBoldedText(status.text_unescaped, mHighlightKeywords)); + } + } + + if (mShowAccountColor) { + holder.setAccountColor(getAccountColor(mContext, status.account_id)); + } + + final boolean isMention = ParcelableUserMention.hasMention(status.mentions, status.account_id); + final boolean isMyStatus = status.account_id == status.user_id; + final boolean hasMedia = status.first_media != null; + if (status.is_retweet) { + holder.setUserColor(getUserColor(mContext, status.user_id), + getUserColor(mContext, status.retweeted_by_id)); + } else { + holder.setUserColor(getUserColor(mContext, status.user_id)); + } + holder.setHighlightColor(getCardHighlightColor(!mMentionsHighlightDisabled && isMention, + !mFavoritesHighlightDisabled && status.is_favorite, status.is_retweet)); + holder.setTextSize(getTextSize()); + + holder.setIsMyStatus(isMyStatus && !mIndicateMyStatusDisabled); + + holder.setUserType(status.user_is_verified, status.user_is_protected); + holder.setDisplayNameFirst(isDisplayNameFirst()); + holder.setNicknameOnly(isNicknameOnly()); + final String nick = getUserNickname(mContext, status.user_id); + holder.name.setText(TextUtils.isEmpty(nick) ? status.user_name : isNicknameOnly() ? nick : mContext + .getString(R.string.name_with_nickname, status.user_name, nick)); + holder.screen_name.setText("@" + status.user_screen_name); + if (highlightOption != VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) { + linkify.applyUserProfileLinkNoHighlight(holder.name, status.account_id, status.user_id, + status.user_screen_name); + linkify.applyUserProfileLinkNoHighlight(holder.screen_name, status.account_id, status.user_id, + status.user_screen_name); + holder.name.setMovementMethod(null); + holder.screen_name.setMovementMethod(null); + } + holder.time.setTime(status.retweet_timestamp > 0 ? status.retweet_timestamp : status.timestamp); + holder.setStatusType(!mFavoritesHighlightDisabled && status.is_favorite, isValidLocation(status.location), + hasMedia, status.is_possibly_sensitive); + holder.setIsReplyRetweet(status.in_reply_to_status_id > 0, status.is_retweet); + if (status.is_retweet) { + holder.setRetweetedBy(status.retweet_count, status.retweeted_by_id, status.retweeted_by_name, + status.retweeted_by_screen_name); + } else if (status.in_reply_to_status_id > 0) { + holder.setReplyTo(status.in_reply_to_user_id, status.in_reply_to_name, status.in_reply_to_screen_name); + } + if (isDisplayProfileImage()) { + loader.displayProfileImage(holder.my_profile_image, status.user_profile_image_url); + loader.displayProfileImage(holder.profile_image, status.user_profile_image_url); + holder.profile_image.setTag(position); + holder.my_profile_image.setTag(position); + } else { + loader.cancelDisplayTask(holder.profile_image); + loader.cancelDisplayTask(holder.my_profile_image); + holder.profile_image.setVisibility(View.GONE); + holder.my_profile_image.setVisibility(View.GONE); + } + final boolean hasPreview = mDisplayImagePreview && hasMedia; + holder.image_preview_container.setVisibility(hasPreview ? View.VISIBLE : View.GONE); + if (hasPreview) { + if (mImagePreviewScaleType != null) { + holder.image_preview.setScaleType(mImagePreviewScaleType); + } + if (status.is_possibly_sensitive && !mDisplaySensitiveContents) { + holder.image_preview.setImageDrawable(null); + holder.image_preview.setBackgroundResource(R.drawable.image_preview_nsfw); + holder.image_preview_progress.setVisibility(View.GONE); + } else if (!status.first_media.equals(mImageLoadingHandler.getLoadingUri(holder.image_preview))) { + holder.image_preview.setBackgroundResource(0); + loader.displayPreviewImage(holder.image_preview, status.first_media, mImageLoadingHandler); + } + final Resources res = mContext.getResources(); + final int count = status.media.length; + holder.image_preview_count.setText(res.getQuantityString(R.plurals.N_media, count, count)); + holder.image_preview.setTag(position); + } else { + loader.cancelDisplayTask(holder.image_preview); + } + } else { + loader.cancelDisplayTask(holder.profile_image); + loader.cancelDisplayTask(holder.my_profile_image); + loader.cancelDisplayTask(holder.image_preview); + } + if (position > mMaxAnimationPosition) { + if (mAnimationEnabled) { + view.startAnimation(holder.item_animation); + } + mMaxAnimationPosition = position; + } + return view; + } + + @Override + public boolean isLastItemFiltered() { + return mIsLastItemFiltered; + } + + @Override + public void onClick(final View view) { + if (mMultiSelectManager.isActive()) return; + final Object tag = view.getTag(); + final int position = tag instanceof Integer ? (Integer) tag : -1; + if (position == -1) return; + switch (view.getId()) { + case R.id.image_preview: { + final ParcelableStatus status = getStatus(position); + if (status == null || status.first_media == null) return; + openImage(mContext, status.account_id, status.first_media, status.is_possibly_sensitive); + break; + } + case R.id.my_profile_image: + case R.id.profile_image: { + final ParcelableStatus status = getStatus(position); + if (status == null) return; + if (mContext instanceof Activity) { + openUserProfile((Activity) mContext, status.account_id, status.user_id, status.user_screen_name); + } + break; + } + } + } + + @Override + public void onOverflowIconClick(final View view) { + if (mMultiSelectManager.isActive()) return; + final Object tag = view.getTag(); + if (tag instanceof StatusListViewHolder) { + final StatusListViewHolder holder = (StatusListViewHolder) tag; + final int position = holder.position; + if (position == -1 || mListener == null) return; + mListener.onMenuButtonClick(view, position, getItemId(position)); + } + } + + @Override + public void setAnimationEnabled(final boolean anim) { + mAnimationEnabled = anim; + } + + @Override + public void setCardHighlightOption(final String option) { + mCardHighlightOption = getCardHighlightOptionInt(option); + } + + @Override + public void setData(final List data) { + clear(); + if (data != null && !data.isEmpty()) { + addAll(data); + } + rebuildFilterInfo(); + } + + @Override + public void setDisplayImagePreview(final boolean display) { + mDisplayImagePreview = display; + } + + @Override + public void setDisplaySensitiveContents(final boolean display) { + mDisplaySensitiveContents = display; + } + + @Override + public void setFavoritesHightlightDisabled(final boolean disable) { + mFavoritesHighlightDisabled = disable; + } + + @Override + public void setFiltersEnabled(final boolean enabled) { + if (mFiltersEnabled == enabled) return; + mFiltersEnabled = enabled; + rebuildFilterInfo(); + } + + @Override + public void setGapDisallowed(final boolean disallowed) { + mGapDisallowed = disallowed; + } + + @Override + public void setHighlightKeyword(final String... keywords) { + mHighlightKeywords = keywords; + } + + @Override + public void setIgnoredFilterFields(final boolean user, final boolean textPlain, final boolean textHtml, + final boolean source, final boolean retweetedById) { + mFilterIgnoreTextPlain = textPlain; + mFilterIgnoreTextHtml = textHtml; + mFilterIgnoreUser = user; + mFilterIgnoreSource = source; + mFilterRetweetedById = retweetedById; + rebuildFilterInfo(); + } + + @Override + public void setImagePreviewScaleType(final String scaleTypeString) { + final ScaleType scaleType = ScaleType.valueOf(scaleTypeString.toUpperCase(Locale.US)); + mImagePreviewScaleType = scaleType; + } + + @Override + public void setIndicateMyStatusDisabled(final boolean disable) { + mIndicateMyStatusDisabled = disable; + } + + @Override + public void setMaxAnimationPosition(final int position) { + mMaxAnimationPosition = position; + } + + @Override + public void setMentionsHightlightDisabled(final boolean disable) { + mMentionsHighlightDisabled = disable; + } + + @Override + public void setMenuButtonClickListener(final MenuButtonClickListener listener) { + mListener = listener; + } + + private void rebuildFilterInfo() { + if (!isEmpty()) { + final ParcelableStatus last = getItem(super.getCount() - 1); + final long user_id = mFilterIgnoreUser ? -1 : last.user_id; + final String text_plain = mFilterIgnoreTextPlain ? null : last.text_plain; + final String text_html = mFilterIgnoreTextHtml ? null : last.text_html; + final String source = mFilterIgnoreSource ? null : last.source; + final long retweeted_by_id = mFilterRetweetedById ? -1 : last.retweeted_by_id; + mIsLastItemFiltered = isFiltered(mDatabase, user_id, text_plain, text_html, source, retweeted_by_id); + } else { + mIsLastItemFiltered = false; + } + notifyDataSetChanged(); + } + + private static int getItemResource(final boolean compactCards) { + return compactCards ? R.layout.card_item_status_compact : R.layout.card_item_status; + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java index 95896ca2a..ea8c81f11 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java @@ -1,11 +1,23 @@ package org.mariotaku.twidere.adapter.iface; +import android.content.Context; + import org.mariotaku.twidere.model.ParcelableStatus; +import org.mariotaku.twidere.util.ImageLoaderWrapper; +import org.mariotaku.twidere.util.ImageLoadingHandler; /** * Created by mariotaku on 14/11/18. */ public interface IStatusesAdapter { - public ParcelableStatus getStatus(int position); + ImageLoaderWrapper getImageLoader(); + + Context getContext(); + + ImageLoadingHandler getImageLoadingHandler(); + + ParcelableStatus getStatus(int position); + + int getStatusCount(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesListAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesListAdapter.java index c8d6d9212..6d7f8e95f 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesListAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesListAdapter.java @@ -27,8 +27,6 @@ public interface IStatusesListAdapter extends IBaseCardAdapter, IStatusesA public long getAccountId(final int position); - public int getActualCount(); - public ParcelableStatus getLastStatus(); public long getLastStatusId(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesListFragment.java index 6fb67f034..6e29d6abc 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesListFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesListFragment.java @@ -53,7 +53,7 @@ import org.mariotaku.twidere.util.PositionManager; import org.mariotaku.twidere.util.TwitterWrapper; import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.util.collection.NoDuplicatesCopyOnWriteArrayList; -import org.mariotaku.twidere.view.holder.StatusViewHolder; +import org.mariotaku.twidere.view.holder.StatusListViewHolder; import java.util.Collections; import java.util.HashMap; @@ -161,8 +161,8 @@ abstract class BaseStatusesListFragment extends BasePullToRefreshListFragm @Override public boolean onItemLongClick(final AdapterView parent, final View view, final int position, final long id) { final Object tag = view.getTag(); - if (tag instanceof StatusViewHolder) { - final StatusViewHolder holder = (StatusViewHolder) tag; + if (tag instanceof StatusListViewHolder) { + final StatusListViewHolder holder = (StatusListViewHolder) tag; final ParcelableStatus status = mAdapter.getStatus(position - mListView.getHeaderViewsCount()); final AsyncTwitterWrapper twitter = getTwitterWrapper(); if (twitter != null) { @@ -196,7 +196,7 @@ abstract class BaseStatusesListFragment extends BasePullToRefreshListFragm @Override public void onListItemClick(final ListView l, final View v, final int position, final long id) { final Object tag = v.getTag(); - if (tag instanceof StatusViewHolder) { + if (tag instanceof StatusListViewHolder) { final int pos = position - l.getHeaderViewsCount(); final ParcelableStatus status = mAdapter.getStatus(pos); if (status == null) return; @@ -204,8 +204,8 @@ abstract class BaseStatusesListFragment extends BasePullToRefreshListFragm if (twitter != null) { TwitterWrapper.removeUnreadCounts(getActivity(), getTabPosition(), status.account_id, status.id); } - if (((StatusViewHolder) tag).show_as_gap) { - final long since_id = position + 1 < mAdapter.getActualCount() ? mAdapter.getStatus(pos + 1).id : -1; + if (((StatusListViewHolder) tag).show_as_gap) { + final long since_id = position + 1 < mAdapter.getStatusCount() ? mAdapter.getStatus(pos + 1).id : -1; getStatuses(new long[]{status.account_id}, new long[]{status.id}, new long[]{since_id}); mListView.setItemChecked(position, false); } else { diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesStaggeredGridFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesStaggeredGridFragment.java index 4a0ac0d84..13165d26a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesStaggeredGridFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesStaggeredGridFragment.java @@ -62,7 +62,7 @@ import org.mariotaku.twidere.util.PositionManager; import org.mariotaku.twidere.util.TwitterWrapper; import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.util.collection.NoDuplicatesCopyOnWriteArrayList; -import org.mariotaku.twidere.view.holder.StatusViewHolder; +import org.mariotaku.twidere.view.holder.StatusListViewHolder; import java.util.Collections; import java.util.HashMap; @@ -152,8 +152,8 @@ abstract class BaseStatusesStaggeredGridFragment extends BasePullToRefresh @Override public boolean onItemLongClick(final AdapterView parent, final View view, final int position, final long id) { final Object tag = view.getTag(); - if (tag instanceof StatusViewHolder) { - final StatusViewHolder holder = (StatusViewHolder) tag; + if (tag instanceof StatusListViewHolder) { + final StatusListViewHolder holder = (StatusListViewHolder) tag; final ParcelableStatus status = mAdapter.getStatus(position - mListView.getHeaderViewsCount()); final AsyncTwitterWrapper twitter = getTwitterWrapper(); if (twitter != null) { @@ -187,7 +187,7 @@ abstract class BaseStatusesStaggeredGridFragment extends BasePullToRefresh @Override public void onListItemClick(final StaggeredGridView l, final View v, final int position, final long id) { final Object tag = v.getTag(); - if (tag instanceof StatusViewHolder) { + if (tag instanceof StatusListViewHolder) { final int pos = position - l.getHeaderViewsCount(); final ParcelableStatus status = mAdapter.getStatus(pos); if (status == null) return; @@ -195,8 +195,8 @@ abstract class BaseStatusesStaggeredGridFragment extends BasePullToRefresh if (twitter != null) { TwitterWrapper.removeUnreadCounts(getActivity(), getTabPosition(), status.account_id, status.id); } - if (((StatusViewHolder) tag).show_as_gap) { - final long since_id = position + 1 < mAdapter.getActualCount() ? mAdapter.getStatus(pos + 1).id : -1; + if (((StatusListViewHolder) tag).show_as_gap) { + final long since_id = position + 1 < mAdapter.getStatusCount() ? mAdapter.getStatus(pos + 1).id : -1; getStatuses(new long[] { status.account_id }, new long[] { status.id }, new long[] { since_id }); mListView.setItemChecked(position, false); } else { diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesListFragment.java index c26ac5a86..ae3828112 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesListFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesListFragment.java @@ -28,7 +28,7 @@ import android.os.Bundle; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; -import org.mariotaku.twidere.adapter.ParcelableStatusesAdapter; +import org.mariotaku.twidere.adapter.ParcelableStatusesListAdapter; import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter; import org.mariotaku.twidere.loader.support.DummyParcelableStatusesLoader; import org.mariotaku.twidere.model.ParcelableStatus; @@ -149,7 +149,7 @@ public abstract class ParcelableStatusesListFragment extends BaseStatusesListFra public void onSaveInstanceState(final Bundle outState) { final List data = getData(); if (data != null) { - outState.putParcelableArrayList(EXTRA_DATA, new ArrayList(data)); + outState.putParcelableArrayList(EXTRA_DATA, new ArrayList<>(data)); } super.onSaveInstanceState(outState); } @@ -208,8 +208,8 @@ public abstract class ParcelableStatusesListFragment extends BaseStatusesListFra } @Override - protected ParcelableStatusesAdapter newAdapterInstance(final boolean compact, final boolean plain) { - return new ParcelableStatusesAdapter(getActivity(), compact, plain); + protected ParcelableStatusesListAdapter newAdapterInstance(final boolean compact, final boolean plain) { + return new ParcelableStatusesListAdapter(getActivity(), compact, plain); } protected abstract Loader> newLoaderInstance(Context context, Bundle args); diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java index 2ee8636ad..965b23137 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java @@ -71,7 +71,7 @@ import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.support.AccountSelectorActivity; import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity; import org.mariotaku.twidere.activity.support.LinkHandlerActivity; -import org.mariotaku.twidere.adapter.ParcelableStatusesAdapter; +import org.mariotaku.twidere.adapter.ParcelableStatusesListAdapter; import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter; import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.model.Account; @@ -882,7 +882,7 @@ public class StatusFragment extends ParcelableStatusesListFragment implements On final List data = getData(); if (data == null) return; data.add(status); - final ParcelableStatusesAdapter adapter = (ParcelableStatusesAdapter) getListAdapter(); + final ParcelableStatusesListAdapter adapter = (ParcelableStatusesListAdapter) getListAdapter(); adapter.setData(data); if (!mLoadMoreAutomatically && mShouldScroll) { setSelection(0); diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusTranslateDialogFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusTranslateDialogFragment.java index 6b51066df..c5b446c10 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusTranslateDialogFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusTranslateDialogFragment.java @@ -47,7 +47,7 @@ import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.SingleResponse; import org.mariotaku.twidere.util.ImageLoaderWrapper; import org.mariotaku.twidere.util.Utils; -import org.mariotaku.twidere.view.holder.StatusViewHolder; +import org.mariotaku.twidere.view.holder.StatusListViewHolder; import twitter4j.TranslationResult; import twitter4j.Twitter; @@ -56,7 +56,7 @@ import twitter4j.TwitterException; public class StatusTranslateDialogFragment extends BaseSupportDialogFragment implements LoaderCallbacks> { - private StatusViewHolder mHolder; + private StatusListViewHolder mHolder; private ProgressBar mProgressBar; private TextView mMessageView; private View mProgressContainer; @@ -95,7 +95,7 @@ public class StatusTranslateDialogFragment extends BaseSupportDialogFragment imp mProgressBar = (ProgressBar) mProgressContainer.findViewById(android.R.id.progress); mMessageView = (TextView) mProgressContainer.findViewById(android.R.id.message); mStatusContainer = view.findViewById(R.id.status_container); - mHolder = new StatusViewHolder(mStatusContainer); + mHolder = new StatusListViewHolder(mStatusContainer); mHolder.setShowAsGap(false); mHolder.setAccountColorEnabled(true); ((View) mHolder.content).setPadding(0, 0, 0, 0); diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserProfileFragmentOld.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserProfileFragmentOld.java index 173441fbf..38a8538c1 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserProfileFragmentOld.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserProfileFragmentOld.java @@ -1,24 +1,25 @@ /* - * Twidere - Twitter client for Android - * + * Twidere - Twitter client for Android + * * Copyright (C) 2012-2014 Mariotaku Lee - * + * * 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 . */ package org.mariotaku.twidere.fragment.support; +import android.annotation.TargetApi; import android.app.ActionBar; import android.app.Activity; import android.content.ActivityNotFoundException; @@ -32,10 +33,14 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Resources; import android.graphics.Color; +import android.graphics.Outline; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.ShapeDrawable; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.support.v4.app.FragmentActivity; @@ -94,10 +99,10 @@ import org.mariotaku.twidere.util.TwidereLinkify; import org.mariotaku.twidere.util.TwidereLinkify.OnLinkClickListener; import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.util.menu.TwidereMenuInfo; +import org.mariotaku.twidere.view.CircularImageView; import org.mariotaku.twidere.view.ColorLabelLinearLayout; import org.mariotaku.twidere.view.ExtendedFrameLayout; import org.mariotaku.twidere.view.ProfileBannerImageView; -import org.mariotaku.twidere.view.ProfileImageView; import org.mariotaku.twidere.view.TwidereMenuBar; import org.mariotaku.twidere.view.iface.IExtendedView.OnSizeChangedListener; @@ -153,7 +158,7 @@ public class UserProfileFragmentOld extends BaseSupportListFragment implements O private ImageLoaderWrapper mProfileImageLoader; private SharedPreferences mPreferences; - private ProfileImageView mProfileImageView; + private CircularImageView mProfileImageView; private ProfileBannerImageView mProfileBannerView; private TextView mNameView, mScreenNameView, mDescriptionView, mLocationView, mURLView, mCreatedAtView, mTweetCount, mFollowersCount, mFriendsCount, mErrorMessageView; @@ -180,6 +185,7 @@ public class UserProfileFragmentOld extends BaseSupportListFragment implements O private int mBannerWidth; + private Drawable mActionBarShadow; private Drawable mActionBarBackground; private final BroadcastReceiver mStatusReceiver = new BroadcastReceiver() { @@ -344,7 +350,7 @@ public class UserProfileFragmentOld extends BaseSupportListFragment implements O final String nick = getUserNickname(getActivity(), user.id, true); mNameView .setText(TextUtils.isEmpty(nick) ? user.name : getString(R.string.name_with_nickname, user.name, nick)); - mProfileImageView.setUserType(user.is_verified, user.is_protected); +// mProfileImageView.setUserType(user.is_verified, user.is_protected); mScreenNameView.setText("@" + user.screen_name); mDescriptionContainer.setVisibility(userIsMe || !isEmpty(user.description_html) ? View.VISIBLE : View.GONE); mDescriptionView.setText(user.description_html != null ? Html.fromHtml(user.description_html) : null); @@ -501,11 +507,18 @@ public class UserProfileFragmentOld extends BaseSupportListFragment implements O final int themeResId = linkHandler.getCurrentThemeResourceId(); final boolean isTransparent = ThemeUtils.isTransparentBackground(themeResId); final int actionBarAlpha = isTransparent ? ThemeUtils.getUserThemeBackgroundAlpha(linkHandler) : 0xFF; - if (ThemeUtils.isColoredActionBar(themeResId) && useUserActionBar()) { - actionBar.setBackgroundDrawable(mActionBarBackground = new ColorDrawable(themeColor)); - } else { - actionBar.setBackgroundDrawable(mActionBarBackground = ThemeUtils.getActionBarBackground(activity, themeResId)); + mActionBarShadow = activity.getResources().getDrawable(R.drawable.shadow_user_banner_action_bar); + if (mActionBarShadow instanceof ShapeDrawable) { + final ShapeDrawable sd = (ShapeDrawable) mActionBarBackground; + sd.setIntrinsicHeight(actionBar.getHeight()); + sd.setIntrinsicWidth(activity.getWindowManager().getDefaultDisplay().getWidth()); } + if (ThemeUtils.isColoredActionBar(themeResId) && useUserActionBar()) { + mActionBarBackground = new ColorDrawable(themeColor); + } else { + mActionBarBackground = ThemeUtils.getActionBarBackground(activity, themeResId); + } + actionBar.setBackgroundDrawable(new ActionBarDrawable(mActionBarShadow, mActionBarBackground)); } private boolean useUserActionBar() { @@ -722,8 +735,9 @@ public class UserProfileFragmentOld extends BaseSupportListFragment implements O profileBannerView.setTranslationY((headerView.getTop() - listView.getListPaddingTop()) / 2); profileBannerView.setBottomClip(headerScroll); - if (mActionBarBackground != null) { + if (mActionBarShadow != null && mActionBarBackground != null) { final float f = headerScroll / (float) mProfileBannerSpace.getHeight(); + mActionBarShadow.setAlpha(Math.round(0xFF * MathUtils.clamp(1 - f, 0, 1))); mActionBarBackground.setAlpha(Math.round(0xFF * MathUtils.clamp(f, 0, 1))); } } @@ -797,7 +811,7 @@ public class UserProfileFragmentOld extends BaseSupportListFragment implements O mFriendsContainer = mHeaderView.findViewById(R.id.friends_container); mFriendsCount = (TextView) mHeaderView.findViewById(R.id.friends_count); mProfileNameContainer = (ColorLabelLinearLayout) mHeaderView.findViewById(R.id.profile_name_container); - mProfileImageView = (ProfileImageView) mHeaderView.findViewById(R.id.profile_image); + mProfileImageView = (CircularImageView) mHeaderView.findViewById(R.id.profile_image); mDescriptionContainer = mHeaderView.findViewById(R.id.description_container); mLocationContainer = mHeaderView.findViewById(R.id.location_container); mURLContainer = mHeaderView.findViewById(R.id.url_container); @@ -1247,4 +1261,28 @@ public class UserProfileFragmentOld extends BaseSupportListFragment implements O } } + private static class ActionBarDrawable extends LayerDrawable { + private final Drawable mBackgroundDrawable; + + public ActionBarDrawable(Drawable shadow, Drawable background) { + super(new Drawable[]{shadow, background}); + mBackgroundDrawable = background; + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public void getOutline(Outline outline) { + mBackgroundDrawable.getOutline(outline); + } + + @Override + public int getIntrinsicWidth() { + return mBackgroundDrawable.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mBackgroundDrawable.getIntrinsicHeight(); + } + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserTimelineFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserTimelineFragment.java index bd1257b48..43833959a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserTimelineFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserTimelineFragment.java @@ -1,94 +1,139 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 Mariotaku Lee - * - * 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 . - */ - package org.mariotaku.twidere.fragment.support; import android.content.Context; +import android.graphics.Color; +import android.graphics.Rect; import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.OnScrollListener; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; -import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter; +import org.mariotaku.twidere.R; +import org.mariotaku.twidere.adapter.ParcelableStatusesAdapter; +import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration; import org.mariotaku.twidere.loader.support.UserTimelineLoader; import org.mariotaku.twidere.model.ParcelableStatus; +import org.mariotaku.twidere.util.Utils; import java.util.List; -import static org.mariotaku.twidere.util.Utils.getAccountId; -import static org.mariotaku.twidere.util.Utils.getAccountScreenName; +/** + * Created by mariotaku on 14/11/5. + */ +public class UserTimelineFragment extends BaseSupportFragment + implements LoaderCallbacks>, OnRefreshListener { -public class UserTimelineFragment extends ParcelableStatusesListFragment { + private SwipeRefreshLayout mSwipeRefreshLayout; + private RecyclerView mRecyclerView; + + private ParcelableStatusesAdapter mAdapter; + private OnScrollListener mOnScrollListener = new OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + final LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); + final LoaderManager lm = getLoaderManager(); + if (lm.hasRunningLoaders()) return; + if (layoutManager.findLastVisibleItemPosition() == mAdapter.getItemCount() - 1) { + getStatuses(mAdapter.getStatus(mAdapter.getStatusCount() - 1).id, 0); + } + } + }; @Override - public Loader> newLoaderInstance(final Context context, final Bundle args) { - if (args == null) return null; + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + final View view = getView(); + assert view != null; + final Context context = view.getContext(); + final boolean compact = Utils.isCompactCards(context); + mSwipeRefreshLayout.setOnRefreshListener(this); + mSwipeRefreshLayout.setColorSchemeColors(Color.RED, Color.GREEN, Color.BLUE); + mAdapter = new ParcelableStatusesAdapter(context, compact); + final LinearLayoutManager layoutManager = new LinearLayoutManager(context); + layoutManager.setOrientation(LinearLayoutManager.VERTICAL); + mRecyclerView.setLayoutManager(layoutManager); + if (compact) { + mRecyclerView.addItemDecoration(new DividerItemDecoration(context, layoutManager.getOrientation())); + } + mRecyclerView.setAdapter(mAdapter); + mRecyclerView.setOnScrollListener(mOnScrollListener); + getLoaderManager().initLoader(0, getArguments(), this); + } + + + public int getStatuses(final long maxId, final long sinceId) { + final Bundle args = new Bundle(getArguments()); + args.putLong(EXTRA_MAX_ID, maxId); + args.putLong(EXTRA_SINCE_ID, sinceId); + getLoaderManager().restartLoader(0, args, this); + return -1; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_layout); + mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view); + } + + @Override + protected void fitSystemWindows(Rect insets) { + super.fitSystemWindows(insets); + mRecyclerView.setClipToPadding(false); + mRecyclerView.setPadding(insets.left, insets.top, insets.right, insets.bottom); + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_recycler_view, container, false); + } + + @Override + public Loader> onCreateLoader(int id, Bundle args) { + mSwipeRefreshLayout.setRefreshing(true); + final List data = mAdapter.getData(); + final Context context = getActivity(); final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1); final long maxId = args.getLong(EXTRA_MAX_ID, -1); final long sinceId = args.getLong(EXTRA_SINCE_ID, -1); final long userId = args.getLong(EXTRA_USER_ID, -1); final String screenName = args.getString(EXTRA_SCREEN_NAME); final int tabPosition = args.getInt(EXTRA_TAB_POSITION, -1); - return new UserTimelineLoader(context, accountId, userId, screenName, maxId, sinceId, getData(), - getSavedStatusesFileArgs(), tabPosition); + return new UserTimelineLoader(context, accountId, userId, screenName, maxId, sinceId, data, + null, tabPosition); } @Override - public void onActivityCreated(final Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - final Bundle args = getArguments(); - final long accountId = args != null ? args.getLong(EXTRA_ACCOUNT_ID, -1) : -1; - final long userId = args != null ? args.getLong(EXTRA_USER_ID, -1) : -1; - final String screenName = args != null ? args.getString(EXTRA_SCREEN_NAME) : null; - final boolean isMyTimeline = userId > 0 ? accountId == userId : accountId == getAccountId(getActivity(), - screenName); - final IStatusesListAdapter> adapter = getListAdapter(); - adapter.setIndicateMyStatusDisabled(isMyTimeline); - adapter.setFiltersEnabled(!isMyTimeline); - adapter.setIgnoredFilterFields(true, false, false, false, false); + public void onLoadFinished(Loader> loader, List data) { + mSwipeRefreshLayout.setRefreshing(false); + mAdapter.setData(data); } @Override - protected String[] getSavedStatusesFileArgs() { - final Bundle args = getArguments(); - if (args == null) return null; - final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1); - final long userId = args.getLong(EXTRA_USER_ID, -1); - final String screenName = args.getString(EXTRA_SCREEN_NAME); - return new String[]{AUTHORITY_USER_TIMELINE, "account" + accountId, "user" + userId + "name" + screenName}; + public void onLoaderReset(Loader> loader) { } + @Override - protected boolean isMyTimeline() { - final Bundle args = getArguments(); - if (args != null) { - final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1); - final long userId = args.getLong(EXTRA_USER_ID, -1); - final String screenName = args.getString(EXTRA_SCREEN_NAME); - if (accountId == userId || screenName != null - && screenName.equals(getAccountScreenName(getActivity(), accountId))) - return true; + public void onRefresh() { + if (mAdapter.getStatusCount() > 0) { + getStatuses(0, mAdapter.getStatus(0).id); + } else { + getStatuses(0, 0); } - return false; } - - @Override - protected boolean shouldShowAccountColor() { - return false; - } - } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserTimelineFragment2.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserTimelineFragment2.java deleted file mode 100644 index e21dc9bb1..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserTimelineFragment2.java +++ /dev/null @@ -1,339 +0,0 @@ -package org.mariotaku.twidere.fragment.support; - -import android.content.Context; -import android.content.Intent; -import android.graphics.Rect; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.LoaderManager.LoaderCallbacks; -import android.support.v4.content.Loader; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.RecyclerView.Adapter; -import android.support.v7.widget.RecyclerView.OnScrollListener; -import android.support.v7.widget.RecyclerView.ViewHolder; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.pkmmte.view.CircularImageView; - -import org.mariotaku.twidere.R; -import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration; -import org.mariotaku.twidere.adapter.iface.IStatusesAdapter; -import org.mariotaku.twidere.app.TwidereApplication; -import org.mariotaku.twidere.loader.support.UserTimelineLoader; -import org.mariotaku.twidere.model.ParcelableMedia; -import org.mariotaku.twidere.model.ParcelableStatus; -import org.mariotaku.twidere.util.ImageLoaderWrapper; -import org.mariotaku.twidere.util.ImageLoadingHandler; -import org.mariotaku.twidere.util.ThemeUtils; -import org.mariotaku.twidere.util.Utils; -import org.mariotaku.twidere.view.ShortTimeView; - -import java.util.List; -import java.util.Locale; - -/** - * Created by mariotaku on 14/11/5. - */ -public class UserTimelineFragment2 extends BaseSupportFragment - implements LoaderCallbacks> { - - private RecyclerView mRecyclerView; - private ProgressBar mProgress; - - private ParcelableTimelineAdapter mAdapter; - private OnScrollListener mOnScrollListener = new OnScrollListener() { - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - } - }; - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - final View view = getView(); - assert view != null; - final Context context = view.getContext(); - final boolean compact = Utils.isCompactCards(context); - mAdapter = new ParcelableTimelineAdapter(context, compact); - final LinearLayoutManager layoutManager = new LinearLayoutManager(context); - layoutManager.setOrientation(LinearLayoutManager.VERTICAL); - mRecyclerView.setLayoutManager(layoutManager); - if (compact) { - mRecyclerView.addItemDecoration(new DividerItemDecoration(context, layoutManager.getOrientation())); - } - mRecyclerView.setAdapter(mAdapter); - mRecyclerView.setOnScrollListener(mOnScrollListener); - getLoaderManager().initLoader(0, getArguments(), this); - setListShown(false); - } - - public void setListShown(boolean shown) { - mRecyclerView.setVisibility(shown ? View.VISIBLE : View.GONE); - mProgress.setVisibility(shown ? View.GONE : View.VISIBLE); - } - - public int getStatuses(final long maxId, final long sinceId) { - final Bundle args = new Bundle(getArguments()); - args.putLong(EXTRA_MAX_ID, maxId); - args.putLong(EXTRA_SINCE_ID, sinceId); - getLoaderManager().restartLoader(0, args, this); - return -1; - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - mRecyclerView = (RecyclerView) view.findViewById(android.R.id.list); - mProgress = (ProgressBar) view.findViewById(android.R.id.progress); - } - - @Override - protected void fitSystemWindows(Rect insets) { - super.fitSystemWindows(insets); - mRecyclerView.setClipToPadding(false); - mRecyclerView.setPadding(insets.left, insets.top, insets.right, insets.bottom); - } - - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_recycler_view, container, false); - } - - @Override - public Loader> onCreateLoader(int id, Bundle args) { - final Context context = getActivity(); - final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1); - final long maxId = args.getLong(EXTRA_MAX_ID, -1); - final long sinceId = args.getLong(EXTRA_SINCE_ID, -1); - final long userId = args.getLong(EXTRA_USER_ID, -1); - final String screenName = args.getString(EXTRA_SCREEN_NAME); - final int tabPosition = args.getInt(EXTRA_TAB_POSITION, -1); - return new UserTimelineLoader(context, accountId, userId, screenName, maxId, sinceId, null, - null, tabPosition); - } - - @Override - public void onLoadFinished(Loader> loader, List data) { - mAdapter.setData(data); - setListShown(true); - } - - @Override - public void onLoaderReset(Loader> loader) { - mAdapter.setData(null); - } - - private static class ParcelableTimelineAdapter extends Adapter implements IStatusesAdapter, OnClickListener { - - private static final int ITEM_VIEW_TYPE_STATUS = 1; - private static final int ITEM_VIEW_TYPE_LOAD_INDICATOR = 2; - - private final Context mContext; - private final LayoutInflater mInflater; - private final ImageLoaderWrapper mImageLoader; - private final ImageLoadingHandler mLoadingHandler; - private final int mCardLayoutResource; - private List mData; - private boolean mHasMoreItem; - - ParcelableTimelineAdapter(Context context, boolean compact) { - mContext = context; - mInflater = LayoutInflater.from(context); - mImageLoader = TwidereApplication.getInstance(context).getImageLoaderWrapper(); - mLoadingHandler = new ImageLoadingHandler(R.id.media_preview_progress); - if (compact) { - mCardLayoutResource = R.layout.card_item_list_status_compat; - } else { - mCardLayoutResource = R.layout.card_item_list_status; - } - } - - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - switch (viewType) { - case ITEM_VIEW_TYPE_STATUS: { - final View view = mInflater.inflate(mCardLayoutResource, parent, false); - return new MediaTimelineViewHolder(this, view); - } - case ITEM_VIEW_TYPE_LOAD_INDICATOR: { - final View view = mInflater.inflate(R.layout.card_item_load_indicator, parent, false); - return new LoadIndicatorViewHolder(view); - } - } - throw new IllegalStateException("Unknown view type " + viewType); - } - - public void setHasMoreItem(boolean hasMoreItem) { - if (mHasMoreItem == hasMoreItem) return; - mHasMoreItem = hasMoreItem; - notifyDataSetChanged(); - } - - @Override - public void onBindViewHolder(ViewHolder holder, int position) { - if (mData == null) return; - if (holder instanceof MediaTimelineViewHolder) { - ((MediaTimelineViewHolder) holder).displayStatus(this, getStatus(position)); - } - } - - public void setData(List data) { - mData = data; - notifyDataSetChanged(); - } - - @Override - public int getItemViewType(int position) { - if (position == getItemCount() - 1) { - return ITEM_VIEW_TYPE_LOAD_INDICATOR; - } - return ITEM_VIEW_TYPE_STATUS; - } - - @Override - public int getItemCount() { - if (mData == null) return mHasMoreItem ? 1 : 0; - return mData.size() + (mHasMoreItem ? 1 : 0); - } - - public ImageLoaderWrapper getImageLoader() { - return mImageLoader; - } - - public Context getContext() { - return mContext; - } - - public ImageLoadingHandler getImageLoadingHandler() { - return mLoadingHandler; - } - - @Override - public void onClick(View v) { - - } - - - @Override - public ParcelableStatus getStatus(int position) { - if (mData == null || (mHasMoreItem && position == mData.size() - 1)) return null; - return mData.get(position); - } - } - - private static class LoadIndicatorViewHolder extends ViewHolder { - public LoadIndicatorViewHolder(View view) { - super(view); - } - } - - private static class MediaTimelineViewHolder extends ViewHolder implements OnClickListener { - - private final IStatusesAdapter adapter; - - private final ImageView mediaPreviewView; - private final CircularImageView profileImageView; - private final TextView textView; - private final TextView nameView; - private final ShortTimeView timeView; - private final View mediaPreviewContainer; - private final View replyIndicator, retweetIndicator, favoriteIndicator; - private final TextView replyCountView, retweetCountView, favoriteCountView; - - public MediaTimelineViewHolder(IStatusesAdapter adapter, View itemView) { - super(itemView); - this.adapter = adapter; - itemView.findViewById(R.id.item_content).setOnClickListener(this); - profileImageView = (CircularImageView) itemView.findViewById(R.id.profile_image); - textView = (TextView) itemView.findViewById(R.id.text); - nameView = (TextView) itemView.findViewById(R.id.name); - timeView = (ShortTimeView) itemView.findViewById(R.id.time); - - mediaPreviewContainer = itemView.findViewById(R.id.media_preview_container); - mediaPreviewView = (ImageView) itemView.findViewById(R.id.media_preview); - - replyIndicator = itemView.findViewById(R.id.reply_indicator); - retweetIndicator = itemView.findViewById(R.id.retweet_indicator); - favoriteIndicator = itemView.findViewById(R.id.favorite_indicator); - - replyCountView = (TextView) itemView.findViewById(R.id.reply_count); - retweetCountView = (TextView) itemView.findViewById(R.id.retweet_count); - favoriteCountView = (TextView) itemView.findViewById(R.id.favorite_count); - - profileImageView.setSelectorColor(ThemeUtils.getUserHighlightColor(itemView.getContext())); - - itemView.setOnClickListener(this); - profileImageView.setOnClickListener(this); - mediaPreviewContainer.setOnClickListener(this); - replyIndicator.setOnClickListener(this); - retweetIndicator.setOnClickListener(this); - favoriteIndicator.setOnClickListener(this); - } - - public void displayStatus(ParcelableTimelineAdapter adapter, ParcelableStatus status) { - final ImageLoaderWrapper loader = adapter.getImageLoader(); - final Context context = adapter.getContext(); - final ParcelableMedia[] media = status.media; - - nameView.setText(status.user_name); - timeView.setTime(status.timestamp); - loader.displayProfileImage(profileImageView, status.user_profile_image_url); - -// profileImageView.setBorderColor(UserColorNicknameUtils.getUserColor(context, status.user_id)); - if (media != null && media.length > 0) { - final ParcelableMedia firstMedia = media[0]; - if (status.text_plain.codePointCount(0, status.text_plain.length()) == firstMedia.end) { - textView.setText(status.text_unescaped.substring(0, firstMedia.start)); - } else { - textView.setText(status.text_unescaped); - } - loader.displayPreviewImageWithCredentials(mediaPreviewView, firstMedia.media_url, - status.account_id, adapter.getImageLoadingHandler()); - mediaPreviewContainer.setVisibility(View.VISIBLE); - } else { - loader.cancelDisplayTask(mediaPreviewView); - textView.setText(status.text_unescaped); - mediaPreviewContainer.setVisibility(View.GONE); - } - retweetIndicator.setActivated(status.is_retweet); - favoriteIndicator.setActivated(status.is_favorite); - - replyCountView.setVisibility(status.reply_count > 0 ? View.VISIBLE : View.INVISIBLE); - retweetCountView.setVisibility(status.retweet_count > 0 ? View.VISIBLE : View.INVISIBLE); - favoriteCountView.setVisibility(status.favorite_count > 0 ? View.VISIBLE : View.INVISIBLE); - replyCountView.setText(Utils.getLocalizedNumber(Locale.getDefault(), status.reply_count)); - retweetCountView.setText(Utils.getLocalizedNumber(Locale.getDefault(), status.retweet_count)); - favoriteCountView.setText(Utils.getLocalizedNumber(Locale.getDefault(), status.favorite_count)); - } - - @Override - public void onClick(View v) { - final Context context = itemView.getContext(); - final ParcelableStatus status = adapter.getStatus(getPosition()); - switch (v.getId()) { - case R.id.item_content: { - Utils.openStatus(context, status); - break; - } - case R.id.profile_image: { - Utils.openUserProfile(context, status.account_id, status.user_id, status.user_screen_name); - break; - } - case R.id.reply_indicator: { - final Intent intent = new Intent(INTENT_ACTION_REPLY); - intent.setPackage(context.getPackageName()); - intent.putExtra(EXTRA_STATUS, status); - context.startActivity(intent); - break; - } - } - } - } -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserTimelineFragmentOld.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserTimelineFragmentOld.java new file mode 100644 index 000000000..d8839de9e --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserTimelineFragmentOld.java @@ -0,0 +1,94 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2014 Mariotaku Lee + * + * 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 . + */ + +package org.mariotaku.twidere.fragment.support; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.content.Loader; + +import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter; +import org.mariotaku.twidere.loader.support.UserTimelineLoader; +import org.mariotaku.twidere.model.ParcelableStatus; + +import java.util.List; + +import static org.mariotaku.twidere.util.Utils.getAccountId; +import static org.mariotaku.twidere.util.Utils.getAccountScreenName; + +public class UserTimelineFragmentOld extends ParcelableStatusesListFragment { + + @Override + public Loader> newLoaderInstance(final Context context, final Bundle args) { + if (args == null) return null; + final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1); + final long maxId = args.getLong(EXTRA_MAX_ID, -1); + final long sinceId = args.getLong(EXTRA_SINCE_ID, -1); + final long userId = args.getLong(EXTRA_USER_ID, -1); + final String screenName = args.getString(EXTRA_SCREEN_NAME); + final int tabPosition = args.getInt(EXTRA_TAB_POSITION, -1); + return new UserTimelineLoader(context, accountId, userId, screenName, maxId, sinceId, getData(), + getSavedStatusesFileArgs(), tabPosition); + } + + @Override + public void onActivityCreated(final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + final Bundle args = getArguments(); + final long accountId = args != null ? args.getLong(EXTRA_ACCOUNT_ID, -1) : -1; + final long userId = args != null ? args.getLong(EXTRA_USER_ID, -1) : -1; + final String screenName = args != null ? args.getString(EXTRA_SCREEN_NAME) : null; + final boolean isMyTimeline = userId > 0 ? accountId == userId : accountId == getAccountId(getActivity(), + screenName); + final IStatusesListAdapter> adapter = getListAdapter(); + adapter.setIndicateMyStatusDisabled(isMyTimeline); + adapter.setFiltersEnabled(!isMyTimeline); + adapter.setIgnoredFilterFields(true, false, false, false, false); + } + + @Override + protected String[] getSavedStatusesFileArgs() { + final Bundle args = getArguments(); + if (args == null) return null; + final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1); + final long userId = args.getLong(EXTRA_USER_ID, -1); + final String screenName = args.getString(EXTRA_SCREEN_NAME); + return new String[]{AUTHORITY_USER_TIMELINE, "account" + accountId, "user" + userId + "name" + screenName}; + } + + @Override + protected boolean isMyTimeline() { + final Bundle args = getArguments(); + if (args != null) { + final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1); + final long userId = args.getLong(EXTRA_USER_ID, -1); + final String screenName = args.getString(EXTRA_SCREEN_NAME); + if (accountId == userId || screenName != null + && screenName.equals(getAccountScreenName(getActivity(), accountId))) + return true; + } + return false; + } + + @Override + protected boolean shouldShowAccountColor() { + return false; + } + +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/loader/support/ParcelableStatusesLoader.java b/twidere/src/main/java/org/mariotaku/twidere/loader/support/ParcelableStatusesLoader.java index dab30412e..00e2effee 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/loader/support/ParcelableStatusesLoader.java +++ b/twidere/src/main/java/org/mariotaku/twidere/loader/support/ParcelableStatusesLoader.java @@ -30,62 +30,62 @@ import java.util.List; public abstract class ParcelableStatusesLoader extends AsyncTaskLoader> implements Constants { - private final List mData = new NoDuplicatesArrayList(); - private final boolean mFirstLoad; - private final int mTabPosition; + private final List mData = new NoDuplicatesArrayList<>(); + private final boolean mFirstLoad; + private final int mTabPosition; - private Long mLastViewedId; + private Long mLastViewedId; - public ParcelableStatusesLoader(final Context context, final List data, final int tab_position) { - super(context); - mFirstLoad = data == null; - if (data != null) { - mData.addAll(data); - } - mTabPosition = tab_position; - } + public ParcelableStatusesLoader(final Context context, final List data, final int tab_position) { + super(context); + mFirstLoad = data == null; + if (data != null) { + mData.addAll(data); + } + mTabPosition = tab_position; + } - public Long getLastViewedId() { - return mLastViewedId; - } + public Long getLastViewedId() { + return mLastViewedId; + } - protected boolean containsStatus(final long status_id) { - for (final ParcelableStatus status : mData) { - if (status.id == status_id) return true; - } - return false; - } + protected boolean containsStatus(final long status_id) { + for (final ParcelableStatus status : mData) { + if (status.id == status_id) return true; + } + return false; + } - protected boolean deleteStatus(final List statuses, final long status_id) { - if (statuses == null || statuses.isEmpty()) return false; - boolean result = false; - for (final ParcelableStatus status : statuses.toArray(new ParcelableStatus[statuses.size()])) { - if (status.id == status_id) { - result |= statuses.remove(status); - } - } - return result; - } + protected boolean deleteStatus(final List statuses, final long status_id) { + if (statuses == null || statuses.isEmpty()) return false; + boolean result = false; + for (final ParcelableStatus status : statuses.toArray(new ParcelableStatus[statuses.size()])) { + if (status.id == status_id) { + result |= statuses.remove(status); + } + } + return result; + } - protected List getData() { - return mData; - } + protected List getData() { + return mData; + } - protected int getTabPosition() { - return mTabPosition; - } + protected int getTabPosition() { + return mTabPosition; + } - protected boolean isFirstLoad() { - return mFirstLoad; - } + protected boolean isFirstLoad() { + return mFirstLoad; + } - @Override - protected void onStartLoading() { - forceLoad(); - } + @Override + protected void onStartLoading() { + forceLoad(); + } - protected void setLastViewedId(final Long id) { - mLastViewedId = id; - } + protected void setLastViewedId(final Long id) { + mLastViewedId = id; + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/loader/support/Twitter4JStatusesLoader.java b/twidere/src/main/java/org/mariotaku/twidere/loader/support/Twitter4JStatusesLoader.java index 71e731b86..71aac3387 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/loader/support/Twitter4JStatusesLoader.java +++ b/twidere/src/main/java/org/mariotaku/twidere/loader/support/Twitter4JStatusesLoader.java @@ -55,16 +55,16 @@ public abstract class Twitter4JStatusesLoader extends ParcelableStatusesLoader { private final Object[] mSavedStatusesFileArgs; public Twitter4JStatusesLoader(final Context context, final long account_id, final long max_id, - final long since_id, final List data, final String[] saved_statuses_args, - final int tab_position) { - super(context, data, tab_position); + final long since_id, final List data, final String[] savedStatusesArgs, + final int tabPosition) { + super(context, data, tabPosition); mContext = context; mAccountId = account_id; mMaxId = max_id; mSinceId = since_id; mDatabase = TwidereApplication.getInstance(context).getSQLiteDatabase(); mHandler = new Handler(); - mSavedStatusesFileArgs = saved_statuses_args; + mSavedStatusesFileArgs = savedStatusesArgs; } @SuppressWarnings("unchecked") @@ -118,7 +118,7 @@ public abstract class Twitter4JStatusesLoader extends ParcelableStatusesLoader { } } saveCachedData(serializationFile, data); - return new CopyOnWriteArrayList(data); + return new CopyOnWriteArrayList<>(data); } protected abstract List getStatuses(Twitter twitter, Paging paging) throws TwitterException; diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/CustomTabConfiguration2.java b/twidere/src/main/java/org/mariotaku/twidere/model/CustomTabConfiguration2.java index 362e3c9ea..2d01de031 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/model/CustomTabConfiguration2.java +++ b/twidere/src/main/java/org/mariotaku/twidere/model/CustomTabConfiguration2.java @@ -32,7 +32,7 @@ import org.mariotaku.twidere.fragment.support.SearchStatusesFragment; import org.mariotaku.twidere.fragment.support.TrendsSuggectionsFragment; import org.mariotaku.twidere.fragment.support.UserFavoritesFragment; import org.mariotaku.twidere.fragment.support.UserListTimelineFragment; -import org.mariotaku.twidere.fragment.support.UserTimelineFragment; +import org.mariotaku.twidere.fragment.support.UserTimelineFragmentOld; import java.util.Comparator; import java.util.Map.Entry; @@ -54,7 +54,7 @@ public enum CustomTabConfiguration2 implements Constants { FAVORITES(UserFavoritesFragment.class, R.string.favorites, R.drawable.ic_action_star, CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_USER, 4), - USER_TIMELINE(UserTimelineFragment.class, R.string.users_statuses, R.drawable.ic_action_quote, + USER_TIMELINE(UserTimelineFragmentOld.class, R.string.users_statuses, R.drawable.ic_action_quote, CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_USER, 5), SEARCH_STATUSES(SearchStatusesFragment.class, R.string.search_statuses, R.drawable.ic_action_search, diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/ParcelableStatus.java b/twidere/src/main/java/org/mariotaku/twidere/model/ParcelableStatus.java index d6cc89fa2..6ac036bb9 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/model/ParcelableStatus.java +++ b/twidere/src/main/java/org/mariotaku/twidere/model/ParcelableStatus.java @@ -99,9 +99,9 @@ public class ParcelableStatus implements TwidereParcelable, Comparable * Type: INTEGER (long) @@ -723,18 +725,18 @@ public interface TweetStore { public static final String[] COLUMNS = new String[]{_ID, ACCOUNT_ID, STATUS_ID, USER_ID, STATUS_TIMESTAMP, TEXT_HTML, TEXT_PLAIN, TEXT_UNESCAPED, USER_NAME, USER_SCREEN_NAME, USER_PROFILE_IMAGE_URL, IN_REPLY_TO_STATUS_ID, IN_REPLY_TO_USER_ID, IN_REPLY_TO_USER_NAME, - IN_REPLY_TO_USER_SCREEN_NAME, SOURCE, LOCATION, RETWEET_COUNT, FAVORITE_COUNT, - REPLY_COUNT, DESCENDENT_REPLY_COUNT, RETWEET_ID, RETWEET_TIMESTAMP, - RETWEETED_BY_USER_ID, RETWEETED_BY_USER_NAME, RETWEETED_BY_USER_SCREEN_NAME, - MY_RETWEET_ID, IS_RETWEET, IS_FAVORITE, IS_PROTECTED, IS_VERIFIED, IS_FOLLOWING, - IS_GAP, IS_POSSIBLY_SENSITIVE, MEDIA, FIRST_MEDIA, MENTIONS}; + IN_REPLY_TO_USER_SCREEN_NAME, SOURCE, LOCATION, RETWEET_COUNT, FAVORITE_COUNT, REPLY_COUNT, + DESCENDENT_REPLY_COUNT, RETWEET_ID, RETWEET_TIMESTAMP, RETWEETED_BY_USER_ID, + RETWEETED_BY_USER_NAME, RETWEETED_BY_USER_SCREEN_NAME, RETWEETED_BY_USER_PROFILE_IMAGE, + MY_RETWEET_ID, IS_RETWEET, IS_FAVORITE, IS_PROTECTED, IS_VERIFIED, IS_FOLLOWING, IS_GAP, + IS_POSSIBLY_SENSITIVE, MEDIA, FIRST_MEDIA, MENTIONS}; public static final String[] TYPES = new String[]{TYPE_PRIMARY_KEY, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, - TYPE_INT, TYPE_INT, TYPE_INT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_INT, - TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_TEXT, TYPE_TEXT, TYPE_INT, - TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, - TYPE_BOOLEAN, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT}; + TYPE_INT, TYPE_INT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_INT, TYPE_INT, + TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_TEXT, TYPE_TEXT, + TYPE_TEXT, TYPE_INT, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, + TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT}; } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java b/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java index c15835fc1..cf9c2a719 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java @@ -255,10 +255,10 @@ public final class ContentValuesCreator implements TwidereConstants { return values; } - public static ContentValues makeStatusContentValues(final Status orig, final long account_id) { + public static ContentValues makeStatusContentValues(final Status orig, final long accountId) { if (orig == null || orig.getId() <= 0) return null; final ContentValues values = new ContentValues(); - values.put(Statuses.ACCOUNT_ID, account_id); + values.put(Statuses.ACCOUNT_ID, accountId); values.put(Statuses.STATUS_ID, orig.getId()); values.put(Statuses.STATUS_TIMESTAMP, orig.getCreatedAt().getTime()); values.put(Statuses.MY_RETWEET_ID, orig.getCurrentUserRetweet()); @@ -272,6 +272,7 @@ public final class ContentValuesCreator implements TwidereConstants { values.put(Statuses.RETWEETED_BY_USER_ID, retweetUser.getId()); values.put(Statuses.RETWEETED_BY_USER_NAME, retweetUser.getName()); values.put(Statuses.RETWEETED_BY_USER_SCREEN_NAME, retweetUser.getScreenName()); + values.put(Statuses.RETWEETED_BY_USER_PROFILE_IMAGE, ParseUtils.parseString(retweetUser.getProfileImageUrlHttps())); status = retweetedStatus; } else { status = orig; diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/CustomTabUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/CustomTabUtils.java index 2780f3b4a..7edd59e85 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/CustomTabUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/CustomTabUtils.java @@ -46,7 +46,7 @@ import org.mariotaku.twidere.fragment.support.StaggeredHomeTimelineFragment; import org.mariotaku.twidere.fragment.support.TrendsSuggectionsFragment; import org.mariotaku.twidere.fragment.support.UserFavoritesFragment; import org.mariotaku.twidere.fragment.support.UserListTimelineFragment; -import org.mariotaku.twidere.fragment.support.UserTimelineFragment; +import org.mariotaku.twidere.fragment.support.UserTimelineFragmentOld; import org.mariotaku.twidere.model.CustomTabConfiguration; import org.mariotaku.twidere.model.CustomTabConfiguration.ExtraConfiguration; import org.mariotaku.twidere.model.SupportTabSpec; @@ -81,7 +81,7 @@ public class CustomTabUtils implements Constants { R.string.favorites, R.drawable.ic_action_star, CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_USER, 4)); CUSTOM_TABS_CONFIGURATION_MAP.put(TAB_TYPE_USER_TIMELINE, new CustomTabConfiguration( - UserTimelineFragment.class, R.string.users_statuses, R.drawable.ic_action_quote, + UserTimelineFragmentOld.class, R.string.users_statuses, R.drawable.ic_action_quote, CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_USER, 5)); CUSTOM_TABS_CONFIGURATION_MAP.put(TAB_TYPE_SEARCH_STATUSES, new CustomTabConfiguration( SearchStatusesFragment.class, R.string.search_statuses, R.drawable.ic_action_search, diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/ThemeUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/ThemeUtils.java index 30ed35846..eca403164 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/ThemeUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/ThemeUtils.java @@ -62,7 +62,7 @@ public class ThemeUtils implements Constants { private static final int[] ANIM_CLOSE_STYLE_ATTRS = {android.R.attr.activityCloseEnterAnimation, android.R.attr.activityCloseExitAnimation}; - private static final String[] sClassPrefixList = {"android.widget.", "android.webkit."}; + private static final String[] sClassPrefixList = {"android.widget.", "android.webkit.", "org.mariotaku.twidere.view"}; private ThemeUtils() { throw new AssertionError(); @@ -181,6 +181,9 @@ public class ThemeUtils implements Constants { public static RefreshNowConfig buildRefreshNowConfig(final Context context) { final RefreshNowConfig.Builder builder = new RefreshNowConfig.Builder(context); + builder.minPullDivisor(2); + builder.extraPullDivisor(1); + builder.maxOverScrollDistance(72); return builder.build(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java index 85e703698..745bbf5d5 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java @@ -61,6 +61,7 @@ import android.os.Bundle; import android.os.SystemClock; import android.provider.BaseColumns; import android.provider.MediaStore; +import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; @@ -143,7 +144,7 @@ import org.mariotaku.twidere.fragment.support.UserListsListFragment; import org.mariotaku.twidere.fragment.support.UserMediaTimelineFragment; import org.mariotaku.twidere.fragment.support.UserMentionsFragment; import org.mariotaku.twidere.fragment.support.UserProfileFragmentOld; -import org.mariotaku.twidere.fragment.support.UserTimelineFragment2; +import org.mariotaku.twidere.fragment.support.UserTimelineFragment; import org.mariotaku.twidere.fragment.support.UsersListFragment; import org.mariotaku.twidere.graphic.PaddingDrawable; import org.mariotaku.twidere.model.Account; @@ -746,7 +747,7 @@ public final class Utils implements Constants, TwitterConstants { break; } case LINK_ID_USER_TIMELINE: { - fragment = new UserTimelineFragment2(); + fragment = new UserTimelineFragment(); final String paramScreenName = uri.getQueryParameter(QUERY_PARAM_SCREEN_NAME); final String paramUserId = uri.getQueryParameter(QUERY_PARAM_USER_ID); if (!args.containsKey(EXTRA_SCREEN_NAME)) { @@ -1841,14 +1842,14 @@ public final class Utils implements Constants, TwitterConstants { FORMAT_PATTERN_TEXT, text); } - public static String getInReplyToName(final Status status) { - if (status == null) return null; + @NonNull + public static String getInReplyToName(@NonNull final Status status) { final Status orig = status.isRetweet() ? status.getRetweetedStatus() : status; - final long in_reply_to_user_id = status.getInReplyToUserId(); + final long inReplyToUserId = status.getInReplyToUserId(); final UserMentionEntity[] entities = status.getUserMentionEntities(); if (entities == null) return orig.getInReplyToScreenName(); for (final UserMentionEntity entity : entities) { - if (in_reply_to_user_id == entity.getId()) return entity.getName(); + if (inReplyToUserId == entity.getId()) return entity.getName(); } return orig.getInReplyToScreenName(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/CardActionTextView.java b/twidere/src/main/java/org/mariotaku/twidere/view/CardActionTextView.java new file mode 100644 index 000000000..d773ea05a --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/view/CardActionTextView.java @@ -0,0 +1,98 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2014 Mariotaku Lee + * + * 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 . + */ + +package org.mariotaku.twidere.view; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.PorterDuff.Mode; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.AttributeSet; +import android.widget.TextView; + +import org.mariotaku.twidere.R; + +/** + * Created by mariotaku on 14/11/20. + */ +public class CardActionTextView extends TextView { + + private int mColor; + private int mActivatedColor; + + public CardActionTextView(Context context) { + this(context, null); + } + + public CardActionTextView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CardActionTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + final TypedArray defaultValues = context.obtainStyledAttributes( + new int[]{android.R.attr.colorActivatedHighlight}); + final int defaultActivatedColor = defaultValues.getColor(0, 0); + defaultValues.recycle(); + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.IconActionButton); + mColor = a.getColor(R.styleable.IconActionButton_color, 0); + mActivatedColor = a.getColor(R.styleable.IconActionButton_activatedColor, defaultActivatedColor); + a.recycle(); + updateColorFilter(); + } + + public int getActivatedColor() { + return mActivatedColor; + } + + public int getColor() { + return mColor != 0 ? mColor : getCurrentTextColor(); + } + + @Override + public void setActivated(boolean activated) { + super.setActivated(activated); + updateColorFilter(); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + @Override + public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, Drawable bottom) { + super.setCompoundDrawablesRelative(start, top, end, bottom); + updateColorFilter(); + } + + @Override + public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) { + super.setCompoundDrawables(left, top, right, bottom); + updateColorFilter(); + } + + private void updateColorFilter() { + for (Drawable d : getCompoundDrawables()) { + if (d != null) { + d.mutate(); + d.setColorFilter(isActivated() ? getActivatedColor() : getColor(), Mode.SRC_ATOP); + } + } + + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/CircularImageView.java b/twidere/src/main/java/org/mariotaku/twidere/view/CircularImageView.java new file mode 100644 index 000000000..94c2877b7 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/view/CircularImageView.java @@ -0,0 +1,331 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2014 Mariotaku Lee + * + * 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 . + */ + +package org.mariotaku.twidere.view; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.PorterDuff.Mode; +import android.graphics.PorterDuffXfermode; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.StateListDrawable; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewOutlineProvider; +import android.widget.ImageView; + +import org.mariotaku.twidere.R; + +/** + * An ImageView class with a circle mask so that all images are drawn in a + * circle instead of a square. + */ +public class CircularImageView extends ImageView { + + private static final int SHADOW_START_COLOR = 0x37000000; + + private static final boolean USE_OUTLINE = false && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; + + private final Matrix mMatrix; + private final RectF mSource; + private final RectF mDestination; + private final Paint mBitmapPaint; + private final Paint mBorderPaint; + + private boolean mBorderEnabled; + private Bitmap mShadowBitmap; + private float mShadowRadius; + private Drawable mBackground; + + public CircularImageView(Context context) { + this(context, null, 0); + } + + public CircularImageView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CircularImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircularImageView, defStyle, 0); + + mMatrix = new Matrix(); + mSource = new RectF(); + mDestination = new RectF(); + + mBitmapPaint = new Paint(Paint.LINEAR_TEXT_FLAG); + mBitmapPaint.setFilterBitmap(true); + mBitmapPaint.setDither(true); + + mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mBorderPaint.setStyle(Paint.Style.STROKE); + + if (a.hasValue(R.styleable.CircularImageView_border)) { + setBorderEnabled(a.getBoolean(R.styleable.CircularImageView_border, false)); + } else if (a.hasValue(R.styleable.CircularImageView_borderColor) + || a.hasValue(R.styleable.CircularImageView_borderWidth)) { + setBorderEnabled(true); + } + setBorderColor(a.getColor(R.styleable.CircularImageView_borderColor, Color.TRANSPARENT)); + setBorderWidth(a.getDimensionPixelSize(R.styleable.CircularImageView_borderWidth, 0)); + + if (USE_OUTLINE) { + if (a.hasValue(R.styleable.CircularImageView_elevation)) { + ViewCompat.setElevation(this, + a.getDimensionPixelSize(R.styleable.CircularImageView_elevation, 0)); + } + } else { + mShadowRadius = a.getDimensionPixelSize(R.styleable.CircularImageView_elevation, 0); + } + + a.recycle(); + + if (USE_OUTLINE) { + initOutlineProvider(); + } + } + + public void setBorderWidth(int width) { + mBorderPaint.setStrokeWidth(width); + invalidate(); + } + + public void setBorderEnabled(boolean enabled) { + mBorderEnabled = enabled; + invalidate(); + } + + public void setBorderColor(int color) { + mBorderPaint.setARGB(Color.alpha(color), Color.red(color), Color.green(color), + Color.blue(color)); + invalidate(); + } + + private void initOutlineProvider() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + setClipToOutline(true); + setOutlineProvider(new CircularOutlineProvider()); + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + updateShadowBitmap(); + updateBackgroundPadding(); + } + + @Override + public void setPadding(int left, int top, int right, int bottom) { + super.setPadding(left, top, right, bottom); + updateShadowBitmap(); + updateBackgroundPadding(); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + @Override + public void setPaddingRelative(int start, int top, int end, int bottom) { + super.setPaddingRelative(start, top, end, bottom); + updateShadowBitmap(); + updateBackgroundPadding(); + } + + private void updateShadowBitmap() { + if (USE_OUTLINE) return; + final int width = getWidth(), height = getHeight(); + if (width <= 0 || height <= 0) return; + final int contentLeft = getPaddingLeft(), contentTop = getPaddingTop(), + contentRight = width - getPaddingRight(), + contentBottom = height - getPaddingBottom(); + final int contentWidth = contentRight - contentLeft, + contentHeight = contentBottom - contentTop; + final float radius = mShadowRadius, dy = radius * 1.5f / 2; + final int size = Math.round(Math.min(contentWidth, contentHeight) + radius * 2); + mShadowBitmap = Bitmap.createBitmap(size, Math.round(size + dy), Config.ARGB_8888); + Canvas canvas = new Canvas(mShadowBitmap); + final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(Color.WHITE); + paint.setShadowLayer(radius, 0, radius * 1.5f / 2, SHADOW_START_COLOR); + final RectF rect = new RectF(radius, radius, size - radius, size - radius); + canvas.drawOval(rect, paint); + paint.setShadowLayer(0, 0, 0, 0); + paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR)); + canvas.drawOval(rect, paint); + invalidate(); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (mBackground != null) { + mBackground.draw(canvas); + } + super.dispatchDraw(canvas); + } + + @Override + public void setBackgroundDrawable(Drawable background) { + if (USE_OUTLINE) { + super.setBackgroundDrawable(background); + return; + } + super.setBackgroundDrawable(null); + mBackground = background; + updateBackgroundPadding(); + } + + private void updateBackgroundPadding() { + if (USE_OUTLINE) return; + final Drawable drawable = mBackground; + if (drawable == null) return; + final int width = getWidth(), height = getHeight(); + if (width <= 0 || height <= 0) return; + final int contentLeft = getPaddingLeft(), contentTop = getPaddingTop(), + contentRight = width - getPaddingRight(), + contentBottom = height - getPaddingBottom(); + final int contentWidth = contentRight - contentLeft, + contentHeight = contentBottom - contentTop; + final int size = Math.min(contentWidth, contentHeight); + drawable.setBounds(contentLeft + (contentWidth - size) / 2, + contentTop + (contentHeight - size) / 2, + contentRight - (contentWidth - size) / 2, + contentBottom - (contentHeight - size) / 2); + } + + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + public void setBackground(Drawable background) { + if (USE_OUTLINE) { + super.setBackground(background); + return; + } + super.setBackground(null); + mBackground = background; + updateBackgroundPadding(); + } + + @Override + protected void onDraw(@NonNull Canvas canvas) { + + mDestination.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), + getHeight() - getPaddingBottom()); + + if (USE_OUTLINE) { + super.onDraw(canvas); + } else { + final int contentLeft = getPaddingLeft(), contentTop = getPaddingTop(), + contentRight = getWidth() - getPaddingRight(), + contentBottom = getHeight() - getPaddingBottom(); + final int contentWidth = contentRight - contentLeft, + contentHeight = contentBottom - contentTop; + final int size = Math.min(contentWidth, contentHeight); + if (mShadowBitmap != null) { + canvas.drawBitmap(mShadowBitmap, contentLeft + (contentWidth - size) / 2 - mShadowRadius, + contentTop + (contentHeight - size) / 2 - mShadowRadius, null); + } + Drawable drawable = getDrawable(); + BitmapDrawable bitmapDrawable = null; + // support state list drawable by getting the current state + if (drawable instanceof StateListDrawable) { + if (drawable.getCurrent() != null) { + bitmapDrawable = (BitmapDrawable) drawable.getCurrent(); + } + } else { + bitmapDrawable = (BitmapDrawable) drawable; + } + + if (bitmapDrawable == null) { + return; + } + Bitmap bitmap = bitmapDrawable.getBitmap(); + if (bitmap == null) { + return; + } + + mSource.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); + + drawBitmapWithCircleOnCanvas(bitmap, canvas, mSource, mDestination); + } + + // Then draw the border. + if (mBorderEnabled) { + canvas.drawCircle(mDestination.centerX(), mDestination.centerY(), + mDestination.width() / 2f - mBorderPaint.getStrokeWidth() / 2, mBorderPaint); + } + } + + /** + * Given the source bitmap and a canvas, draws the bitmap through a circular + * mask. Only draws a circle with diameter equal to the destination width. + * + * @param bitmap The source bitmap to draw. + * @param canvas The canvas to draw it on. + * @param source The source bound of the bitmap. + * @param dest The destination bound on the canvas. + */ + public void drawBitmapWithCircleOnCanvas(Bitmap bitmap, Canvas canvas, + RectF source, RectF dest) { + // Draw bitmap through shader first. + BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, + Shader.TileMode.CLAMP); + mMatrix.reset(); + + // Fit bitmap to bounds. + mMatrix.setRectToRect(source, dest, Matrix.ScaleToFit.FILL); + + shader.setLocalMatrix(mMatrix); + mBitmapPaint.setShader(shader); + canvas.drawCircle(dest.centerX(), dest.centerY(), Math.min(dest.width(), dest.height()) / 2f, + mBitmapPaint); + + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private static class CircularOutlineProvider extends ViewOutlineProvider { + @Override + public void getOutline(View view, Outline outline) { + final int contentLeft = view.getPaddingLeft(), contentTop = view.getPaddingTop(), + contentRight = view.getWidth() - view.getPaddingRight(), + contentBottom = view.getHeight() - view.getPaddingBottom(); + final int contentWidth = contentRight - contentLeft, + contentHeight = contentBottom - contentTop; + final int size = Math.min(contentWidth, contentHeight); + outline.setOval(contentLeft + (contentWidth - size) / 2, + contentTop + (contentHeight - size) / 2, + contentRight - (contentWidth - size) / 2, + contentBottom - (contentHeight - size) / 2); + } + } +} \ No newline at end of file diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/PullToRefreshVerticalRecyclerView.java b/twidere/src/main/java/org/mariotaku/twidere/view/PullToRefreshVerticalRecyclerView.java new file mode 100644 index 000000000..c1a8e2894 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/view/PullToRefreshVerticalRecyclerView.java @@ -0,0 +1,68 @@ +package org.mariotaku.twidere.view; + +import android.content.Context; +import android.support.annotation.IdRes; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; + +import com.handmark.pulltorefresh.library.PullToRefreshBase; + +/** + * Created by mariotaku on 14/11/19. + */ +public class PullToRefreshVerticalRecyclerView extends PullToRefreshBase { + + @IdRes + public static final int REFRESHABLE_VIEW_ID = 0x7f200001; + + public PullToRefreshVerticalRecyclerView(Context context) { + super(context); + } + + public PullToRefreshVerticalRecyclerView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PullToRefreshVerticalRecyclerView(Context context, Mode mode) { + super(context, mode); + } + + public PullToRefreshVerticalRecyclerView(Context context, Mode mode, AnimationStyle animStyle) { + super(context, mode, animStyle); + } + + @Override + public Orientation getPullToRefreshScrollDirection() { + return Orientation.VERTICAL; + } + + @Override + protected RecyclerView createRefreshableView(Context context, AttributeSet attrs) { + final RecyclerView recyclerView = new RecyclerView(context, attrs); + recyclerView.setId(REFRESHABLE_VIEW_ID); + return recyclerView; + } + + @Override + protected boolean isReadyForPullStart() { + final RecyclerView recyclerView = getRefreshableView(); + if (recyclerView.getChildCount() <= 0) + return true; + int firstVisiblePosition = recyclerView.getChildPosition(recyclerView.getChildAt(0)); + if (firstVisiblePosition == 0) + return recyclerView.getChildAt(0).getTop() == 0; + else + return false; + + } + + @Override + protected boolean isReadyForPullEnd() { + final RecyclerView recyclerView = getRefreshableView(); + int lastVisiblePosition = recyclerView.getChildPosition(recyclerView.getChildAt(recyclerView.getChildCount() - 1)); + if (lastVisiblePosition >= recyclerView.getAdapter().getItemCount() - 1) { + return recyclerView.getChildAt(recyclerView.getChildCount() - 1).getBottom() <= recyclerView.getBottom(); + } + return false; + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityListViewHolder.java similarity index 95% rename from twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityViewHolder.java rename to twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityListViewHolder.java index 76c7da880..92a7a09cb 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityListViewHolder.java @@ -26,7 +26,7 @@ import android.widget.TextView; import org.mariotaku.twidere.R; -public class ActivityViewHolder extends StatusViewHolder { +public class ActivityListViewHolder extends StatusListViewHolder { public final ImageView activity_profile_image_1, activity_profile_image_2, activity_profile_image_3, activity_profile_image_4, activity_profile_image_5; @@ -35,7 +35,7 @@ public class ActivityViewHolder extends StatusViewHolder { public final TextView activity_profile_image_more_number; public final View divider; - public ActivityViewHolder(final View view) { + public ActivityListViewHolder(final View view) { super(view); divider = findViewById(R.id.divider); activity_profile_images_container = (ViewGroup) findViewById(R.id.activity_profile_image_container); diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/LoadIndicatorViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/LoadIndicatorViewHolder.java new file mode 100644 index 000000000..e579be8d9 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/LoadIndicatorViewHolder.java @@ -0,0 +1,14 @@ +package org.mariotaku.twidere.view.holder; + +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.*; +import android.view.View; + +/** +* Created by mariotaku on 14/11/19. +*/ +public class LoadIndicatorViewHolder extends RecyclerView.ViewHolder { + public LoadIndicatorViewHolder(View view) { + super(view); + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusListViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusListViewHolder.java new file mode 100644 index 000000000..69ad50ae2 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusListViewHolder.java @@ -0,0 +1,187 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2014 Mariotaku Lee + * + * 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 . + */ + +package org.mariotaku.twidere.view.holder; + +import static org.mariotaku.twidere.util.Utils.getDisplayName; +import static org.mariotaku.twidere.util.Utils.getStatusTypeIconRes; +import static org.mariotaku.twidere.util.Utils.getUserTypeIconRes; + +import android.content.Context; +import android.graphics.Color; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.MarginLayoutParams; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import org.mariotaku.twidere.R; +import org.mariotaku.twidere.util.Utils; +import org.mariotaku.twidere.view.ProfileImageView; +import org.mariotaku.twidere.view.ShortTimeView; + +public class StatusListViewHolder extends CardViewHolder { + + public final ProfileImageView my_profile_image, profile_image; + public final ImageView image_preview; + public final ViewGroup image_preview_container; + public final ProgressBar image_preview_progress; + public final TextView name, screen_name, reply_retweet_status; + public final ShortTimeView time; + public final TextView text; + public final TextView image_preview_count; + + private final float density; + private final boolean is_rtl; + public boolean show_as_gap; + public int position; + private boolean account_color_enabled; + private float text_size; + private boolean nickname_only, name_first; + private boolean display_profile_image; + private int card_highlight_option; + + public StatusListViewHolder(final View view) { + super(view); + final Context context = getContext(); + profile_image = (ProfileImageView) findViewById(R.id.profile_image); + my_profile_image = (ProfileImageView) findViewById(R.id.my_profile_image); + image_preview = (ImageView) findViewById(R.id.image_preview); + image_preview_progress = (ProgressBar) findViewById(R.id.image_preview_progress); + image_preview_container = (ViewGroup) findViewById(R.id.image_preview_container); + name = (TextView) findViewById(R.id.name); + screen_name = (TextView) findViewById(R.id.screen_name); + text = (TextView) findViewById(R.id.text); + time = (ShortTimeView) findViewById(R.id.time); + reply_retweet_status = (TextView) findViewById(R.id.reply_retweet_status); + image_preview_count = (TextView) findViewById(R.id.image_preview_count); + show_as_gap = content.isGap(); + is_rtl = Utils.isRTL(context); + density = context.getResources().getDisplayMetrics().density; + } + + public void setAccountColor(final int color) { + content.drawEnd(account_color_enabled && !show_as_gap ? color : Color.TRANSPARENT); + } + + public void setAccountColorEnabled(final boolean enabled) { + account_color_enabled = enabled && !show_as_gap; + if (!account_color_enabled) { + content.drawEnd(Color.TRANSPARENT); + } + } + + public void setCardHighlightOption(final int option) { + card_highlight_option = option; + } + + public void setDisplayNameFirst(final boolean name_first) { + this.name_first = name_first; + } + + public void setDisplayProfileImage(final boolean display) { + display_profile_image = display; + } + + public void setHighlightColor(final int color) { + final boolean line = (card_highlight_option & VALUE_CARD_HIGHLIGHT_OPTION_CODE_LINE) != 0; + final boolean bg = (card_highlight_option & VALUE_CARD_HIGHLIGHT_OPTION_CODE_BACKGROUND) != 0; + content.drawTop(!show_as_gap && line ? color : Color.TRANSPARENT); + content.drawBackground(!show_as_gap && bg && color != 0 ? 0x1A000000 | 0x00FFFFFF & color : Color.TRANSPARENT); + } + + public void setIsMyStatus(final boolean my_status) { + profile_image.setVisibility(my_status ? View.GONE : View.VISIBLE); + my_profile_image.setVisibility(my_status ? View.VISIBLE : View.GONE); + final MarginLayoutParams lp = (MarginLayoutParams) time.getLayoutParams(); + if (is_rtl) { + lp.leftMargin = (int) (my_status ? 6 * density : 0); + } else { + lp.rightMargin = (int) (my_status ? 6 * density : 0); + } + } + + public void setIsReplyRetweet(final boolean is_reply, final boolean is_retweet) { + reply_retweet_status.setVisibility(is_retweet || is_reply ? View.VISIBLE : View.GONE); + } + + public void setNicknameOnly(final boolean nickname_only) { + this.nickname_only = nickname_only; + } + + public void setReplyTo(final long user_id, final String name, final String screen_name) { + final String display_name = getDisplayName(getContext(), user_id, name, screen_name, name_first, nickname_only, + false); + reply_retweet_status.setText(getString(R.string.in_reply_to, display_name)); + reply_retweet_status.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_indicator_conversation, 0, 0, 0); + } + + public void setRetweetedBy(final long count, final long user_id, final String name, final String screen_name) { + final String display_name = getDisplayName(getContext(), user_id, name, screen_name, name_first, nickname_only, + false); + reply_retweet_status.setText(count > 1 ? getString(R.string.retweeted_by_with_count, display_name, count - 1) + : getString(R.string.retweeted_by, display_name)); + reply_retweet_status.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_indicator_retweet, 0, 0, 0); + } + + public void setShowAsGap(final boolean show_gap) { + show_as_gap = show_gap; + if (content != null) { + content.setIsGap(show_gap); + } + // if (item_menu != null) { + // item_menu.setVisibility(show_gap ? View.GONE : View.VISIBLE); + // } + } + + public void setStatusType(final boolean is_favorite, final boolean has_location, final boolean has_media, + final boolean is_possibly_sensitive) { + final int res = getStatusTypeIconRes(is_favorite, has_location, has_media, is_possibly_sensitive); + time.setCompoundDrawablesWithIntrinsicBounds(0, 0, res, 0); + } + + public boolean setTextSize(final float text_size) { + if (this.text_size == text_size) return false; + this.text_size = text_size; + text.setTextSize(text_size); + name.setTextSize(text_size); + screen_name.setTextSize(text_size * 0.75f); + time.setTextSize(text_size * 0.65f); + reply_retweet_status.setTextSize(text_size * 0.65f); + image_preview_count.setTextSize(text_size * 1.25f); + return true; + } + + public void setUserColor(final int... colors) { + content.drawStart(show_as_gap ? null : colors); + } + + public void setUserType(final boolean isVerified, final boolean isProtected) { + // if (display_profile_image) { + // profile_image.setUserType(isVerified, isProtected); + // my_profile_image.setUserType(isVerified, isProtected); + // name.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + // } else { + // profile_image.setUserType(false, false); + // my_profile_image.setUserType(false, false); + name.setCompoundDrawablesWithIntrinsicBounds(0, 0, getUserTypeIconRes(isVerified, isProtected), 0); + // } + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java index ec483d923..99a377ea5 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java @@ -1,187 +1,187 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 Mariotaku Lee - * - * 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 . - */ - package org.mariotaku.twidere.view.holder; -import static org.mariotaku.twidere.util.Utils.getDisplayName; -import static org.mariotaku.twidere.util.Utils.getStatusTypeIconRes; -import static org.mariotaku.twidere.util.Utils.getUserTypeIconRes; - import android.content.Context; -import android.graphics.Color; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v7.widget.RecyclerView; import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.MarginLayoutParams; +import android.view.View.OnClickListener; import android.widget.ImageView; -import android.widget.ProgressBar; import android.widget.TextView; import org.mariotaku.twidere.R; +import org.mariotaku.twidere.adapter.iface.IStatusesAdapter; +import org.mariotaku.twidere.constant.IntentConstants; +import org.mariotaku.twidere.fragment.support.StatusMenuDialogFragment; +import org.mariotaku.twidere.model.ParcelableMedia; +import org.mariotaku.twidere.model.ParcelableStatus; +import org.mariotaku.twidere.util.ImageLoaderWrapper; import org.mariotaku.twidere.util.Utils; -import org.mariotaku.twidere.view.ProfileImageView; +import org.mariotaku.twidere.view.CircularImageView; import org.mariotaku.twidere.view.ShortTimeView; -public class StatusViewHolder extends CardViewHolder { +import java.util.Locale; - public final ProfileImageView my_profile_image, profile_image; - public final ImageView image_preview; - public final ViewGroup image_preview_container; - public final ProgressBar image_preview_progress; - public final TextView name, screen_name, reply_retweet_status; - public final ShortTimeView time; - public final TextView text; - public final TextView image_preview_count; +/** + * Created by mariotaku on 14/11/19. + */ +public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClickListener { - private final float density; - private final boolean is_rtl; - public boolean show_as_gap; - public int position; - private boolean account_color_enabled; - private float text_size; - private boolean nickname_only, name_first; - private boolean display_profile_image; - private int card_highlight_option; + private final IStatusesAdapter adapter; - public StatusViewHolder(final View view) { - super(view); - final Context context = getContext(); - profile_image = (ProfileImageView) findViewById(R.id.profile_image); - my_profile_image = (ProfileImageView) findViewById(R.id.my_profile_image); - image_preview = (ImageView) findViewById(R.id.image_preview); - image_preview_progress = (ProgressBar) findViewById(R.id.image_preview_progress); - image_preview_container = (ViewGroup) findViewById(R.id.image_preview_container); - name = (TextView) findViewById(R.id.name); - screen_name = (TextView) findViewById(R.id.screen_name); - text = (TextView) findViewById(R.id.text); - time = (ShortTimeView) findViewById(R.id.time); - reply_retweet_status = (TextView) findViewById(R.id.reply_retweet_status); - image_preview_count = (TextView) findViewById(R.id.image_preview_count); - show_as_gap = content.isGap(); - is_rtl = Utils.isRTL(context); - density = context.getResources().getDisplayMetrics().density; - } + private final ImageView retweetProfileImageView; + private final CircularImageView profileImageView; + private final ImageView profileTypeView; + private final ImageView mediaPreviewView; + private final TextView textView; + private final TextView nameView, screenNameView; + private final TextView replyRetweetView; + private final ShortTimeView timeView; + private final View mediaPreviewContainer; + private final TextView replyCountView, retweetCountView, favoriteCountView; - public void setAccountColor(final int color) { - content.drawEnd(account_color_enabled && !show_as_gap ? color : Color.TRANSPARENT); - } + public StatusViewHolder(IStatusesAdapter adapter, View itemView) { + super(itemView); + this.adapter = adapter; + itemView.findViewById(R.id.item_content).setOnClickListener(this); + itemView.findViewById(R.id.menu).setOnClickListener(this); + profileImageView = (CircularImageView) itemView.findViewById(R.id.profile_image); + profileTypeView = (ImageView) itemView.findViewById(R.id.profile_type); + textView = (TextView) itemView.findViewById(R.id.text); + nameView = (TextView) itemView.findViewById(R.id.name); + screenNameView = (TextView) itemView.findViewById(R.id.screen_name); + retweetProfileImageView = (ImageView) itemView.findViewById(R.id.retweet_profile_image); + replyRetweetView = (TextView) itemView.findViewById(R.id.reply_retweet_status); + timeView = (ShortTimeView) itemView.findViewById(R.id.time); - public void setAccountColorEnabled(final boolean enabled) { - account_color_enabled = enabled && !show_as_gap; - if (!account_color_enabled) { - content.drawEnd(Color.TRANSPARENT); - } - } + mediaPreviewContainer = itemView.findViewById(R.id.media_preview_container); + mediaPreviewView = (ImageView) itemView.findViewById(R.id.media_preview); - public void setCardHighlightOption(final int option) { - card_highlight_option = option; - } + replyCountView = (TextView) itemView.findViewById(R.id.reply_count); + retweetCountView = (TextView) itemView.findViewById(R.id.retweet_count); + favoriteCountView = (TextView) itemView.findViewById(R.id.favorite_count); +//TODO +// profileImageView.setSelectorColor(ThemeUtils.getUserHighlightColor(itemView.getContext())); - public void setDisplayNameFirst(final boolean name_first) { - this.name_first = name_first; - } + itemView.setOnClickListener(this); + profileImageView.setOnClickListener(this); + mediaPreviewContainer.setOnClickListener(this); + retweetCountView.setOnClickListener(this); + retweetCountView.setOnClickListener(this); + favoriteCountView.setOnClickListener(this); + } - public void setDisplayProfileImage(final boolean display) { - display_profile_image = display; - } + public void displayStatus(ParcelableStatus status) { + final ImageLoaderWrapper loader = adapter.getImageLoader(); + final Context context = adapter.getContext(); + final ParcelableMedia[] media = status.media; - public void setHighlightColor(final int color) { - final boolean line = (card_highlight_option & VALUE_CARD_HIGHLIGHT_OPTION_CODE_LINE) != 0; - final boolean bg = (card_highlight_option & VALUE_CARD_HIGHLIGHT_OPTION_CODE_BACKGROUND) != 0; - content.drawTop(!show_as_gap && line ? color : Color.TRANSPARENT); - content.drawBackground(!show_as_gap && bg && color != 0 ? 0x1A000000 | 0x00FFFFFF & color : Color.TRANSPARENT); - } + if (status.retweet_id > 0) { + replyRetweetView.setText(context.getString(R.string.retweeted_by_name, status.retweeted_by_name)); + replyRetweetView.setVisibility(View.VISIBLE); + replyRetweetView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + retweetProfileImageView.setVisibility(View.VISIBLE); + loader.displayProfileImage(retweetProfileImageView, status.retweeted_by_profile_image); + } else if (status.in_reply_to_user_id > 0) { + replyRetweetView.setText(context.getString(R.string.in_reply_to_name, status.in_reply_to_name)); + replyRetweetView.setVisibility(View.VISIBLE); + replyRetweetView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_action_reply, 0, 0, 0); + retweetProfileImageView.setVisibility(View.GONE); + loader.cancelDisplayTask(retweetProfileImageView); + } else { + replyRetweetView.setText(null); + replyRetweetView.setVisibility(View.GONE); + replyRetweetView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + retweetProfileImageView.setVisibility(View.GONE); + loader.cancelDisplayTask(retweetProfileImageView); + } - public void setIsMyStatus(final boolean my_status) { - profile_image.setVisibility(my_status ? View.GONE : View.VISIBLE); - my_profile_image.setVisibility(my_status ? View.VISIBLE : View.GONE); - final MarginLayoutParams lp = (MarginLayoutParams) time.getLayoutParams(); - if (is_rtl) { - lp.leftMargin = (int) (my_status ? 6 * density : 0); - } else { - lp.rightMargin = (int) (my_status ? 6 * density : 0); - } - } + if (status.user_is_protected) { + profileTypeView.setImageResource(R.drawable.ic_user_type_protected); + } else if (status.user_is_verified) { + profileTypeView.setImageResource(R.drawable.ic_user_type_verified); + } else { + profileTypeView.setImageDrawable(null); + } - public void setIsReplyRetweet(final boolean is_reply, final boolean is_retweet) { - reply_retweet_status.setVisibility(is_retweet || is_reply ? View.VISIBLE : View.GONE); - } + nameView.setText(status.user_name); + screenNameView.setText("@" + status.user_screen_name); + timeView.setTime(status.timestamp); - public void setNicknameOnly(final boolean nickname_only) { - this.nickname_only = nickname_only; - } +// final int userColor = UserColorNicknameUtils.getUserColor(context, status.user_id); +// profileImageView.setBorderColor(userColor); - public void setReplyTo(final long user_id, final String name, final String screen_name) { - final String display_name = getDisplayName(getContext(), user_id, name, screen_name, name_first, nickname_only, - false); - reply_retweet_status.setText(getString(R.string.in_reply_to, display_name)); - reply_retweet_status.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_indicator_conversation, 0, 0, 0); - } + loader.displayProfileImage(profileImageView, status.user_profile_image_url); - public void setRetweetedBy(final long count, final long user_id, final String name, final String screen_name) { - final String display_name = getDisplayName(getContext(), user_id, name, screen_name, name_first, nickname_only, - false); - reply_retweet_status.setText(count > 1 ? getString(R.string.retweeted_by_with_count, display_name, count - 1) - : getString(R.string.retweeted_by, display_name)); - reply_retweet_status.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_indicator_retweet, 0, 0, 0); - } + if (media != null && media.length > 0) { + final ParcelableMedia firstMedia = media[0]; + if (status.text_plain.codePointCount(0, status.text_plain.length()) == firstMedia.end) { + textView.setText(status.text_unescaped.substring(0, firstMedia.start)); + } else { + textView.setText(status.text_unescaped); + } + loader.displayPreviewImageWithCredentials(mediaPreviewView, firstMedia.media_url, + status.account_id, adapter.getImageLoadingHandler()); + mediaPreviewContainer.setVisibility(View.VISIBLE); + } else { + loader.cancelDisplayTask(mediaPreviewView); + textView.setText(status.text_unescaped); + mediaPreviewContainer.setVisibility(View.GONE); + } - public void setShowAsGap(final boolean show_gap) { - show_as_gap = show_gap; - if (content != null) { - content.setIsGap(show_gap); - } - // if (item_menu != null) { - // item_menu.setVisibility(show_gap ? View.GONE : View.VISIBLE); - // } - } + if (status.reply_count > 0) { + replyCountView.setText(Utils.getLocalizedNumber(Locale.getDefault(), status.reply_count)); + } else { + replyCountView.setText(null); + } + if (status.retweet_count > 0) { + retweetCountView.setText(Utils.getLocalizedNumber(Locale.getDefault(), status.retweet_count)); + } else { + retweetCountView.setText(null); + } + if (status.favorite_count > 0) { + favoriteCountView.setText(Utils.getLocalizedNumber(Locale.getDefault(), status.favorite_count)); + } else { + favoriteCountView.setText(null); + } - public void setStatusType(final boolean is_favorite, final boolean has_location, final boolean has_media, - final boolean is_possibly_sensitive) { - final int res = getStatusTypeIconRes(is_favorite, has_location, has_media, is_possibly_sensitive); - time.setCompoundDrawablesWithIntrinsicBounds(0, 0, res, 0); - } + retweetCountView.setEnabled(!status.user_is_protected); - public boolean setTextSize(final float text_size) { - if (this.text_size == text_size) return false; - this.text_size = text_size; - text.setTextSize(text_size); - name.setTextSize(text_size); - screen_name.setTextSize(text_size * 0.75f); - time.setTextSize(text_size * 0.65f); - reply_retweet_status.setTextSize(text_size * 0.65f); - image_preview_count.setTextSize(text_size * 1.25f); - return true; - } + retweetCountView.setActivated(status.is_retweet); + favoriteCountView.setActivated(status.is_favorite); + } - public void setUserColor(final int... colors) { - content.drawStart(show_as_gap ? null : colors); - } - - public void setUserType(final boolean isVerified, final boolean isProtected) { - // if (display_profile_image) { - // profile_image.setUserType(isVerified, isProtected); - // my_profile_image.setUserType(isVerified, isProtected); - // name.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); - // } else { - // profile_image.setUserType(false, false); - // my_profile_image.setUserType(false, false); - name.setCompoundDrawablesWithIntrinsicBounds(0, 0, getUserTypeIconRes(isVerified, isProtected), 0); - // } - } + @Override + public void onClick(View v) { + final Context context = itemView.getContext(); + final ParcelableStatus status = adapter.getStatus(getPosition()); + switch (v.getId()) { + case R.id.item_content: { + Utils.openStatus(context, status); + break; + } + case R.id.menu: { + if (context instanceof FragmentActivity) { + final Bundle args = new Bundle(); + args.putParcelable(IntentConstants.EXTRA_STATUS, status); + final StatusMenuDialogFragment f = new StatusMenuDialogFragment(); + f.setArguments(args); + f.show(((FragmentActivity) context).getSupportFragmentManager(), "status_menu"); + } + break; + } + case R.id.profile_image: { + Utils.openUserProfile(context, status.account_id, status.user_id, status.user_screen_name); + break; + } + case R.id.reply_count: { + final Intent intent = new Intent(IntentConstants.INTENT_ACTION_REPLY); + intent.setPackage(context.getPackageName()); + intent.putExtra(IntentConstants.EXTRA_STATUS, status); + context.startActivity(intent); + break; + } + } + } } diff --git a/twidere/src/main/res/drawable/bg_oval_black.xml b/twidere/src/main/res/drawable/bg_oval_black.xml new file mode 100644 index 000000000..c2168ee10 --- /dev/null +++ b/twidere/src/main/res/drawable/bg_oval_black.xml @@ -0,0 +1,24 @@ + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/drawable/bg_oval_white.xml b/twidere/src/main/res/drawable/bg_oval_white.xml new file mode 100644 index 000000000..6bcaa49ca --- /dev/null +++ b/twidere/src/main/res/drawable/bg_oval_white.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/twidere/src/main/res/drawable/shadow_user_banner_action_bar.xml b/twidere/src/main/res/drawable/shadow_user_banner_action_bar.xml new file mode 100644 index 000000000..9c94836a7 --- /dev/null +++ b/twidere/src/main/res/drawable/shadow_user_banner_action_bar.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/layout/action_item_home_actions_compat.xml b/twidere/src/main/res/layout/action_item_home_actions_compat.xml index a2920ce6e..50af956e3 100644 --- a/twidere/src/main/res/layout/action_item_home_actions_compat.xml +++ b/twidere/src/main/res/layout/action_item_home_actions_compat.xml @@ -1,15 +1,12 @@ + > - + android:layout_gravity="center"/> - diff --git a/twidere/src/main/res/layout/card_item_list_status.xml b/twidere/src/main/res/layout/card_item_list_status.xml index 0f4b566c9..743524cd2 100644 --- a/twidere/src/main/res/layout/card_item_list_status.xml +++ b/twidere/src/main/res/layout/card_item_list_status.xml @@ -8,55 +8,107 @@ android:layout_marginLeft="@dimen/element_spacing_normal" android:layout_marginRight="@dimen/element_spacing_normal" android:layout_marginTop="@dimen/element_spacing_small" + app:cardBackgroundColor="?cardItemBackgroundColor" app:cardCornerRadius="2dp" app:cardElevation="2dp"> - - + + + android:minHeight="@dimen/element_size_small" + android:singleLine="true" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textSize="10sp"/> + - + android:scaleType="centerCrop"/> + + - + android:layout_marginBottom="@dimen/element_spacing_xsmall" + android:layout_marginTop="@dimen/element_spacing_small" + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + + android:singleLine="true" + android:textAppearance="?android:textAppearanceSmall" + android:textSize="10sp"/> - + - + android:overScrollMode="never" + android:scrollbars="none"> - - - + android:gravity="center_vertical" + android:orientation="horizontal"> - + - + - + - - - - - + + + \ No newline at end of file diff --git a/twidere/src/main/res/layout/card_item_list_status_compat.xml b/twidere/src/main/res/layout/card_item_list_status_compat.xml index fcb3114fb..613d03bc8 100644 --- a/twidere/src/main/res/layout/card_item_list_status_compat.xml +++ b/twidere/src/main/res/layout/card_item_list_status_compat.xml @@ -2,7 +2,6 @@ - + + + + + android:scaleType="centerCrop"/> + + - + android:layout_weight="1" + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + android:textAppearance="?android:textAppearanceSmall" + android:textSize="10sp"/> - + android:overScrollMode="never" + android:scrollbars="none"> - - - + android:gravity="center_vertical" + android:orientation="horizontal"> - + - + - + - + - + + + + + - - \ No newline at end of file diff --git a/twidere/src/main/res/layout/fragment_recycler_view.xml b/twidere/src/main/res/layout/fragment_recycler_view.xml index e8ee6b9d3..726018075 100644 --- a/twidere/src/main/res/layout/fragment_recycler_view.xml +++ b/twidere/src/main/res/layout/fragment_recycler_view.xml @@ -1,20 +1,23 @@ + android:clipToPadding="false" + android:layout_height="match_parent"> - + android:layout_height="match_parent"> - + + \ No newline at end of file diff --git a/twidere/src/main/res/layout/header_user_profile.xml b/twidere/src/main/res/layout/header_user_profile.xml index 8ee1b0ef3..db6cbfeee 100644 --- a/twidere/src/main/res/layout/header_user_profile.xml +++ b/twidere/src/main/res/layout/header_user_profile.xml @@ -1,8 +1,8 @@ @@ -19,35 +19,46 @@ android:background="@drawable/shadow_top"/> - - - - - + + + + + - + + + + + + + + + + + + + + + + + - - - + android:contentDescription="@string/profile_image" + android:src="@drawable/ic_profile_image_default"/> - + android:layout_margin="@dimen/element_spacing_normal" + app:cardBackgroundColor="?cardItemBackgroundColor" + app:cardCornerRadius="@dimen/corner_radius_card" + app:cardElevation="@dimen/elevation_card"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:text="@string/description" + android:textAppearance="?android:textAppearanceMedium"/> - + android:text="@string/location" + android:textAppearance="?android:textAppearanceMedium"/> + android:text="@string/url" + android:textAppearance="?android:textAppearanceMedium"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/twidere/src/main/res/layout/header_user_profile_banner.xml b/twidere/src/main/res/layout/header_user_profile_banner.xml index 29486f69f..d39318410 100644 --- a/twidere/src/main/res/layout/header_user_profile_banner.xml +++ b/twidere/src/main/res/layout/header_user_profile_banner.xml @@ -5,5 +5,4 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" - android:foreground="@drawable/shadow_user_banner_image" android:scaleType="centerCrop"/> diff --git a/twidere/src/main/res/layout/preference_widget_color_picker.xml b/twidere/src/main/res/layout/preference_widget_color_picker.xml index f3f091219..4fdfde894 100644 --- a/twidere/src/main/res/layout/preference_widget_color_picker.xml +++ b/twidere/src/main/res/layout/preference_widget_color_picker.xml @@ -1,13 +1,9 @@ - + android:scaleType="centerCrop"/> diff --git a/twidere/src/main/res/values-v21/styles.xml b/twidere/src/main/res/values-v21/styles.xml index eba0615f6..97d4a2880 100644 --- a/twidere/src/main/res/values-v21/styles.xml +++ b/twidere/src/main/res/values-v21/styles.xml @@ -8,4 +8,26 @@ + + + + \ No newline at end of file diff --git a/twidere/src/main/res/values/attrs.xml b/twidere/src/main/res/values/attrs.xml index d3b12cdec..d8f0b1c13 100644 --- a/twidere/src/main/res/values/attrs.xml +++ b/twidere/src/main/res/values/attrs.xml @@ -3,6 +3,7 @@ + @@ -72,6 +73,12 @@ + + + + + + diff --git a/twidere/src/main/res/values/dimens.xml b/twidere/src/main/res/values/dimens.xml index e43215385..f601efabd 100644 --- a/twidere/src/main/res/values/dimens.xml +++ b/twidere/src/main/res/values/dimens.xml @@ -12,6 +12,7 @@ 16dp 24dp -4dp + -8dp 56dp 42dp 48dp @@ -74,5 +75,10 @@ 16dp 180dp 48dp + 24dp + + + 2dp + 2dp \ No newline at end of file diff --git a/twidere/src/main/res/values/strings.xml b/twidere/src/main/res/values/strings.xml index afbd0cd32..a91482246 100644 --- a/twidere/src/main/res/values/strings.xml +++ b/twidere/src/main/res/values/strings.xml @@ -87,10 +87,10 @@ Look and feel Display profile image Disabling this will increase list scroll speed and reduce data usage. - In reply to %s - %s retweeted this - %1$s and %2$d others retweeted this - %d users retweeted this + In reply to %s + Retweeted by %s + Retweeted by %1$s and %2$d others + Retweeted by %d users Users retweeted this Users favorited this Reply to %s diff --git a/twidere/src/main/res/values/styles.xml b/twidere/src/main/res/values/styles.xml index 37d6873c9..18512cec5 100644 --- a/twidere/src/main/res/values/styles.xml +++ b/twidere/src/main/res/values/styles.xml @@ -151,7 +151,28 @@ ?android:selectableItemBackground - + + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/values/themes.xml b/twidere/src/main/res/values/themes.xml index e43d818d5..05c9e5014 100644 --- a/twidere/src/main/res/values/themes.xml +++ b/twidere/src/main/res/values/themes.xml @@ -39,6 +39,7 @@ @style/Widget.CardActionButton + @style/Widget.ProfileImage.Large @drawable/bg_card_item_dark #1a1a1a @@ -83,6 +84,7 @@ @style/Widget.Light.CardActionButton + @style/Widget.Light.ProfileImage.Large @drawable/bg_card_item_light #f8f8f8 @@ -153,6 +155,7 @@ @style/Widget.CardActionButton + @style/Widget.ProfileImage.Large @drawable/bg_card_item_dark #1a1a1a @@ -182,6 +185,7 @@ @style/Widget.Light.CardActionButton + @style/Widget.Light.ProfileImage.Large @drawable/bg_card_item_light #f8f8f8 @@ -240,6 +244,7 @@ @style/Widget.CardActionButton + @style/Widget.ProfileImage.Large @drawable/bg_card_item_dark #1a1a1a @@ -338,6 +343,7 @@ @style/Widget.CardActionButton + @style/Widget.ProfileImage.Large @drawable/bg_card_item_dark #1a1a1a @@ -373,6 +379,7 @@ @style/Widget.Light.CardActionButton + @style/Widget.Light.ProfileImage.Large @drawable/bg_card_item_light #f8f8f8 @@ -408,6 +415,7 @@ @style/Widget.Light.CardActionButton + @style/Widget.Light.ProfileImage.Large @drawable/bg_card_item_light #f8f8f8