1
0
mirror of https://github.com/TwidereProject/Twidere-Android synced 2024-12-18 03:28:55 +01:00

used my CircularImageView implementation

This commit is contained in:
Mariotaku Lee 2014-11-21 11:00:46 +08:00
parent 49742e7446
commit 22351c2916
60 changed files with 2482 additions and 1502 deletions

3
.gitmodules vendored
View File

@ -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

View File

@ -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
}
}

View File

@ -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

@ -0,0 +1 @@
Subproject commit 5ca6a6cfb871ad4a273abeee8b97d8cc046b7f19

View File

@ -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')
project(':RefreshNow').projectDir = file('libraries/RefreshNow/library')
project(':PullToRefresh').projectDir = file('libraries/PullToRefresh/library')

View File

@ -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'])
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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<ViewHolder> 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;
}
}

View File

@ -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<P
configBaseCardAdapter(context, this);
}
public abstract void bindView(final int position, final ActivityViewHolder holder, final ParcelableActivity item);
public abstract void bindView(final int position, final ActivityListViewHolder holder, final ParcelableActivity item);
@Override
public ImageLoaderWrapper getImageLoader() {
@ -84,9 +84,9 @@ public abstract class BaseParcelableActivitiesAdapter extends BaseArrayAdapter<P
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 ActivityViewHolder holder = tag instanceof ActivityViewHolder ? (ActivityViewHolder) tag
: new ActivityViewHolder(view);
if (!(tag instanceof ActivityViewHolder)) {
final ActivityListViewHolder holder = tag instanceof ActivityListViewHolder ? (ActivityListViewHolder) tag
: new ActivityListViewHolder(view);
if (!(tag instanceof ActivityListViewHolder)) {
if (mPlainList) {
((View) holder.content).setPadding(0, 0, 0, 0);
holder.content.setItemBackground(null);
@ -149,7 +149,7 @@ public abstract class BaseParcelableActivitiesAdapter extends BaseArrayAdapter<P
}
}
protected void displayActivityUserProfileImages(final ActivityViewHolder holder, final ParcelableStatus[] statuses) {
protected void displayActivityUserProfileImages(final ActivityListViewHolder holder, final ParcelableStatus[] statuses) {
if (statuses == null) {
displayActivityUserProfileImages(holder, new String[0]);
} else {
@ -161,7 +161,7 @@ public abstract class BaseParcelableActivitiesAdapter extends BaseArrayAdapter<P
}
}
protected void displayActivityUserProfileImages(final ActivityViewHolder holder, final ParcelableUser[] users) {
protected void displayActivityUserProfileImages(final ActivityListViewHolder holder, final ParcelableUser[] users) {
if (users == null) {
displayActivityUserProfileImages(holder, new String[0]);
} else {
@ -205,7 +205,7 @@ public abstract class BaseParcelableActivitiesAdapter extends BaseArrayAdapter<P
return isDisplayProfileImage();
}
private void displayActivityUserProfileImages(final ActivityViewHolder holder, final String[] urls) {
private void displayActivityUserProfileImages(final ActivityListViewHolder holder, final String[] urls) {
final int length = urls != null ? Math.min(holder.activity_profile_images.length, urls.length) : 0;
final boolean shouldDisplayImages = isDisplayProfileImage() && length > 0;
holder.activity_profile_images_container.setVisibility(shouldDisplayImages ? View.VISIBLE : View.GONE);

View File

@ -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));

View File

@ -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();

View File

@ -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;

View File

@ -1,445 +1,46 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.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<ParcelableStatus> implements
IStatusesListAdapter<List<ParcelableStatus>>, OnClickListener, OnOverflowIconClickListener {
private List<ParcelableStatus> 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<ParcelableStatus> 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<ParcelableStatus> 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<ParcelableStatus> getData() {
return mData;
}
}

View File

@ -0,0 +1,450 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.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<ParcelableStatus> implements
IStatusesListAdapter<List<ParcelableStatus>>, 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<ParcelableStatus> 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;
}
}

View File

@ -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();
}

View File

@ -27,8 +27,6 @@ public interface IStatusesListAdapter<Data> extends IBaseCardAdapter, IStatusesA
public long getAccountId(final int position);
public int getActualCount();
public ParcelableStatus getLastStatus();
public long getLastStatusId();

View File

@ -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<Data> 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<Data> 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<Data> 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 {

View File

@ -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<Data> 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<Data> 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<Data> 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 {

View File

@ -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<ParcelableStatus> data = getData();
if (data != null) {
outState.putParcelableArrayList(EXTRA_DATA, new ArrayList<ParcelableStatus>(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<List<ParcelableStatus>> newLoaderInstance(Context context, Bundle args);

View File

@ -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<ParcelableStatus> 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);

View File

@ -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<SingleResponse<TranslationResult>> {
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);

View File

@ -1,24 +1,25 @@
/*
* Twidere - Twitter client for Android
*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.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();
}
}
}

View File

@ -1,94 +1,139 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.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<List<ParcelableStatus>>, 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<List<ParcelableStatus>> 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<List<ParcelableStatus>> onCreateLoader(int id, Bundle args) {
mSwipeRefreshLayout.setRefreshing(true);
final List<ParcelableStatus> 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<List<ParcelableStatus>> adapter = getListAdapter();
adapter.setIndicateMyStatusDisabled(isMyTimeline);
adapter.setFiltersEnabled(!isMyTimeline);
adapter.setIgnoredFilterFields(true, false, false, false, false);
public void onLoadFinished(Loader<List<ParcelableStatus>> loader, List<ParcelableStatus> 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<List<ParcelableStatus>> 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;
}
}

View File

@ -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<List<ParcelableStatus>> {
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<List<ParcelableStatus>> 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<List<ParcelableStatus>> loader, List<ParcelableStatus> data) {
mAdapter.setData(data);
setListShown(true);
}
@Override
public void onLoaderReset(Loader<List<ParcelableStatus>> loader) {
mAdapter.setData(null);
}
private static class ParcelableTimelineAdapter extends Adapter<ViewHolder> 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<ParcelableStatus> 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<ParcelableStatus> 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;
}
}
}
}
}

View File

@ -0,0 +1,94 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.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<List<ParcelableStatus>> 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<List<ParcelableStatus>> 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;
}
}

View File

@ -30,62 +30,62 @@ import java.util.List;
public abstract class ParcelableStatusesLoader extends AsyncTaskLoader<List<ParcelableStatus>> implements Constants {
private final List<ParcelableStatus> mData = new NoDuplicatesArrayList<ParcelableStatus>();
private final boolean mFirstLoad;
private final int mTabPosition;
private final List<ParcelableStatus> mData = new NoDuplicatesArrayList<>();
private final boolean mFirstLoad;
private final int mTabPosition;
private Long mLastViewedId;
private Long mLastViewedId;
public ParcelableStatusesLoader(final Context context, final List<ParcelableStatus> 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<ParcelableStatus> 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<ParcelableStatus> 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<ParcelableStatus> 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<ParcelableStatus> getData() {
return mData;
}
protected List<ParcelableStatus> 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;
}
}

View File

@ -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<ParcelableStatus> data, final String[] saved_statuses_args,
final int tab_position) {
super(context, data, tab_position);
final long since_id, final List<ParcelableStatus> 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<ParcelableStatus>(data);
return new CopyOnWriteArrayList<>(data);
}
protected abstract List<Status> getStatuses(Twitter twitter, Paging paging) throws TwitterException;

View File

@ -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,

View File

@ -99,9 +99,9 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
public final boolean is_gap, is_retweet, is_favorite, is_possibly_sensitive, user_is_following, user_is_protected,
user_is_verified;
public final String retweeted_by_name, retweeted_by_screen_name, text_html, text_plain, user_name,
user_screen_name, in_reply_to_name, in_reply_to_screen_name, source, user_profile_image_url,
text_unescaped, first_media;
public final String retweeted_by_name, retweeted_by_screen_name, retweeted_by_profile_image,
text_html, text_plain, user_name, user_screen_name, in_reply_to_name, in_reply_to_screen_name,
source, user_profile_image_url, text_unescaped, first_media;
public final ParcelableLocation location;
@ -135,6 +135,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
my_retweet_id = getAsLong(values, Statuses.MY_RETWEET_ID, -1);
retweeted_by_name = values.getAsString(Statuses.RETWEETED_BY_USER_NAME);
retweeted_by_screen_name = values.getAsString(Statuses.RETWEETED_BY_USER_SCREEN_NAME);
retweeted_by_profile_image = values.getAsString(Statuses.RETWEETED_BY_USER_PROFILE_IMAGE);
source = values.getAsString(Statuses.SOURCE);
retweet_count = getAsInteger(values, Statuses.RETWEET_COUNT, 0);
favorite_count = getAsInteger(values, Statuses.FAVORITE_COUNT, 0);
@ -170,6 +171,8 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
retweeted_by_name = idx.retweeted_by_user_name != -1 ? c.getString(idx.retweeted_by_user_name) : null;
retweeted_by_screen_name = idx.retweeted_by_user_screen_name != -1 ? c
.getString(idx.retweeted_by_user_screen_name) : null;
retweeted_by_profile_image = idx.retweeted_by_user_profile_image != -1 ? c
.getString(idx.retweeted_by_user_profile_image) : null;
text_html = idx.text_html != -1 ? c.getString(idx.text_html) : null;
media = ParcelableMedia.fromJSONString(idx.media != -1 ? c.getString(idx.media) : null);
text_plain = idx.text_plain != -1 ? c.getString(idx.text_plain) : null;
@ -210,6 +213,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
user_is_verified = in.readBoolean("is_verified");
retweeted_by_name = in.readString("retweeted_by_name");
retweeted_by_screen_name = in.readString("retweeted_by_screen_name");
retweeted_by_profile_image = in.readString("retweeted_by_profile_image");
text_html = in.readString("text_html");
text_plain = in.readString("text_plain");
user_name = in.readString("name");
@ -248,6 +252,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
user_is_verified = in.readInt() == 1;
retweeted_by_name = in.readString();
retweeted_by_screen_name = in.readString();
retweeted_by_profile_image = in.readString();
text_html = in.readString();
text_plain = in.readString();
user_name = in.readString();
@ -280,6 +285,8 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
retweeted_by_id = retweet_user != null ? retweet_user.getId() : -1;
retweeted_by_name = retweet_user != null ? retweet_user.getName() : null;
retweeted_by_screen_name = retweet_user != null ? retweet_user.getScreenName() : null;
retweeted_by_profile_image = retweet_user != null ?
ParseUtils.parseString(retweet_user.getProfileImageUrlHttps()) : null;
final Status status = retweeted != null ? retweeted : orig;
final User user = status.getUser();
user_id = user.getId();
@ -343,20 +350,45 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
@Override
public String toString() {
return "ParcelableStatus{retweet_id=" + retweet_id + ", retweeted_by_id=" + retweeted_by_id + ", id=" + id
+ ", account_id=" + account_id + ", user_id=" + user_id + ", timestamp=" + timestamp
+ ", retweet_count=" + retweet_count + ", favorite_count=" + favorite_count
+ ", in_reply_to_status_id=" + in_reply_to_status_id + ", in_reply_to_user_id=" + in_reply_to_user_id
+ ", my_retweet_id=" + my_retweet_id + ", is_gap=" + is_gap + ", is_retweet=" + is_retweet
+ ", is_favorite=" + is_favorite + ", is_possibly_sensitive=" + is_possibly_sensitive
+ ", user_is_following=" + user_is_following + ", user_is_protected=" + user_is_protected
+ ", user_is_verified=" + user_is_verified + ", retweeted_by_name=" + retweeted_by_name
+ ", retweeted_by_screen_name=" + retweeted_by_screen_name + ", text_html=" + text_html
+ ", text_plain=" + text_plain + ", user_name=" + user_name + ", user_screen_name=" + user_screen_name
+ ", in_reply_to_name=" + in_reply_to_name + ", in_reply_to_screen_name=" + in_reply_to_screen_name
+ ", source=" + source + ", user_profile_image_url=" + user_profile_image_url + ", text_unescaped="
+ text_unescaped + ", first_media=" + first_media + ", location=" + location + ", mentions="
+ Arrays.toString(mentions) + ", media=" + Arrays.toString(media) + "}";
return "ParcelableStatus{" +
"id=" + id +
", account_id=" + account_id +
", timestamp=" + timestamp +
", user_id=" + user_id +
", retweet_id=" + retweet_id +
", retweeted_by_id=" + retweeted_by_id +
", retweet_timestamp=" + retweet_timestamp +
", retweet_count=" + retweet_count +
", favorite_count=" + favorite_count +
", reply_count=" + reply_count +
", descendent_reply_count=" + descendent_reply_count +
", in_reply_to_status_id=" + in_reply_to_status_id +
", in_reply_to_user_id=" + in_reply_to_user_id +
", my_retweet_id=" + my_retweet_id +
", is_gap=" + is_gap +
", is_retweet=" + is_retweet +
", is_favorite=" + is_favorite +
", is_possibly_sensitive=" + is_possibly_sensitive +
", user_is_following=" + user_is_following +
", user_is_protected=" + user_is_protected +
", user_is_verified=" + user_is_verified +
", retweeted_by_name='" + retweeted_by_name + '\'' +
", retweeted_by_screen_name='" + retweeted_by_screen_name + '\'' +
", retweeted_by_profile_image='" + retweeted_by_profile_image + '\'' +
", text_html='" + text_html + '\'' +
", text_plain='" + text_plain + '\'' +
", user_name='" + user_name + '\'' +
", user_screen_name='" + user_screen_name + '\'' +
", in_reply_to_name='" + in_reply_to_name + '\'' +
", in_reply_to_screen_name='" + in_reply_to_screen_name + '\'' +
", source='" + source + '\'' +
", user_profile_image_url='" + user_profile_image_url + '\'' +
", text_unescaped='" + text_unescaped + '\'' +
", first_media='" + first_media + '\'' +
", location=" + location +
", mentions=" + Arrays.toString(mentions) +
", media=" + Arrays.toString(media) +
'}';
}
@Override
@ -381,6 +413,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
out.writeBoolean("is_verified", user_is_verified);
out.writeString("retweeted_by_name", retweeted_by_name);
out.writeString("retweeted_by_screen_name", retweeted_by_screen_name);
out.writeString("retweeted_by_profile_image", retweeted_by_profile_image);
out.writeString("text_html", text_html);
out.writeString("text_plain", text_plain);
out.writeString("text_unescaped", text_unescaped);
@ -419,6 +452,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
out.writeInt(user_is_verified ? 1 : 0);
out.writeString(retweeted_by_name);
out.writeString(retweeted_by_screen_name);
out.writeString(retweeted_by_profile_image);
out.writeString(text_html);
out.writeString(text_plain);
out.writeString(user_name);
@ -447,9 +481,9 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
text_html, text_plain, text_unescaped, user_profile_image_url, is_favorite, is_retweet,
is_gap, location, is_protected, is_verified, in_reply_to_status_id, in_reply_to_user_id,
in_reply_to_user_name, in_reply_to_user_screen_name, my_retweet_id, retweeted_by_user_name,
retweeted_by_user_screen_name, retweet_id, retweet_timestamp, retweeted_by_user_id,
user_id, source, retweet_count, favorite_count, reply_count, descendent_reply_count,
is_possibly_sensitive, is_following, media, first_media, mentions;
retweeted_by_user_screen_name, retweeted_by_user_profile_image, retweet_id, retweet_timestamp,
retweeted_by_user_id, user_id, source, retweet_count, favorite_count, reply_count,
descendent_reply_count, is_possibly_sensitive, is_following, media, first_media, mentions;
@Override
public String toString() {
@ -521,6 +555,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
retweeted_by_user_id = cursor.getColumnIndex(Statuses.RETWEETED_BY_USER_ID);
retweeted_by_user_name = cursor.getColumnIndex(Statuses.RETWEETED_BY_USER_NAME);
retweeted_by_user_screen_name = cursor.getColumnIndex(Statuses.RETWEETED_BY_USER_SCREEN_NAME);
retweeted_by_user_profile_image = cursor.getColumnIndex(Statuses.RETWEETED_BY_USER_PROFILE_IMAGE);
user_id = cursor.getColumnIndex(Statuses.USER_ID);
source = cursor.getColumnIndex(Statuses.SOURCE);
retweet_count = cursor.getColumnIndex(Statuses.RETWEET_COUNT);

View File

@ -40,14 +40,14 @@ import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereLinkify;
import org.mariotaku.twidere.view.CardItemLinearLayout;
import org.mariotaku.twidere.view.ForegroundImageView;
import org.mariotaku.twidere.view.holder.StatusViewHolder;
import org.mariotaku.twidere.view.holder.StatusListViewHolder;
public class CardPreviewPreference extends Preference implements Constants, OnSharedPreferenceChangeListener {
private final LayoutInflater mInflater;
private final SharedPreferences mPreferences;
private final TwidereLinkify mLinkify;
private StatusViewHolder mHolder;
private StatusListViewHolder mHolder;
private boolean mCompactModeChanged;
public CardPreviewPreference(final Context context) {
@ -92,7 +92,7 @@ public class CardPreviewPreference extends Preference implements Constants, OnSh
final boolean display_image_preview = mPreferences.getBoolean(KEY_DISPLAY_IMAGE_PREVIEW, false);
final boolean display_profile_image = mPreferences.getBoolean(KEY_DISPLAY_PROFILE_IMAGE, true);
final boolean nickname_only = mPreferences.getBoolean(KEY_NICKNAME_ONLY, false);
mHolder = new StatusViewHolder(view);
mHolder = new StatusListViewHolder(view);
mLinkify.setHighlightOption(highlightOption);
mHolder.setDisplayNameFirst(nameFirst);
mHolder.setNicknameOnly(nickname_only);

View File

@ -700,6 +700,8 @@ public interface TweetStore {
public static final String RETWEETED_BY_USER_SCREEN_NAME = "retweeted_by_user_screen_name";
public static final String RETWEETED_BY_USER_PROFILE_IMAGE = "retweeted_by_user_profile_image";
/**
* Timestamp of the status.<br>
* 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};
}

View File

@ -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;

View File

@ -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,

View File

@ -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();
}

View File

@ -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();
}

View File

@ -0,0 +1,98 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view;
import android.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);
}
}
}
}

View File

@ -0,0 +1,331 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view;
import android.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);
}
}
}

View File

@ -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<RecyclerView> {
@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;
}
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -0,0 +1,187 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder;
import 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);
// }
}
}

View File

@ -1,187 +1,187 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder;
import 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;
}
}
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/black"/>
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/white"/>
</shape>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="270"
android:endColor="#00000000"
android:startColor="#A0000000"/>
</shape>

View File

@ -1,15 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
>
<com.pkmmte.view.CircularImageView
<org.mariotaku.twidere.view.CircularImageView
android:id="@+id/background"
android:layout_width="@dimen/float_action_button_size"
android:layout_height="@dimen/float_action_button_size"
android:layout_gravity="center"
app:border="false"
app:selector="true"
app:shadow="true"/>
android:layout_gravity="center"/>
<ProgressBar
android:id="@android:id/progress"

View File

@ -41,7 +41,7 @@
android:layout_height="wrap_content"
android:padding="@dimen/element_spacing_small">
<com.pkmmte.view.CircularImageView
<org.mariotaku.twidere.view.CircularImageView
android:id="@+id/media_profile_image"
android:layout_width="@dimen/element_size_small"
android:layout_height="@dimen/element_size_small"/>

View File

@ -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">
<LinearLayout
<RelativeLayout
android:id="@+id/item_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:orientation="vertical">
<LinearLayout
<org.mariotaku.twidere.view.CircularImageView
android:id="@+id/retweet_profile_image"
android:layout_width="@dimen/element_size_small"
android:layout_height="@dimen/element_size_small"
android:layout_marginBottom="@dimen/element_spacing_small"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginTop="@dimen/element_spacing_small"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/reply_retweet_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/element_spacing_minus_small"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginRight="@dimen/element_spacing_normal"
android:layout_marginTop="@dimen/element_spacing_small"
android:layout_toRightOf="@id/retweet_profile_image"
android:drawablePadding="@dimen/element_spacing_small"
android:ellipsize="end"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="@dimen/element_spacing_normal"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingTop="@dimen/element_spacing_normal">
android:minHeight="@dimen/element_size_small"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="10sp"/>
<RelativeLayout
android:id="@+id/profile_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/retweet_profile_image"
android:layout_below="@id/reply_retweet_status"
android:layout_marginTop="@dimen/element_spacing_normal"
android:paddingLeft="@dimen/element_spacing_normal">
<com.pkmmte.view.CircularImageView
<org.mariotaku.twidere.view.CircularImageView
android:id="@+id/profile_image"
android:layout_width="@dimen/icon_size_status_profile_image"
android:layout_height="@dimen/icon_size_status_profile_image"
android:layout_margin="@dimen/element_spacing_small"
android:layout_weight="0"
android:layout_centerVertical="true"
android:contentDescription="@string/profile_image"
android:scaleType="centerCrop"
app:border="false"
app:selector="true"
app:selector_stroke_color="@android:color/transparent"
app:selector_stroke_width="0dp"/>
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/profile_type"
android:layout_width="@dimen/icon_size_profile_type"
android:layout_height="@dimen/icon_size_profile_type"
android:layout_alignBottom="@+id/profile_image"
android:layout_alignRight="@+id/profile_image"
android:layout_marginBottom="@dimen/element_spacing_minus_small"
android:layout_marginRight="@dimen/element_spacing_minus_normal"
android:scaleType="centerInside"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_toLeftOf="@+id/menu"
android:layout_toRightOf="@+id/profile_image"
android:orientation="vertical">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/element_spacing_xsmall"
android:paddingTop="@dimen/element_spacing_small"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
android:layout_marginBottom="@dimen/element_spacing_xsmall"
android:layout_marginTop="@dimen/element_spacing_small"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
<TextView
android:id="@+id/screen_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_small"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"
android:textSize="10sp"/>
</LinearLayout>
<org.mariotaku.twidere.view.ShortTimeView
android:id="@+id/time"
@ -64,24 +116,30 @@
android:layout_height="wrap_content"
android:paddingBottom="@dimen/element_spacing_small"
android:paddingTop="@dimen/element_spacing_xsmall"
android:textAppearance="?android:textAppearanceSmall"/>
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textSize="10sp"/>
</LinearLayout>
<org.mariotaku.twidere.view.ActionIconButton
android:id="@+id/menu"
style="?cardActionButtonStyle"
android:layout_width="@dimen/element_size_normal"
android:layout_height="@dimen/element_size_normal"
android:layout_weight="0"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_action_more_vertical"/>
</LinearLayout>
</RelativeLayout>
<org.mariotaku.twidere.view.ImagePreviewContainer
android:id="@+id/media_preview_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/element_spacing_small"
android:layout_alignLeft="@+id/profile_container"
android:layout_below="@id/profile_container"
android:layout_marginTop="@dimen/element_spacing_normal"
android:foreground="?android:selectableItemBackground">
<ImageView
@ -105,75 +163,70 @@
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginRight="@dimen/element_spacing_normal"
android:layout_marginTop="@dimen/element_spacing_small"
android:layout_alignLeft="@+id/profile_container"
android:layout_below="@id/media_preview_container"
android:layout_marginTop="@dimen/element_spacing_normal"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary"/>
<LinearLayout
android:layout_width="match_parent"
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/profile_container"
android:layout_below="@+id/text"
android:layout_marginTop="@dimen/element_spacing_small"
android:gravity="center_vertical"
android:orientation="horizontal">
android:overScrollMode="never"
android:scrollbars="none">
<org.mariotaku.twidere.view.IconActionButton
android:id="@+id/reply_indicator"
style="?cardActionButtonStyle"
android:layout_width="@dimen/button_size_content_card"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:padding="@dimen/element_spacing_small"
android:src="@drawable/ic_action_reply"
app:activatedColor="@android:color/holo_blue_light"
app:color="?android:textColorSecondary"/>
<TextView
android:id="@+id/reply_count"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:minWidth="@dimen/element_spacing_mlarge"/>
android:gravity="center_vertical"
android:orientation="horizontal">
<org.mariotaku.twidere.view.IconActionButton
android:id="@+id/retweet_indicator"
style="?cardActionButtonStyle"
android:layout_width="@dimen/button_size_content_card"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:padding="@dimen/element_spacing_small"
android:src="@drawable/ic_action_retweet"
app:activatedColor="@android:color/holo_green_light"
app:color="?android:textColorSecondary"/>
<org.mariotaku.twidere.view.CardActionTextView
android:id="@+id/reply_count"
style="?cardActionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:drawableLeft="@drawable/ic_action_reply"
android:gravity="center"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceSmall"
app:activatedColor="@android:color/holo_blue_light"/>
<TextView
android:id="@+id/retweet_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:minWidth="@dimen/element_spacing_mlarge"/>
<org.mariotaku.twidere.view.CardActionTextView
android:id="@+id/retweet_count"
style="?cardActionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:drawableLeft="@drawable/ic_action_retweet"
android:gravity="center"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceSmall"
app:activatedColor="@android:color/holo_green_light"/>
<org.mariotaku.twidere.view.IconActionButton
android:id="@+id/favorite_indicator"
style="?cardActionButtonStyle"
android:layout_width="@dimen/button_size_content_card"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:padding="@dimen/element_spacing_small"
android:src="@drawable/ic_action_star"
app:activatedColor="@android:color/holo_orange_light"
app:color="?android:textColorSecondary"/>
<org.mariotaku.twidere.view.CardActionTextView
android:id="@+id/favorite_count"
style="?cardActionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:drawableLeft="@drawable/ic_action_star"
android:gravity="center"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceSmall"
app:activatedColor="@android:color/holo_orange_light"/>
<TextView
android:id="@+id/favorite_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:minWidth="@dimen/element_spacing_mlarge"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
</android.support.v7.widget.CardView>

View File

@ -2,7 +2,6 @@
<RelativeLayout
android:id="@+id/item_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
@ -11,16 +10,47 @@
android:paddingRight="@dimen/element_spacing_normal"
android:paddingTop="@dimen/element_spacing_normal">
<com.pkmmte.view.CircularImageView
<org.mariotaku.twidere.view.CircularImageView
android:scaleType="centerCrop"
android:id="@+id/retweet_profile_image"
android:layout_width="@dimen/element_size_small"
android:layout_height="@dimen/element_size_small"
android:layout_marginLeft="@dimen/element_spacing_normal"/>
<TextView
android:id="@+id/reply_retweet_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/element_spacing_small"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginRight="@dimen/element_spacing_normal"
android:layout_toRightOf="@id/retweet_profile_image"
android:ellipsize="end"
android:gravity="center_vertical"
android:minHeight="@dimen/element_size_small"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="10sp"/>
<org.mariotaku.twidere.view.CircularImageView
android:id="@+id/profile_image"
android:layout_width="@dimen/icon_size_status_profile_image"
android:layout_height="@dimen/icon_size_status_profile_image"
android:layout_alignLeft="@id/retweet_profile_image"
android:layout_alignWithParentIfMissing="true"
android:layout_below="@+id/reply_retweet_status"
android:contentDescription="@string/profile_image"
android:scaleType="centerCrop"
app:border="false"
app:selector="true"
app:selector_stroke_color="@android:color/transparent"
app:selector_stroke_width="0dp"/>
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/profile_type"
android:layout_width="@dimen/icon_size_profile_type"
android:layout_height="@dimen/icon_size_profile_type"
android:layout_alignBottom="@+id/profile_image"
android:layout_marginRight="@dimen/element_spacing_minus_normal"
android:layout_marginBottom="@dimen/element_spacing_minus_small"
android:layout_alignRight="@+id/profile_image"
android:scaleType="centerInside"/>
<LinearLayout
android:id="@+id/status_content"
@ -37,28 +67,38 @@
android:gravity="center_vertical"
android:orientation="horizontal">
<FrameLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
</FrameLayout>
<TextView
android:id="@+id/screen_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_small"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"
android:textSize="10sp"/>
</LinearLayout>
<org.mariotaku.twidere.view.ShortTimeView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:textAppearance="?android:textAppearanceSmall"/>
android:textAppearance="?android:textAppearanceSmall"
android:textSize="10sp"/>
</LinearLayout>
<org.mariotaku.twidere.view.ImagePreviewContainer
@ -95,79 +135,76 @@
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
<HorizontalScrollView
android:id="@+id/actions_scroller"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/status_content"
android:layout_below="@+id/status_content"
android:gravity="center_vertical"
android:layout_marginTop="@dimen/element_spacing_small"
android:orientation="horizontal">
android:overScrollMode="never"
android:scrollbars="none">
<org.mariotaku.twidere.view.IconActionButton
android:id="@+id/reply_indicator"
style="?cardActionButtonStyle"
android:layout_width="@dimen/button_size_content_card"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:padding="@dimen/element_spacing_small"
android:src="@drawable/ic_action_reply"
app:activatedColor="@android:color/holo_blue_light"
app:color="?android:textColorSecondary"/>
<TextView
android:id="@+id/reply_count"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:minWidth="@dimen/element_spacing_mlarge"/>
android:gravity="center_vertical"
android:orientation="horizontal">
<org.mariotaku.twidere.view.IconActionButton
android:id="@+id/retweet_indicator"
style="?cardActionButtonStyle"
android:layout_width="@dimen/button_size_content_card"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:padding="@dimen/element_spacing_small"
android:src="@drawable/ic_action_retweet"
app:activatedColor="@android:color/holo_green_light"
app:color="?android:textColorSecondary"/>
<TextView
android:id="@+id/reply_count"
style="?cardActionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:drawableLeft="@drawable/ic_action_reply"
android:gravity="center"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceSmall"/>
<TextView
android:id="@+id/retweet_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:minWidth="@dimen/element_spacing_mlarge"/>
<TextView
android:id="@+id/retweet_count"
style="?cardActionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:drawableLeft="@drawable/ic_action_retweet"
android:gravity="center"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceSmall"/>
<org.mariotaku.twidere.view.IconActionButton
android:id="@+id/favorite_indicator"
style="?cardActionButtonStyle"
android:layout_width="@dimen/button_size_content_card"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:padding="@dimen/element_spacing_small"
android:src="@drawable/ic_action_star"
app:activatedColor="@android:color/holo_orange_light"
app:color="?android:textColorSecondary"/>
<TextView
android:id="@+id/favorite_count"
style="?cardActionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:drawableLeft="@drawable/ic_action_star"
android:gravity="center"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceSmall"/>
<TextView
android:id="@+id/favorite_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:minWidth="@dimen/element_spacing_mlarge"/>
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
</HorizontalScrollView>
<Space
android:layout_width="wrap_content"
android:layout_toRightOf="@+id/actions_scroller"
android:layout_toLeftOf="@+id/menu"
android:layout_height="wrap_content"/>
<org.mariotaku.twidere.view.ActionIconButton
android:id="@+id/menu"
style="?cardActionButtonStyle"
android:layout_width="@dimen/button_size_content_card"
android:layout_height="@dimen/button_size_content_card"
android:layout_alignBottom="@+id/actions_scroller"
android:layout_alignParentRight="true"
android:layout_weight="0"
android:src="@drawable/ic_action_more_horizontal"/>
<org.mariotaku.twidere.view.ActionIconButton
style="?cardActionButtonStyle"
android:layout_width="@dimen/button_size_content_card"
android:layout_height="@dimen/button_size_content_card"
android:layout_weight="0"
android:src="@drawable/ic_action_more_horizontal"/>
</LinearLayout>
</RelativeLayout>

View File

@ -1,20 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="@+id/fragment_content"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
android:clipToPadding="false"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@android:id/list"
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_layout"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent">
<ProgressBar
android:id="@android:id/progress"
style="?android:progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:scrollbars="vertical"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/header_user_profile"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/header_user_profile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
@ -19,35 +19,46 @@
android:background="@drawable/shadow_top"/>
<!--<View-->
<!--android:id="@+id/profile_layer_bottom"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="match_parent"-->
<!--android:layout_alignBottom="@+id/card"-->
<!--android:layout_alignTop="@+id/profile_name_container"/>-->
<!--android:id="@+id/profile_layer_bottom"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="match_parent"-->
<!--android:layout_alignBottom="@+id/card"-->
<!--android:layout_alignTop="@+id/profile_name_container"/>-->
<FrameLayout
android:id="@+id/profile_image_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<!--<FrameLayout-->
<!--android:id="@+id/profile_image_container"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_alignBottom="@+id/profile_name_container"-->
<!--android:layout_marginLeft="@dimen/element_spacing_normal"-->
<!--android:background="?cardItemBackground">-->
<!--<org.mariotaku.twidere.view.ProfileImageView-->
<!--android:id="@+id/profile_image"-->
<!--android:layout_width="@dimen/icon_size_user_profile"-->
<!--android:layout_height="@dimen/icon_size_user_profile"-->
<!--android:contentDescription="@string/profile_image"-->
<!--android:scaleType="centerCrop"-->
<!--android:src="@drawable/ic_profile_image_default"/>-->
<!--</FrameLayout>-->
<org.mariotaku.twidere.view.CircularImageView
android:id="@+id/profile_image"
style="?profileImageStyleLarge"
android:elevation="0dp"
android:layout_width="@dimen/icon_size_user_profile"
android:layout_height="@dimen/icon_size_user_profile"
android:layout_alignBottom="@+id/profile_name_container"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:background="?cardItemBackground">
<org.mariotaku.twidere.view.ProfileImageView
android:id="@+id/profile_image"
android:layout_width="@dimen/icon_size_user_profile"
android:layout_height="@dimen/icon_size_user_profile"
android:contentDescription="@string/profile_image"
android:scaleType="centerCrop"
android:src="@drawable/ic_profile_image_default"/>
</FrameLayout>
android:contentDescription="@string/profile_image"
android:src="@drawable/ic_profile_image_default"/>
<org.mariotaku.twidere.view.ColorLabelLinearLayout
android:id="@+id/profile_name_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/profile_banner_space"
android:layout_toRightOf="@+id/profile_image_container"
android:layout_toRightOf="@+id/profile_image"
android:background="?android:selectableItemBackground"
android:clipToPadding="false"
android:orientation="vertical"
@ -89,212 +100,220 @@
</LinearLayout>
</org.mariotaku.twidere.view.ColorLabelLinearLayout>
<org.mariotaku.twidere.view.CardItemLinearLayout
<android.support.v7.widget.CardView
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/profile_name_container"
android:orientation="vertical"
android:padding="@dimen/element_spacing_normal"
android:splitMotionEvents="false">
android:layout_margin="@dimen/element_spacing_normal"
app:cardBackgroundColor="?cardItemBackgroundColor"
app:cardCornerRadius="@dimen/corner_radius_card"
app:cardElevation="@dimen/elevation_card">
<LinearLayout
android:id="@+id/description_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:orientation="vertical"
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/description"
android:textAppearance="?android:textAppearanceMedium"/>
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<LinearLayout
android:id="@+id/location_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:orientation="vertical"
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/location"
android:textAppearance="?android:textAppearanceMedium"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<LinearLayout
android:id="@+id/url_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:orientation="vertical"
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/url"
android:textAppearance="?android:textAppearanceMedium"/>
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<LinearLayout
android:id="@+id/created_at_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:orientation="vertical"
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/created_at"
android:textAppearance="?android:textAppearanceMedium"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/created_at"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.2dp"
android:background="@color/divider_color"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/action_button_size"
android:baselineAligned="false"
android:divider="@drawable/divider_vertical_small_holo"
android:orientation="horizontal"
android:showDividers="middle"
android:splitMotionEvents="false">
<LinearLayout
android:id="@+id/tweets_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/description_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/statuses_count"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorPrimary"/>
android:text="@string/description"
android:textAppearance="?android:textAppearanceMedium"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="wrap_content"
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/statuses"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<LinearLayout
android:id="@+id/followers_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/location_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/followers_count"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorPrimary"/>
android:text="@string/location"
android:textAppearance="?android:textAppearanceMedium"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="wrap_content"
android:id="@+id/location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/followers"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<LinearLayout
android:id="@+id/friends_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/url_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/friends_count"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorPrimary"/>
android:text="@string/url"
android:textAppearance="?android:textAppearanceMedium"/>
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<LinearLayout
android:id="@+id/created_at_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:orientation="vertical"
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/following"
android:text="@string/created_at"
android:textAppearance="?android:textAppearanceMedium"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/created_at"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.2dp"
android:background="@color/divider_color"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/action_button_size"
android:baselineAligned="false"
android:divider="@drawable/divider_vertical_small_holo"
android:orientation="horizontal"
android:showDividers="middle"
android:splitMotionEvents="false">
<LinearLayout
android:id="@+id/tweets_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/statuses_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorPrimary"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/statuses"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<LinearLayout
android:id="@+id/followers_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/followers_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorPrimary"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/followers"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<LinearLayout
android:id="@+id/friends_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/friends_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorPrimary"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/following"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</org.mariotaku.twidere.view.CardItemLinearLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>

View File

@ -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"/>

View File

@ -1,13 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<com.pkmmte.view.CircularImageView
<org.mariotaku.twidere.view.CircularImageView
android:id="@+id/color"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/element_size_small"
android:layout_height="@dimen/element_size_small"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
app:border="true"
app:border_color="@android:color/white"
app:border_width="1dp"/>
android:scaleType="centerCrop"/>

View File

@ -8,4 +8,26 @@
<style name="Widget.Light.CardActionButton" parent="android:Widget.DeviceDefault.ImageButton">
<item name="android:background">?android:selectableItemBackgroundBorderless</item>
</style>
<style name="Widget.ProfileImage.Large">
<item name="android:background">@drawable/bg_oval_white</item>
<!--<item name="android:background">@android:color/transparent</item>-->
<item name="android:paddingTop">1dp</item>
<item name="android:paddingLeft">2dp</item>
<item name="android:paddingRight">2dp</item>
<item name="android:paddingBottom">3dp</item>
<item name="elevation">@dimen/elevation_card</item>
<item name="borderWidth">2dp</item>
</style>
<style name="Widget.Light.ProfileImage.Large">
<item name="android:background">@drawable/bg_oval_white</item>
<!--<item name="android:background">@android:color/transparent</item>-->
<item name="android:paddingTop">1dp</item>
<item name="android:paddingLeft">2dp</item>
<item name="android:paddingRight">2dp</item>
<item name="android:paddingBottom">3dp</item>
<item name="elevation">@dimen/elevation_card</item>
<item name="borderWidth">2dp</item>
</style>
</resources>

View File

@ -3,6 +3,7 @@
<declare-styleable name="Twidere">
<attr name="cardActionButtonStyle" format="reference"/>
<attr name="profileImageStyleLarge" format="reference"/>
<attr name="cardItemBackground" format="reference"/>
<attr name="cardItemBackgroundColor" format="color"/>
<attr name="cardItemMessageIncomingBackground" format="reference"/>
@ -72,6 +73,12 @@
<attr name="color" format="color"/>
<attr name="activatedColor" format="color"/>
</declare-styleable>
<declare-styleable name="CircularImageView">
<attr name="border" format="boolean"/>
<attr name="borderWidth" format="dimension"/>
<attr name="borderColor" format="color"/>
<attr name="elevation" format="dimension"/>
</declare-styleable>
<attr name="notificationType">
<flag name="none" value="0"/>
<flag name="ringtone" value="1"/>

View File

@ -12,6 +12,7 @@
<dimen name="element_spacing_large">16dp</dimen>
<dimen name="element_spacing_xlarge">24dp</dimen>
<dimen name="element_spacing_minus_small">-4dp</dimen>
<dimen name="element_spacing_minus_normal">-8dp</dimen>
<dimen name="icon_size_list_item">56dp</dimen>
<dimen name="icon_size_list_item_small">42dp</dimen>
<dimen name="icon_size_card_list_item">48dp</dimen>
@ -74,5 +75,10 @@
<dimen name="unread_indicator_size">16dp</dimen>
<dimen name="account_selector_popup_width">180dp</dimen>
<dimen name="icon_size_status_profile_image">48dp</dimen>
<dimen name="icon_size_profile_type">24dp</dimen>
<!-- Elevation values -->
<dimen name="elevation_card">2dp</dimen>
<dimen name="corner_radius_card">2dp</dimen>
</resources>

View File

@ -87,10 +87,10 @@
<string name="look_and_feel">Look and feel</string>
<string name="display_profile_image">Display profile image</string>
<string name="image_load_summary">Disabling this will increase list scroll speed and reduce data usage.</string>
<string name="in_reply_to">In reply to <xliff:g id="user_name">%s</xliff:g></string>
<string name="retweeted_by"><xliff:g id="user_name">%s</xliff:g> retweeted this</string>
<string name="retweeted_by_with_count"><xliff:g id="user_name">%1$s</xliff:g> and <xliff:g id="retweet_count">%2$d</xliff:g> others retweeted this</string>
<string name="retweeted_by_count"><xliff:g id="retweet_count">%d</xliff:g> users retweeted this</string>
<string name="in_reply_to_name">In reply to <xliff:g id="user_name">%s</xliff:g></string>
<string name="retweeted_by_name">Retweeted by <xliff:g id="user_name">%s</xliff:g></string>
<string name="retweeted_by_name_with_count">Retweeted by <xliff:g id="user_name">%1$s</xliff:g> and <xliff:g id="retweet_count">%2$d</xliff:g> others</string>
<string name="retweeted_by_count">Retweeted by <xliff:g id="retweet_count">%d</xliff:g> users</string>
<string name="users_retweeted_this">Users retweeted this</string>
<string name="users_favorited_this">Users favorited this</string>
<string name="reply_to">Reply to <xliff:g id="user_name">%s</xliff:g></string>

View File

@ -151,7 +151,28 @@
<item name="android:background">?android:selectableItemBackground</item>
</style>
<style name="Widget.Light.CardActionButton" parent="android:Widget.DeviceDefault.ImageButton">
<style name="Widget.Light.CardActionButton" parent="android:Widget.DeviceDefault.Light.ImageButton">
<item name="android:background">?android:selectableItemBackground</item>
</style>
<style name="Widget.ProfileImage" parent="android:Widget.DeviceDefault">
<item name="android:scaleType">centerCrop</item>
<item name="borderWidth">1dp</item>
</style>
<style name="Widget.Light.ProfileImage" parent="android:Widget.DeviceDefault">
<item name="android:scaleType">centerCrop</item>
<item name="borderWidth">1dp</item>
</style>
<style name="Widget.ProfileImage.Large">
<item name="android:background">@drawable/bg_oval_black</item>
<item name="borderWidth">2dp</item>
</style>
<style name="Widget.Light.ProfileImage.Large">
<item name="android:background">@drawable/bg_oval_white</item>
<item name="borderWidth">2dp</item>
</style>
</resources>

View File

@ -39,6 +39,7 @@
<!-- Card UI styles -->
<item name="cardActionButtonStyle">@style/Widget.CardActionButton</item>
<item name="profileImageStyleLarge">@style/Widget.ProfileImage.Large</item>
<item name="cardItemBackground">@drawable/bg_card_item_dark</item>
<item name="cardItemBackgroundColor">#1a1a1a</item>
<item name="cardItemMessageIncomingBackground">
@ -83,6 +84,7 @@
<!-- Card UI styles -->
<item name="cardActionButtonStyle">@style/Widget.Light.CardActionButton</item>
<item name="profileImageStyleLarge">@style/Widget.Light.ProfileImage.Large</item>
<item name="cardItemBackground">@drawable/bg_card_item_light</item>
<item name="cardItemBackgroundColor">#f8f8f8</item>
<item name="cardItemMessageIncomingBackground">
@ -153,6 +155,7 @@
<!-- Card UI styles -->
<item name="cardActionButtonStyle">@style/Widget.CardActionButton</item>
<item name="profileImageStyleLarge">@style/Widget.ProfileImage.Large</item>
<item name="cardItemBackground">@drawable/bg_card_item_dark</item>
<item name="cardItemBackgroundColor">#1a1a1a</item>
<item name="cardItemMessageIncomingBackground">
@ -182,6 +185,7 @@
<!-- Card UI styles -->
<item name="cardActionButtonStyle">@style/Widget.Light.CardActionButton</item>
<item name="profileImageStyleLarge">@style/Widget.Light.ProfileImage.Large</item>
<item name="cardItemBackground">@drawable/bg_card_item_light</item>
<item name="cardItemBackgroundColor">#f8f8f8</item>
<item name="cardItemMessageIncomingBackground">
@ -240,6 +244,7 @@
<!-- Card UI styles -->
<item name="cardActionButtonStyle">@style/Widget.CardActionButton</item>
<item name="profileImageStyleLarge">@style/Widget.ProfileImage.Large</item>
<item name="cardItemBackground">@drawable/bg_card_item_dark</item>
<item name="cardItemBackgroundColor">#1a1a1a</item>
<item name="cardItemMessageIncomingBackground">
@ -338,6 +343,7 @@
<!-- Card UI styles -->
<item name="cardActionButtonStyle">@style/Widget.CardActionButton</item>
<item name="profileImageStyleLarge">@style/Widget.ProfileImage.Large</item>
<item name="cardItemBackground">@drawable/bg_card_item_dark</item>
<item name="cardItemBackgroundColor">#1a1a1a</item>
<item name="cardItemMessageIncomingBackground">
@ -373,6 +379,7 @@
<!-- Card UI styles -->
<item name="cardActionButtonStyle">@style/Widget.Light.CardActionButton</item>
<item name="profileImageStyleLarge">@style/Widget.Light.ProfileImage.Large</item>
<item name="cardItemBackground">@drawable/bg_card_item_light</item>
<item name="cardItemBackgroundColor">#f8f8f8</item>
<item name="cardItemMessageIncomingBackground">
@ -408,6 +415,7 @@
<!-- Card UI styles -->
<item name="cardActionButtonStyle">@style/Widget.Light.CardActionButton</item>
<item name="profileImageStyleLarge">@style/Widget.Light.ProfileImage.Large</item>
<item name="cardItemBackground">@drawable/bg_card_item_light</item>
<item name="cardItemBackgroundColor">#f8f8f8</item>
<item name="cardItemMessageIncomingBackground">