removed unused resource

improved favorite feature
improving tab indicator
This commit is contained in:
Mariotaku Lee 2014-12-12 21:53:18 +08:00
parent 8d18bdf844
commit a31cde86a1
81 changed files with 1240 additions and 3531 deletions

View File

@ -92,7 +92,6 @@ dependencies {
compile 'org.apache.httpcomponents:httpclient-android:4.3.5'
compile 'org.apache.httpcomponents:httpmime:4.3.5'
compile 'com.google.android.apps.dashclock:dashclock-api:2.0.0'
compile 'com.astuetz:pagerslidingtabstrip:1.0.1'
compile 'com.squareup:otto:1.3.5'
googleCompile 'com.google.android.gms:play-services:6.5.87'
fdroidCompile 'org.osmdroid:osmdroid-android:4.2'

View File

@ -532,13 +532,11 @@ public class HomeActivity extends BaseSupportActivity implements OnClickListener
mTabIndicator.setViewPager(mViewPager);
mTabIndicator.setOnPageChangeListener(this);
if (mTabDisplayOption != 0) {
mTabIndicator.setDisplayLabel((mTabDisplayOption & VALUE_TAB_DIPLAY_OPTION_CODE_LABEL) != 0);
mTabIndicator.setDisplayIcon((mTabDisplayOption & VALUE_TAB_DIPLAY_OPTION_CODE_ICON) != 0);
mTabIndicator.setTabDisplayOption(mTabDisplayOption);
} else {
mTabIndicator.setDisplayLabel(false);
mTabIndicator.setDisplayIcon(true);
mTabIndicator.setTabDisplayOption(TabPagerIndicator.ICON);
}
// mTabIndicator.setDisplayBadge(mPreferences.getBoolean(KEY_UNREAD_COUNT, true));
mTabIndicator.setDisplayBadge(mPreferences.getBoolean(KEY_UNREAD_COUNT, true));
mActionsButton.setOnClickListener(this);
mActionsButton.setOnLongClickListener(this);
setTabPosition(initialTabPosition);

View File

@ -16,6 +16,7 @@ import org.mariotaku.twidere.adapter.iface.IStatusesAdapter;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.fragment.support.UserFragment;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.ImageLoadingHandler;
import org.mariotaku.twidere.util.Utils;
@ -38,15 +39,18 @@ public abstract class AbsStatusesAdapter<D> extends Adapter<ViewHolder> implemen
private final ImageLoaderWrapper mImageLoader;
private final ImageLoadingHandler mLoadingHandler;
private final int mCardLayoutResource;
private final AsyncTwitterWrapper mTwitterWrapper;
private boolean mLoadMoreIndicatorEnabled;
private StatusAdapterListener mStatusAdapterListener;
public AbsStatusesAdapter(Context context, boolean compact) {
mContext = context;
final TwidereApplication app = TwidereApplication.getInstance(context);
mInflater = LayoutInflater.from(context);
mImageLoader = TwidereApplication.getInstance(context).getImageLoaderWrapper();
mImageLoader = app.getImageLoaderWrapper();
mLoadingHandler = new ImageLoadingHandler(R.id.media_preview_progress);
mTwitterWrapper = app.getTwitterWrapper();
if (compact) {
mCardLayoutResource = R.layout.card_item_status_compat;
} else {
@ -54,62 +58,13 @@ public abstract class AbsStatusesAdapter<D> extends Adapter<ViewHolder> implemen
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case ITEM_VIEW_TYPE_STATUS: {
final View view = mInflater.inflate(mCardLayoutResource, parent, false);
final StatusViewHolder holder = new StatusViewHolder(this, view);
holder.setupViews();
return holder;
}
case ITEM_VIEW_TYPE_GAP: {
final View view = mInflater.inflate(R.layout.card_item_gap, parent, false);
return new GapViewHolder(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 abstract D getData();
public void setLoadMoreIndicatorEnabled(boolean enabled) {
if (mLoadMoreIndicatorEnabled == enabled) return;
mLoadMoreIndicatorEnabled = enabled;
notifyDataSetChanged();
}
public boolean hasLoadMoreIndicator() {
return mLoadMoreIndicatorEnabled;
}
public abstract void setData(D data);
@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;
} else if (isGapItem(position)) {
return ITEM_VIEW_TYPE_GAP;
}
return ITEM_VIEW_TYPE_STATUS;
}
@Override
public final int getItemCount() {
return getStatusCount() + (mLoadMoreIndicatorEnabled ? 1 : 0);
public AsyncTwitterWrapper getTwitterWrapper() {
return mTwitterWrapper;
}
@Override
@ -150,6 +105,63 @@ public abstract class AbsStatusesAdapter<D> extends Adapter<ViewHolder> implemen
}
}
public boolean hasLoadMoreIndicator() {
return mLoadMoreIndicatorEnabled;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case ITEM_VIEW_TYPE_STATUS: {
final View view = mInflater.inflate(mCardLayoutResource, parent, false);
final StatusViewHolder holder = new StatusViewHolder(this, view);
holder.setupViews();
return holder;
}
case ITEM_VIEW_TYPE_GAP: {
final View view = mInflater.inflate(R.layout.card_item_gap, parent, false);
return new GapViewHolder(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);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case ITEM_VIEW_TYPE_STATUS: {
bindStatus(((StatusViewHolder) holder), position);
break;
}
}
}
@Override
public int getItemViewType(int position) {
if (position == getItemCount() - 1) {
return ITEM_VIEW_TYPE_LOAD_INDICATOR;
} else if (isGapItem(position)) {
return ITEM_VIEW_TYPE_GAP;
}
return ITEM_VIEW_TYPE_STATUS;
}
@Override
public final int getItemCount() {
return getStatusCount() + (mLoadMoreIndicatorEnabled ? 1 : 0);
}
@Override
public final void onGapClick(ViewHolder holder, int position) {
if (mStatusAdapterListener != null) {
mStatusAdapterListener.onGapClick((GapViewHolder) holder, position);
}
}
@Override
public void onItemActionClick(ViewHolder holder, int id, int position) {
if (mStatusAdapterListener != null) {
@ -164,28 +176,25 @@ public abstract class AbsStatusesAdapter<D> extends Adapter<ViewHolder> implemen
}
}
public abstract void setData(D data);
public abstract D getData();
public void setEventListener(StatusAdapterListener listener) {
mStatusAdapterListener = listener;
}
@Override
public final void onGapClick(ViewHolder holder, int position) {
if (mStatusAdapterListener != null) {
mStatusAdapterListener.onGapClick((GapViewHolder) holder, position);
}
public void setLoadMoreIndicatorEnabled(boolean enabled) {
if (mLoadMoreIndicatorEnabled == enabled) return;
mLoadMoreIndicatorEnabled = enabled;
notifyDataSetChanged();
}
protected abstract void bindStatus(StatusViewHolder holder, int position);
public static interface StatusAdapterListener {
void onGapClick(GapViewHolder holder, int position);
void onStatusActionClick(StatusViewHolder holder, int id, int position);
void onStatusClick(StatusViewHolder holder, int position);
void onGapClick(GapViewHolder holder, int position);
void onStatusMenuClick(StatusViewHolder holder, int position);
}

View File

@ -24,6 +24,7 @@ import android.database.Cursor;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.ParcelableStatus.CursorIndices;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.view.holder.StatusViewHolder;
/**
@ -72,7 +73,6 @@ public class CursorStatusesAdapter extends AbsStatusesAdapter<Cursor> {
mIndices = data != null ? new CursorIndices(data) : null;
notifyDataSetChanged();
}
@Override
public Cursor getData() {
return mCursor;

View File

@ -1,347 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.adapter;
import android.app.Activity;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.widget.RecyclerView.ViewHolder;
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.util.ImageLoadingHandler;
import org.mariotaku.twidere.util.MultiSelectManager;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.holder.StatusListViewHolder;
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.util.Utils.configBaseCardAdapter;
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 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));
}
public ParcelableStatusesListAdapter(final Context context, final boolean compactCards) {
super(context, getItemResource(compactCards));
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 void onItemActionClick(ViewHolder holder, int id, int position) {
}
@Override
public void onItemMenuClick(ViewHolder holder, int position) {
}
@Override
public void onStatusClick(StatusViewHolder holder, int position) {
}
@Override
public void onUserProfileClick(StatusViewHolder holder, int position) {
}
@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 StatusViewHolder holder;
if (tag instanceof StatusViewHolder) {
holder = (StatusViewHolder) tag;
} else {
holder = new StatusViewHolder(this, view);
view.setTag(holder);
}
final ParcelableStatus status = getItem(position);
holder.displayStatus(status);
return view;
}
@Override
public boolean isGapItem(int position) {
return false;
}
@Override
public void onGapClick(ViewHolder holder, int position) {
}
@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(mContext, status.account_id, status.user_id,
status.user_screen_name, null);
}
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_compat : R.layout.card_item_status;
}
}

View File

@ -1,157 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.adapter;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import org.mariotaku.twidere.R;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
public class SeparatedListAdapter<T extends Adapter> extends BaseAdapter {
private final Map<String, T> mSections = new LinkedHashMap<String, T>();
private final ArrayAdapter<String> mHeaders;
private final static int TYPE_SECTION_HEADER = 0;
public SeparatedListAdapter(final Context context) {
mHeaders = new ArrayAdapter<String>(context, R.layout.list_item_section_header);
}
public void addSection(final String section, final T adapter) {
mHeaders.add(section);
mSections.put(section, adapter);
notifyDataSetChanged();
}
public boolean areAllItemsSelectable() {
return false;
}
public void clear() {
mHeaders.clear();
mSections.clear();
notifyDataSetChanged();
}
public ArrayList<T> getAdapters() {
return new ArrayList<T>(mSections.values());
}
@Override
public int getCount() {
// total together all sections, plus one for each section header
int total = 0;
for (final T adapter : mSections.values()) {
total += adapter.getCount() + 1;
}
return total;
}
@Override
public Object getItem(int position) {
for (final Object section : mSections.keySet()) {
final Adapter adapter = mSections.get(section);
final int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0) return section;
if (position < size) return adapter.getItem(position - 1);
// otherwise jump into next section
position -= size;
}
return null;
}
@Override
public long getItemId(final int position) {
return position;
}
@Override
public int getItemViewType(int position) {
int type = 1;
for (final Object section : mSections.keySet()) {
final Adapter adapter = mSections.get(section);
final int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0) return TYPE_SECTION_HEADER;
if (position < size) return type + adapter.getItemViewType(position - 1);
// otherwise jump into next section
position -= size;
type += adapter.getViewTypeCount();
}
return -1;
}
@Override
public View getView(int position, final View convertView, final ViewGroup parent) {
int sectionnum = 0;
for (final Object section : mSections.keySet()) {
final Adapter adapter = mSections.get(section);
final int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0) return mHeaders.getView(sectionnum, convertView, parent);
if (position < size) return adapter.getView(position - 1, convertView, parent);
// otherwise jump into next section
position -= size;
sectionnum++;
}
return null;
}
@Override
public int getViewTypeCount() {
// assume that headers count as one, then total all sections
int total = 1;
for (final Adapter adapter : mSections.values()) {
total += adapter.getViewTypeCount();
}
return total;
}
@Override
public boolean isEnabled(final int position) {
return getItemViewType(position) != TYPE_SECTION_HEADER;
}
@Override
public void notifyDataSetChanged() {
for (final T adapter : mSections.values()) {
if (adapter instanceof BaseAdapter) {
((BaseAdapter) adapter).notifyDataSetChanged();
}
}
super.notifyDataSetChanged();
}
}

View File

@ -3,6 +3,7 @@ package org.mariotaku.twidere.adapter.iface;
import android.content.Context;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.ImageLoadingHandler;
import org.mariotaku.twidere.view.holder.StatusViewHolder;
@ -27,4 +28,6 @@ public interface IStatusesAdapter<Data> extends IGapSupportedAdapter, ICardSuppo
void onUserProfileClick(StatusViewHolder holder, int position);
void setData(Data data);
AsyncTwitterWrapper getTwitterWrapper();
}

View File

@ -18,6 +18,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.AbsStatusesAdapter;
@ -31,6 +32,7 @@ import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.SimpleDrawerCallback;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.message.StatusListChangedEvent;
import org.mariotaku.twidere.view.HeaderDrawerLayout.DrawerCallback;
import org.mariotaku.twidere.view.holder.GapViewHolder;
import org.mariotaku.twidere.view.holder.StatusViewHolder;
@ -41,6 +43,13 @@ import org.mariotaku.twidere.view.holder.StatusViewHolder;
public abstract class AbsStatusesFragment<Data> extends BaseSupportFragment implements LoaderCallbacks<Data>,
OnRefreshListener, DrawerCallback, RefreshScrollTopInterface, StatusAdapterListener {
private final Object mStatusesBusCallback;
protected AbsStatusesFragment() {
mStatusesBusCallback = createMessageBusCallback();
}
private View mContentView;
private SharedPreferences mPreferences;
private View mProgressContainer;
@ -151,19 +160,13 @@ public abstract class AbsStatusesFragment<Data> extends BaseSupportFragment impl
public void onStart() {
super.onStart();
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
final Object callback = getMessageBusCallback();
if (callback != null) {
bus.register(callback);
}
bus.register(mStatusesBusCallback);
}
@Override
public void onStop() {
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
final Object callback = getMessageBusCallback();
if (callback != null) {
bus.unregister(callback);
}
bus.unregister(mStatusesBusCallback);
super.onStop();
}
@ -273,8 +276,8 @@ public abstract class AbsStatusesFragment<Data> extends BaseSupportFragment impl
mAdapter.setData(data);
}
protected Object getMessageBusCallback() {
return null;
protected Object createMessageBusCallback() {
return new StatusesBusCallback();
}
protected abstract AbsStatusesAdapter<Data> onCreateAdapter(Context context, boolean compact);
@ -285,4 +288,17 @@ public abstract class AbsStatusesFragment<Data> extends BaseSupportFragment impl
mProgressContainer.setVisibility(shown ? View.GONE : View.VISIBLE);
mSwipeRefreshLayout.setVisibility(shown ? View.VISIBLE : View.GONE);
}
protected final class StatusesBusCallback {
protected StatusesBusCallback() {
}
@Subscribe
public void notifyStatusListChanged(StatusListChangedEvent event) {
mAdapter.notifyDataSetChanged();
}
}
}

View File

@ -1,592 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.fragment.support;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.util.LongSparseArray;
import android.util.Log;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ImageView.ScaleType;
import android.widget.ListView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter.MenuButtonClickListener;
import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter;
import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.ParcelableAccount.ParcelableAccountWithCredentials;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.task.TwidereAsyncTask;
import org.mariotaku.twidere.util.AsyncTaskManager;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ClipboardUtils;
import org.mariotaku.twidere.util.MultiSelectManager;
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.StatusListViewHolder;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.mariotaku.twidere.util.Utils.clearListViewChoices;
import static org.mariotaku.twidere.util.Utils.configBaseCardAdapter;
import static org.mariotaku.twidere.util.Utils.isMyRetweet;
import static org.mariotaku.twidere.util.Utils.openStatus;
import static org.mariotaku.twidere.util.Utils.showOkMessage;
import static org.mariotaku.twidere.util.Utils.startStatusShareChooser;
abstract class BaseStatusesListFragment<Data> extends BasePullToRefreshListFragment implements LoaderCallbacks<Data>,
OnItemLongClickListener, OnMenuItemClickListener, MultiSelectManager.Callback, MenuButtonClickListener {
private AsyncTaskManager mAsyncTaskManager;
private SharedPreferences mPreferences;
private ListView mListView;
private IStatusesListAdapter<Data> mAdapter;
private Data mData;
private ParcelableStatus mSelectedStatus;
private boolean mLoadMoreAutomatically;
private int mListScrollOffset;
private MultiSelectManager mMultiSelectManager;
private PositionManager mPositionManager;
private int mFirstVisibleItem;
private int mSelectedPosition;
private final LongSparseArray<Set<Long>> mUnreadCountsToRemove = new LongSparseArray<>();
private final List<Integer> mReadPositions = new NoDuplicatesCopyOnWriteArrayList<>();
private RemoveUnreadCountsTask<Data> mRemoveUnreadCountsTask;
public AsyncTaskManager getAsyncTaskManager() {
return mAsyncTaskManager;
}
public final Data getData() {
return mData;
}
@Override
public IStatusesListAdapter<Data> getListAdapter() {
return mAdapter;
}
public ParcelableStatus getSelectedStatus() {
return mSelectedStatus;
}
public SharedPreferences getSharedPreferences() {
return mPreferences;
}
public abstract int getStatuses(long[] account_ids, long[] max_ids, long[] since_ids);
public final LongSparseArray<Set<Long>> getUnreadCountsToRemove() {
return mUnreadCountsToRemove;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final Context context = getActivity();
mAsyncTaskManager = getAsyncTaskManager();
mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
mPositionManager = new PositionManager(context);
mMultiSelectManager = getMultiSelectManager();
mListView = getListView();
final boolean compactCards = mPreferences.getBoolean(KEY_COMPACT_CARDS, false);
mAdapter = newAdapterInstance(compactCards);
mAdapter.setMenuButtonClickListener(this);
setListAdapter(null);
setListHeaderFooters(mListView);
setListAdapter(mAdapter);
if (!compactCards) {
mListView.setDivider(null);
}
mListView.setSelector(android.R.color.transparent);
mListView.setOnItemLongClickListener(this);
setListShown(false);
getLoaderManager().initLoader(0, getArguments(), this);
}
@Override
public abstract Loader<Data> onCreateLoader(int id, Bundle args);
@Override
public boolean onItemLongClick(final AdapterView<?> parent, final View view, final int position, final long id) {
final Object tag = view.getTag();
if (tag instanceof StatusListViewHolder) {
final StatusListViewHolder holder = (StatusListViewHolder) tag;
final ParcelableStatus status = mAdapter.getStatus(position - mListView.getHeaderViewsCount());
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (twitter != null) {
TwitterWrapper.removeUnreadCounts(getActivity(), getTabPosition(), status.account_id, status.id);
}
if (holder.show_as_gap) return false;
if (mPreferences.getBoolean(KEY_LONG_CLICK_TO_OPEN_MENU, false)) {
openMenu(holder.content.getFakeOverflowButton(), status, position);
} else {
setItemSelected(status, position, !mMultiSelectManager.isSelected(status));
}
return true;
}
return false;
}
@Override
public void onItemsCleared() {
clearListViewChoices(mListView);
}
@Override
public void onItemSelected(final Object item) {
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
@Override
public void onItemUnselected(final Object item) {
}
@Override
public void onListItemClick(final ListView l, final View v, final int position, final long id) {
final Object tag = v.getTag();
if (tag instanceof StatusListViewHolder) {
final int pos = position - l.getHeaderViewsCount();
final ParcelableStatus status = mAdapter.getStatus(pos);
if (status == null) return;
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (twitter != null) {
TwitterWrapper.removeUnreadCounts(getActivity(), getTabPosition(), status.account_id, status.id);
}
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 {
if (mMultiSelectManager.isActive()) {
setItemSelected(status, position, !mMultiSelectManager.isSelected(status));
return;
}
openStatus(getActivity(), status.account_id, status.id);
}
}
}
@Override
public final void onLoaderReset(final Loader<Data> loader) {
mAdapter.setData(mData = null);
}
@Override
public void onLoadFinished(final Loader<Data> loader, final Data data) {
if (getActivity() == null || getView() == null) return;
setListShown(true);
setRefreshComplete();
setProgressBarIndeterminateVisibility(false);
setData(data);
mFirstVisibleItem = -1;
mReadPositions.clear();
final int firstVisiblePosition = mListView.getFirstVisiblePosition();
final int lastVisiblePosition = mListView.getLastVisiblePosition();
final int listVisiblePosition, savedChildIndex;
final boolean rememberPosition = mPreferences.getBoolean(KEY_REMEMBER_POSITION, true);
final boolean loadMoreFromTop = mPreferences.getBoolean(KEY_LOAD_MORE_FROM_TOP, false);
final int childCount = mListView.getChildCount();
if (firstVisiblePosition != 0 && lastVisiblePosition != mListView.getCount() - 1 && loadMoreFromTop) {
listVisiblePosition = lastVisiblePosition;
savedChildIndex = childCount - 1;
if (childCount > 0) {
final View lastChild = mListView.getChildAt(savedChildIndex);
mListScrollOffset = lastChild != null ? lastChild.getTop() - mListView.getListPaddingTop() : 0;
}
} else {
listVisiblePosition = firstVisiblePosition;
savedChildIndex = 0;
if (childCount > 0) {
final View firstChild = mListView.getChildAt(savedChildIndex);
mListScrollOffset = firstChild != null ? firstChild.getTop() - mListView.getListPaddingTop() : 0;
}
}
final long lastViewedId = mAdapter.getStatusId(listVisiblePosition);
mAdapter.setData(data);
mAdapter.setShowAccountColor(shouldShowAccountColor());
final int currFirstVisiblePosition = mListView.getFirstVisiblePosition();
final long currViewedId = mAdapter.getStatusId(currFirstVisiblePosition);
final long statusId;
if (lastViewedId <= 0) {
if (!rememberPosition) return;
statusId = mPositionManager.getPosition(getPositionKey());
} else if ((listVisiblePosition > 0 || rememberPosition) && currViewedId > 0 && lastViewedId != currViewedId) {
statusId = lastViewedId;
} else {
if (listVisiblePosition == 0 && mAdapter.getStatusId(0) != lastViewedId) {
mAdapter.setMaxAnimationPosition(mListView.getLastVisiblePosition());
}
return;
}
final int position = mAdapter.findPositionByStatusId(statusId);
if (position > -1 && position < mListView.getCount()) {
mAdapter.setMaxAnimationPosition(mListView.getLastVisiblePosition());
mListView.setSelectionFromTop(position, mListScrollOffset);
mListScrollOffset = 0;
}
}
@Override
public void onMenuButtonClick(final View button, final int position, final long id) {
if (mMultiSelectManager.isActive()) return;
final ParcelableStatus status = mAdapter.getStatus(position);
if (status == null) return;
openMenu(button, status, position);
}
@Override
public final boolean onMenuItemClick(final MenuItem item) {
final ParcelableStatus status = mSelectedStatus;
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (status == null || twitter == null) return false;
switch (item.getItemId()) {
case MENU_VIEW: {
openStatus(getActivity(), status, null);
break;
}
case MENU_SHARE: {
startStatusShareChooser(getActivity(), status);
break;
}
case MENU_COPY: {
if (ClipboardUtils.setText(getActivity(), status.text_plain)) {
showOkMessage(getActivity(), R.string.text_copied, false);
}
break;
}
case MENU_RETWEET: {
if (isMyRetweet(status)) {
twitter.cancelRetweetAsync(status.account_id, status.id, status.my_retweet_id);
} else {
twitter.retweetStatusAsync(status.account_id, status.id);
}
break;
}
case MENU_QUOTE: {
final Intent intent = new Intent(INTENT_ACTION_QUOTE);
final Bundle bundle = new Bundle();
bundle.putParcelable(EXTRA_STATUS, status);
intent.putExtras(bundle);
startActivity(intent);
break;
}
case MENU_REPLY: {
final Intent intent = new Intent(INTENT_ACTION_REPLY);
final Bundle bundle = new Bundle();
bundle.putParcelable(EXTRA_STATUS, status);
intent.putExtras(bundle);
startActivity(intent);
break;
}
case MENU_FAVORITE: {
if (status.is_favorite) {
twitter.destroyFavoriteAsync(status.account_id, status.id);
} else {
twitter.createFavoriteAsync(status.account_id, status.id);
}
break;
}
case MENU_DELETE: {
DestroyStatusDialogFragment.show(getFragmentManager(), status);
break;
}
case MENU_ADD_TO_FILTER: {
AddStatusFilterDialogFragment.show(getFragmentManager(), status);
break;
}
case MENU_TRANSLATE: {
final ParcelableAccountWithCredentials account = ParcelableAccount.getAccountWithCredentials(getActivity(),
status.account_id);
if (ParcelableAccountWithCredentials.isOfficialCredentials(getActivity(), account)) {
StatusTranslateDialogFragment.show(getFragmentManager(), status);
} else {
}
break;
}
case MENU_MULTI_SELECT: {
final boolean isSelected = !mMultiSelectManager.isSelected(status);
setItemSelected(status, mSelectedPosition, isSelected);
break;
}
default: {
if (item.getIntent() != null) {
try {
startActivity(item.getIntent());
} catch (final ActivityNotFoundException e) {
Log.w(LOGTAG, e);
return false;
}
}
break;
}
}
return true;
}
@Override
public void onRefreshFromEnd() {
if (mLoadMoreAutomatically) return;
loadMoreStatuses();
}
@Override
public void onResume() {
super.onResume();
mListView.setFastScrollEnabled(mPreferences.getBoolean(KEY_FAST_SCROLL_THUMB, false));
configBaseCardAdapter(getActivity(), mAdapter);
final boolean displayImagePreview = mPreferences.getBoolean(KEY_DISPLAY_IMAGE_PREVIEW, false);
final boolean displaySensitiveContents = mPreferences.getBoolean(KEY_DISPLAY_SENSITIVE_CONTENTS, false);
final boolean indicateMyStatus = mPreferences.getBoolean(KEY_INDICATE_MY_STATUS, true);
final String cardHighlightOption = mPreferences.getString(KEY_CARD_HIGHLIGHT_OPTION,
DEFAULT_CARD_HIGHLIGHT_OPTION);
final String previewScaleType = Utils.getNonEmptyString(mPreferences, KEY_IMAGE_PREVIEW_SCALE_TYPE,
ScaleType.CENTER_CROP.name());
mAdapter.setDisplayImagePreview(displayImagePreview);
mAdapter.setImagePreviewScaleType(previewScaleType);
mAdapter.setDisplaySensitiveContents(displaySensitiveContents);
mAdapter.setIndicateMyStatusDisabled(isMyTimeline() || !indicateMyStatus);
mAdapter.setCardHighlightOption(cardHighlightOption);
mAdapter.notifyDataSetChanged();
mLoadMoreAutomatically = mPreferences.getBoolean(KEY_LOAD_MORE_AUTOMATICALLY, false);
}
@Override
public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
final int totalItemCount) {
super.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
addReadPosition(firstVisibleItem);
}
@Override
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
super.onScrollStateChanged(view, scrollState);
switch (scrollState) {
case SCROLL_STATE_IDLE:
for (int i = mListView.getFirstVisiblePosition(), j = mListView.getLastVisiblePosition(); i < j; i++) {
mReadPositions.add(i);
}
removeUnreadCounts();
break;
default:
break;
}
}
@Override
public void onStart() {
super.onStart();
mMultiSelectManager.registerCallback(this);
final int choiceMode = mListView.getChoiceMode();
if (mMultiSelectManager.isActive()) {
if (choiceMode != ListView.CHOICE_MODE_MULTIPLE) {
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
} else {
if (choiceMode != ListView.CHOICE_MODE_NONE) {
Utils.clearListViewChoices(mListView);
}
}
updateRefreshState();
}
@Override
public void onStop() {
savePosition();
mMultiSelectManager.unregisterCallback(this);
super.onStop();
}
@Override
public boolean scrollToStart() {
final AsyncTwitterWrapper twitter = getTwitterWrapper();
final int tab_position = getTabPosition();
if (twitter != null && tab_position >= 0) {
twitter.clearUnreadCountAsync(tab_position);
}
return super.scrollToStart();
}
@Override
public void setUserVisibleHint(final boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
updateRefreshState();
}
protected final int getListScrollOffset() {
return mListScrollOffset;
}
protected abstract long[] getNewestStatusIds();
protected abstract long[] getOldestStatusIds();
protected abstract String getPositionKey();
protected boolean isMyTimeline() {
return false;
}
protected abstract void loadMoreStatuses();
protected abstract IStatusesListAdapter<Data> newAdapterInstance(boolean compact);
@Override
protected void onReachedBottom() {
if (!mLoadMoreAutomatically) return;
loadMoreStatuses();
}
protected void savePosition() {
final int first_visible_position = mListView.getFirstVisiblePosition();
if (mListView.getChildCount() > 0) {
final View first_child = mListView.getChildAt(0);
mListScrollOffset = first_child != null ? first_child.getTop() : 0;
}
final long status_id = mAdapter.getStatusId(first_visible_position);
mPositionManager.setPosition(getPositionKey(), status_id);
}
protected final void setData(final Data data) {
mData = data;
}
protected void setItemSelected(final ParcelableStatus status, final int position, final boolean selected) {
if (selected) {
mMultiSelectManager.selectItem(status);
} else {
mMultiSelectManager.unselectItem(status);
}
if (position >= 0) {
mListView.setItemChecked(position, selected);
}
}
protected void setListHeaderFooters(final ListView list) {
}
protected boolean shouldEnablePullToRefresh() {
return true;
}
protected abstract boolean shouldShowAccountColor();
protected abstract void updateRefreshState();
private void addReadPosition(final int firstVisibleItem) {
if (mFirstVisibleItem != firstVisibleItem) {
mReadPositions.add(firstVisibleItem);
}
mFirstVisibleItem = firstVisibleItem;
}
private void addUnreadCountsToRemove(final long accountId, final long id) {
if (mUnreadCountsToRemove.indexOfKey(accountId) < 0) {
final Set<Long> counts = new HashSet<>();
counts.add(id);
mUnreadCountsToRemove.put(accountId, counts);
} else {
final Set<Long> counts = mUnreadCountsToRemove.get(accountId);
counts.add(id);
}
}
private void openMenu(final View view, final ParcelableStatus status, final int position) {
mSelectedStatus = status;
mSelectedPosition = position;
final FragmentActivity activity = getActivity();
if (activity == null || activity.isFinishing() || view == null || status == null) return;
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (twitter != null) {
TwitterWrapper.removeUnreadCounts(getActivity(), getTabPosition(), status.account_id, status.id);
}
final StatusMenuDialogFragment df = new StatusMenuDialogFragment();
final Bundle args = new Bundle();
args.putParcelable(EXTRA_STATUS, status);
df.setArguments(args);
df.show(getChildFragmentManager(), "status_menu");
}
private void removeUnreadCounts() {
if (mRemoveUnreadCountsTask != null && mRemoveUnreadCountsTask.getStatus() == TwidereAsyncTask.Status.RUNNING)
return;
mRemoveUnreadCountsTask = new RemoveUnreadCountsTask<Data>(mReadPositions, this);
mRemoveUnreadCountsTask.executeTask();
}
static class RemoveUnreadCountsTask<T> extends TwidereAsyncTask<Void, Void, Void> {
private final List<Integer> read_positions;
private final IStatusesListAdapter<T> adapter;
private final BaseStatusesListFragment<T> fragment;
RemoveUnreadCountsTask(final List<Integer> read_positions, final BaseStatusesListFragment<T> fragment) {
this.read_positions = read_positions;
this.fragment = fragment;
this.adapter = fragment.getListAdapter();
}
@Override
protected Void doInBackground(final Void... params) {
for (final int pos : read_positions) {
final long id = adapter.getStatusId(pos), account_id = adapter.getAccountId(pos);
fragment.addUnreadCountsToRemove(account_id, id);
}
return null;
}
@Override
protected void onPostExecute(final Void result) {
final AsyncTwitterWrapper twitter = fragment.getTwitterWrapper();
if (twitter != null) {
twitter.removeUnreadCountsAsync(fragment.getTabPosition(), fragment.getUnreadCountsToRemove());
}
}
}
}

View File

@ -32,6 +32,7 @@ import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.message.FavoriteCreatedEvent;
import org.mariotaku.twidere.util.message.FavoriteDestroyedEvent;
import org.mariotaku.twidere.util.message.StatusDestroyedEvent;
import org.mariotaku.twidere.util.message.StatusListChangedEvent;
import org.mariotaku.twidere.util.message.StatusRetweetedEvent;
import java.util.HashSet;
@ -43,11 +44,6 @@ import java.util.Set;
*/
public abstract class ParcelableStatusesFragment extends AbsStatusesFragment<List<ParcelableStatus>> {
private final StatusesBusCallback mStatusesBusCallback;
protected ParcelableStatusesFragment() {
mStatusesBusCallback = new StatusesBusCallback(this);
}
public final void deleteStatus(final long statusId) {
final List<ParcelableStatus> data = getAdapterData();
@ -98,8 +94,8 @@ public abstract class ParcelableStatusesFragment extends AbsStatusesFragment<Lis
}
@Override
protected Object getMessageBusCallback() {
return mStatusesBusCallback;
protected Object createMessageBusCallback() {
return new ParcelableStatusesBusCallback();
}
@Override
@ -155,36 +151,6 @@ public abstract class ParcelableStatusesFragment extends AbsStatusesFragment<Lis
}
}
protected static class StatusesBusCallback {
private final ParcelableStatusesFragment fragment;
StatusesBusCallback(ParcelableStatusesFragment fragment) {
this.fragment = fragment;
}
@Subscribe
public void notifyFavoriteCreated(FavoriteCreatedEvent event) {
fragment.updateFavoritedStatus(event.status);
}
@Subscribe
public void notifyStatusRetweeted(StatusRetweetedEvent event) {
fragment.updateRetweetedStatuses(event.status);
}
@Subscribe
public void notifyFavoriteDestroyed(FavoriteDestroyedEvent event) {
fragment.updateFavoritedStatus(event.status);
}
@Subscribe
public void notifyStatusDestroyed(StatusDestroyedEvent event) {
fragment.deleteStatus(event.status.id);
}
}
private void updateRetweetedStatuses(ParcelableStatus status) {
final List<ParcelableStatus> data = getAdapterData();
if (status == null || status.retweet_id <= 0 || data == null) return;
@ -197,4 +163,33 @@ public abstract class ParcelableStatusesFragment extends AbsStatusesFragment<Lis
setAdapterData(data);
}
protected class ParcelableStatusesBusCallback {
@Subscribe
public void notifyFavoriteCreated(FavoriteCreatedEvent event) {
updateFavoritedStatus(event.status);
}
@Subscribe
public void notifyFavoriteDestroyed(FavoriteDestroyedEvent event) {
updateFavoritedStatus(event.status);
}
@Subscribe
public void notifyStatusDestroyed(StatusDestroyedEvent event) {
deleteStatus(event.status.id);
}
@Subscribe
public void notifyStatusListChanged(StatusListChangedEvent event) {
getAdapter().notifyDataSetChanged();
}
@Subscribe
public void notifyStatusRetweeted(StatusRetweetedEvent event) {
updateRetweetedStatuses(event.status);
}
}
}

View File

@ -1,222 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.fragment.support;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
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;
import org.mariotaku.twidere.util.ArrayUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static org.mariotaku.twidere.util.Utils.encodeQueryParams;
public abstract class ParcelableStatusesListFragment extends BaseStatusesListFragment<List<ParcelableStatus>> {
protected SharedPreferences mPreferences;
private boolean mStatusesRestored;
private boolean mIsFirstCreated;
private final BroadcastReceiver mStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
if (getActivity() == null || !isAdded() || isDetached()) return;
final String action = intent.getAction();
switch (action) {
// case BROADCAST_STATUS_RETWEETED: {
// final long status_id = intent.getLongExtra(EXTRA_STATUS_ID, -1);
// final boolean retweeted = intent.getBooleanExtra(EXTRA_RETWEETED, false);
// if (status_id > 0 && !retweeted) {
// deleteStatus(status_id);
// }
// break;
// }
case BROADCAST_MULTI_MUTESTATE_CHANGED: {
final Bundle args = getArguments();
final long account_id = args != null ? args.getLong(EXTRA_ACCOUNT_ID, -1) : -1;
if (account_id <= 0) return;
getStatuses(new long[]{account_id}, null, null);
break;
}
}
}
};
public final void deleteStatus(final long status_id) {
final List<ParcelableStatus> data = getData();
if (status_id <= 0 || data == null) return;
final ArrayList<ParcelableStatus> data_to_remove = new ArrayList<ParcelableStatus>();
for (final ParcelableStatus status : data) {
if (status.id == status_id || status.retweet_id > 0 && status.retweet_id == status_id) {
data_to_remove.add(status);
}
}
data.removeAll(data_to_remove);
getListAdapter().setData(data);
}
@Override
public final int getStatuses(final long[] accountIds, final long[] maxIds, final long[] sinceIds) {
mStatusesRestored = true;
final long maxId = maxIds != null && maxIds.length == 1 ? maxIds[0] : -1;
final long sinceId = sinceIds != null && sinceIds.length == 1 ? sinceIds[0] : -1;
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 onActivityCreated(final Bundle savedInstanceState) {
mStatusesRestored = false;
if (savedInstanceState != null) {
final List<ParcelableStatus> saved = savedInstanceState.getParcelableArrayList(EXTRA_DATA);
if (saved != null) {
setData(saved);
}
}
super.onActivityCreated(savedInstanceState);
mPreferences = getSharedPreferences();
mIsFirstCreated = savedInstanceState == null;
}
@Override
public final Loader<List<ParcelableStatus>> onCreateLoader(final int id, final Bundle args) {
setProgressBarIndeterminateVisibility(true);
final List<ParcelableStatus> data = getData();
if (isInstanceStateSaved() && data != null && !mStatusesRestored)
return new DummyParcelableStatusesLoader(getActivity(), data);
final Loader<List<ParcelableStatus>> loader = newLoaderInstance(getActivity(), args);
return loader != null ? loader : new DummyParcelableStatusesLoader(getActivity());
}
@Override
public void onLoadFinished(final Loader<List<ParcelableStatus>> loader, final List<ParcelableStatus> data) {
super.onLoadFinished(loader, data);
if (mIsFirstCreated && mPreferences.getBoolean(KEY_REFRESH_ON_START, false)) {
onRefreshFromStart();
}
}
@Override
public void onRefreshFromStart() {
if (isRefreshing()) return;
final IStatusesListAdapter<List<ParcelableStatus>> adapter = getListAdapter();
final int count = adapter.getCount();
final ParcelableStatus status = count > 0 ? adapter.getStatus(0) : null;
if (status != null) {
getStatuses(new long[]{status.account_id}, null, new long[]{status.id});
} else {
getStatuses(null, null, null);
}
}
@Override
public void onSaveInstanceState(final Bundle outState) {
final List<ParcelableStatus> data = getData();
if (data != null) {
outState.putParcelableArrayList(EXTRA_DATA, new ArrayList<>(data));
}
super.onSaveInstanceState(outState);
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onStop() {
super.onStop();
}
@Override
protected final long[] getNewestStatusIds() {
final IStatusesListAdapter<List<ParcelableStatus>> adapter = getListAdapter();
final long last_id = adapter.getCount() > 0 ? adapter.getStatus(0).id : -1;
return last_id > 0 ? new long[]{last_id} : null;
}
@Override
protected final long[] getOldestStatusIds() {
final IStatusesListAdapter<List<ParcelableStatus>> adapter = getListAdapter();
final ParcelableStatus status = adapter.getLastStatus();
final long last_id = status != null ? status.id : -1;
return last_id > 0 ? new long[]{last_id} : null;
}
@Override
protected final String getPositionKey() {
final Object[] args = getSavedStatusesFileArgs();
if (args == null || args.length <= 0) return null;
try {
return encodeQueryParams(ArrayUtils.toString(args, '.', false) + "." + getTabPosition());
} catch (final IOException e) {
e.printStackTrace();
}
return null;
}
protected abstract Object[] getSavedStatusesFileArgs();
@Override
protected void loadMoreStatuses() {
if (isRefreshing()) return;
final IStatusesListAdapter<List<ParcelableStatus>> adapter = getListAdapter();
final ParcelableStatus status = adapter.getLastStatus();
if (status != null) {
getStatuses(new long[]{status.account_id}, new long[]{status.id}, null);
}
}
@Override
protected ParcelableStatusesListAdapter newAdapterInstance(final boolean compact) {
return new ParcelableStatusesListAdapter(getActivity(), compact);
}
protected abstract Loader<List<ParcelableStatus>> newLoaderInstance(Context context, Bundle args);
@Override
protected void updateRefreshState() {
if (getActivity() == null || !getUserVisibleHint() || !isVisible()) return;
final LoaderManager lm = getLoaderManager();
final boolean hasRunningLoaders = lm.hasRunningLoaders();
if (!hasRunningLoaders) {
setRefreshing(true);
}
setRefreshing(hasRunningLoaders);
}
}

View File

@ -72,6 +72,7 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
final Context context = builder.getContext();
final ImageLoaderWrapper loader = TwidereApplication.getInstance(context).getImageLoaderWrapper();
final ImageLoadingHandler handler = new ImageLoadingHandler(R.id.media_preview_progress);
final AsyncTwitterWrapper twitter = getTwitterWrapper();
final LayoutInflater inflater = LayoutInflater.from(context);
@SuppressLint("InflateParams") final View view = inflater.inflate(R.layout.dialog_scrollable_status, null);
final StatusViewHolder holder = new StatusViewHolder(view.findViewById(R.id.item_content));
@ -85,7 +86,7 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
builder.setNegativeButton(android.R.string.cancel, null);
holder.displayStatus(context, loader, handler, getStatus());
holder.displayStatus(context, loader, handler, twitter, getStatus());
view.findViewById(R.id.item_menu).setVisibility(View.GONE);
view.findViewById(R.id.action_buttons).setVisibility(View.GONE);
view.findViewById(R.id.reply_retweet_status).setVisibility(View.GONE);

View File

@ -33,8 +33,6 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import com.astuetz.PagerSlidingTabStrip;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.support.LinkHandlerActivity;
import org.mariotaku.twidere.adapter.support.SupportTabsAdapter;
@ -43,6 +41,7 @@ import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface;
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback;
import org.mariotaku.twidere.provider.RecentSearchProvider;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.view.TabPagerIndicator;
public class SearchFragment extends BaseSupportFragment implements RefreshScrollTopInterface,
SupportFragmentCallback, SystemWindowsInsetsCallback {
@ -50,7 +49,7 @@ public class SearchFragment extends BaseSupportFragment implements RefreshScroll
private ViewPager mViewPager;
private SupportTabsAdapter mAdapter;
private PagerSlidingTabStrip mPagerIndicator;
private TabPagerIndicator mPagerIndicator;
private Fragment mCurrentVisibleFragment;
@ -141,7 +140,7 @@ public class SearchFragment extends BaseSupportFragment implements RefreshScroll
public void onViewCreated(final View view, final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewPager = (ViewPager) view.findViewById(R.id.view_pager);
mPagerIndicator = (PagerSlidingTabStrip) view.findViewById(R.id.view_pager_tabs);
mPagerIndicator = (TabPagerIndicator) view.findViewById(R.id.view_pager_tabs);
}
@Override

View File

@ -438,6 +438,11 @@ public class StatusFragment extends BaseSupportFragment
}
@Override
public AsyncTwitterWrapper getTwitterWrapper() {
return mFragment.getTwitterWrapper();
}
public ParcelableStatus getStatus() {
return mStatus;
}

View File

@ -71,7 +71,6 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.astuetz.PagerSlidingTabStrip;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
@ -113,6 +112,7 @@ import org.mariotaku.twidere.view.CircularImageView;
import org.mariotaku.twidere.view.HeaderDrawerLayout;
import org.mariotaku.twidere.view.HeaderDrawerLayout.DrawerCallback;
import org.mariotaku.twidere.view.ProfileBannerImageView;
import org.mariotaku.twidere.view.TabPagerIndicator;
import org.mariotaku.twidere.view.TintedStatusFrameLayout;
import org.mariotaku.twidere.view.iface.IColorLabelView;
import org.mariotaku.twidere.view.iface.IExtendedView.OnSizeChangedListener;
@ -180,7 +180,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
private TintedStatusFrameLayout mTintedStatusContent;
private HeaderDrawerLayout mHeaderDrawerLayout;
private ViewPager mViewPager;
private PagerSlidingTabStrip mPagerIndicator;
private TabPagerIndicator mPagerIndicator;
private CardView mCardView;
private View mUuckyFooter;
private View mProfileBannerContainer;
@ -617,6 +617,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
mViewPager.setOffscreenPageLimit(3);
mViewPager.setAdapter(mPagerAdapter);
mPagerIndicator.setViewPager(mViewPager);
mPagerIndicator.setTabDisplayOption(TabPagerIndicator.LABEL);
mFollowButton.setOnClickListener(this);
mProfileImageView.setOnClickListener(this);
@ -853,7 +854,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
mURLContainer = headerView.findViewById(R.id.url_container);
mProfileBannerSpace = headerView.findViewById(R.id.profile_banner_space);
mViewPager = (ViewPager) contentView.findViewById(R.id.view_pager);
mPagerIndicator = (PagerSlidingTabStrip) contentView.findViewById(R.id.view_pager_tabs);
mPagerIndicator = (TabPagerIndicator) contentView.findViewById(R.id.view_pager_tabs);
mFollowButton = (Button) headerView.findViewById(R.id.follow);
mFollowProgress = (ProgressBar) headerView.findViewById(R.id.follow_progress);
mUuckyFooter = headerView.findViewById(R.id.uucky_footer);
@ -1143,7 +1144,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
}
mActionBarBackground.setColor(color);
mTintedStatusContent.setColor(color, ThemeUtils.getThemeAlpha(getActivity()));
mPagerIndicator.setIndicatorColor(color);
mPagerIndicator.setStripColor(color);
}
private void setupUserPages() {
@ -1161,12 +1162,11 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
tabArgs.putLong(EXTRA_USER_ID, args.getLong(EXTRA_USER_ID, -1));
tabArgs.putString(EXTRA_SCREEN_NAME, args.getString(EXTRA_SCREEN_NAME));
}
mPagerAdapter.addTab(UserTimelineFragment.class, tabArgs, getString(R.string.statuses), null, 0);
mPagerAdapter.addTab(UserTimelineFragment.class, tabArgs, getString(R.string.statuses), R.drawable.ic_action_quote, 0);
if (Utils.isOfficialKeyAccount(context, accountId)) {
mPagerAdapter.addTab(UserMediaTimelineFragment.class, tabArgs, getString(R.string.media), null, 1);
mPagerAdapter.addTab(UserMediaTimelineFragment.class, tabArgs, getString(R.string.media), R.drawable.ic_action_gallery, 1);
}
mPagerAdapter.addTab(UserFavoritesFragment.class, tabArgs, getString(R.string.favorites), null, 2);
mPagerIndicator.notifyDataSetChanged();
mPagerAdapter.addTab(UserFavoritesFragment.class, tabArgs, getString(R.string.favorites), R.drawable.ic_action_star, 2);
}
private boolean shouldUseNativeMenu() {

View File

@ -55,8 +55,6 @@ import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import com.astuetz.PagerSlidingTabStrip;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.support.UserListSelectorActivity;
import org.mariotaku.twidere.adapter.support.SupportTabsAdapter;
@ -74,6 +72,7 @@ import org.mariotaku.twidere.util.TwidereLinkify;
import org.mariotaku.twidere.view.ColorLabelRelativeLayout;
import org.mariotaku.twidere.view.HeaderDrawerLayout;
import org.mariotaku.twidere.view.HeaderDrawerLayout.DrawerCallback;
import org.mariotaku.twidere.view.TabPagerIndicator;
import twitter4j.Twitter;
import twitter4j.TwitterException;
@ -102,7 +101,7 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList
private Button mRetryButton;
private HeaderDrawerLayout mHeaderDrawerLayout;
private ViewPager mViewPager;
private PagerSlidingTabStrip mPagerIndicator;
private TabPagerIndicator mPagerIndicator;
private CardView mCardView;
private SupportTabsAdapter mPagerAdapter;
@ -484,7 +483,7 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList
mRetryButton = (Button) mErrorRetryContainer.findViewById(R.id.retry);
mErrorMessageView = (TextView) mErrorRetryContainer.findViewById(R.id.error_message);
mViewPager = (ViewPager) contentView.findViewById(R.id.view_pager);
mPagerIndicator = (PagerSlidingTabStrip) contentView.findViewById(R.id.view_pager_tabs);
mPagerIndicator = (TabPagerIndicator) contentView.findViewById(R.id.view_pager_tabs);
}
@Override

View File

@ -28,13 +28,12 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.astuetz.PagerSlidingTabStrip;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.support.SupportTabsAdapter;
import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback;
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface;
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback;
import org.mariotaku.twidere.view.TabPagerIndicator;
public class UserListsFragment extends BaseSupportFragment implements RefreshScrollTopInterface,
SupportFragmentCallback, SystemWindowsInsetsCallback {
@ -42,7 +41,7 @@ public class UserListsFragment extends BaseSupportFragment implements RefreshScr
private ViewPager mViewPager;
private SupportTabsAdapter mAdapter;
private PagerSlidingTabStrip mPagerIndicator;
private TabPagerIndicator mPagerIndicator;
private Fragment mCurrentVisibleFragment;
@ -90,7 +89,7 @@ public class UserListsFragment extends BaseSupportFragment implements RefreshScr
public void onViewCreated(final View view, final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewPager = (ViewPager) view.findViewById(R.id.view_pager);
mPagerIndicator = (PagerSlidingTabStrip) view.findViewById(R.id.view_pager_tabs);
mPagerIndicator = (TabPagerIndicator) view.findViewById(R.id.view_pager_tabs);
}
@Override

View File

@ -19,60 +19,61 @@
package org.mariotaku.twidere.model;
import static org.mariotaku.twidere.util.CompareUtils.bundleEquals;
import static org.mariotaku.twidere.util.CompareUtils.classEquals;
import static org.mariotaku.twidere.util.CompareUtils.objectEquals;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import org.mariotaku.twidere.TwidereConstants;
import static org.mariotaku.twidere.util.CompareUtils.bundleEquals;
import static org.mariotaku.twidere.util.CompareUtils.classEquals;
import static org.mariotaku.twidere.util.CompareUtils.objectEquals;
public class SupportTabSpec implements Comparable<SupportTabSpec>, TwidereConstants {
public final String name;
public final Object icon;
public final String type;
public final Class<? extends Fragment> cls;
public final Bundle args;
public final int position;
public final String name;
public final Object icon;
public final String type;
public final Class<? extends Fragment> cls;
public final Bundle args;
public final int position;
public SupportTabSpec(final String name, final Object icon, final Class<? extends Fragment> cls, final Bundle args,
final int position) {
this(name, icon, null, cls, args, position);
}
public SupportTabSpec(final String name, final Object icon, final Class<? extends Fragment> cls, final Bundle args,
final int position) {
this(name, icon, null, cls, args, position);
}
public SupportTabSpec(final String name, final Object icon, final String type, final Class<? extends Fragment> cls,
final Bundle args, final int position) {
if (cls == null) throw new IllegalArgumentException("Fragment cannot be null!");
if (name == null && icon == null)
throw new IllegalArgumentException("You must specify a name or icon for this tab!");
this.name = name;
this.icon = icon;
this.type = type;
this.cls = cls;
this.args = args;
this.position = position;
public SupportTabSpec(final String name, final Object icon, final String type, final Class<? extends Fragment> cls,
final Bundle args, final int position) {
if (cls == null) throw new IllegalArgumentException("Fragment cannot be null!");
if (name == null && icon == null)
throw new IllegalArgumentException("You must specify a name or icon for this tab!");
this.name = name;
this.icon = icon;
this.type = type;
this.cls = cls;
this.args = args;
this.position = position;
}
}
@Override
public int compareTo(final SupportTabSpec another) {
return position - another.position;
}
@Override
public int compareTo(@NonNull final SupportTabSpec another) {
return position - another.position;
}
@Override
public boolean equals(final Object o) {
if (!(o instanceof SupportTabSpec)) return false;
final SupportTabSpec spec = (SupportTabSpec) o;
return objectEquals(name, spec.name) && objectEquals(icon, spec.icon) && classEquals(cls, spec.cls)
&& bundleEquals(args, spec.args) && position == spec.position;
}
@Override
public boolean equals(final Object o) {
if (!(o instanceof SupportTabSpec)) return false;
final SupportTabSpec spec = (SupportTabSpec) o;
return objectEquals(name, spec.name) && objectEquals(icon, spec.icon) && classEquals(cls, spec.cls)
&& bundleEquals(args, spec.args) && position == spec.position;
}
@Override
public String toString() {
return "SupportTabSpec{name=" + name + ", icon=" + icon + ", type=" + type + ", cls=" + cls + ", args=" + args
+ ", position=" + position + "}";
}
@Override
public String toString() {
return "SupportTabSpec{name=" + name + ", icon=" + icon + ", type=" + type + ", cls=" + cls + ", args=" + args
+ ", position=" + position + "}";
}
}

View File

@ -19,11 +19,6 @@
package org.mariotaku.twidere.preference;
import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
import static org.mariotaku.twidere.util.Utils.getDefaultTextSize;
import static org.mariotaku.twidere.util.Utils.getLinkHighlightOptionInt;
import static org.mariotaku.twidere.util.Utils.getSampleDisplayName;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
@ -38,106 +33,108 @@ import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
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.StatusListViewHolder;
import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
import static org.mariotaku.twidere.util.Utils.getDefaultTextSize;
import static org.mariotaku.twidere.util.Utils.getLinkHighlightOptionInt;
import static org.mariotaku.twidere.util.Utils.getSampleDisplayName;
public class CardPreviewPreference extends Preference implements Constants, OnSharedPreferenceChangeListener {
private final LayoutInflater mInflater;
private final SharedPreferences mPreferences;
private final TwidereLinkify mLinkify;
private StatusListViewHolder mHolder;
private boolean mCompactModeChanged;
private final LayoutInflater mInflater;
private final SharedPreferences mPreferences;
private final TwidereLinkify mLinkify;
private StatusListViewHolder mHolder;
private boolean mCompactModeChanged;
public CardPreviewPreference(final Context context) {
this(context, null);
}
public CardPreviewPreference(final Context context) {
this(context, null);
}
public CardPreviewPreference(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public CardPreviewPreference(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public CardPreviewPreference(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
mInflater = LayoutInflater.from(context);
mLinkify = new TwidereLinkify(null);
mLinkify.setLinkTextColor(ThemeUtils.getUserLinkTextColor(context));
mPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
mPreferences.registerOnSharedPreferenceChangeListener(this);
}
public CardPreviewPreference(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
mInflater = LayoutInflater.from(context);
mLinkify = new TwidereLinkify(null);
mLinkify.setLinkTextColor(ThemeUtils.getUserLinkTextColor(context));
mPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
mPreferences.registerOnSharedPreferenceChangeListener(this);
}
@Override
public View getView(final View convertView, final ViewGroup parent) {
if (mCompactModeChanged) return super.getView(null, parent);
return super.getView(convertView, parent);
}
@Override
public View getView(final View convertView, final ViewGroup parent) {
if (mCompactModeChanged) return super.getView(null, parent);
return super.getView(convertView, parent);
}
@Override
public void onSharedPreferenceChanged(final SharedPreferences preferences, final String key) {
if (mHolder == null) return;
if (KEY_COMPACT_CARDS.equals(key)) {
mCompactModeChanged = true;
}
notifyChanged();
}
@Override
public void onSharedPreferenceChanged(final SharedPreferences preferences, final String key) {
if (mHolder == null) return;
if (KEY_COMPACT_CARDS.equals(key)) {
mCompactModeChanged = true;
}
notifyChanged();
}
@Override
protected void onBindView(final View view) {
if (mPreferences == null) return;
mCompactModeChanged = false;
final Context context = getContext();
final int highlightOption = getLinkHighlightOptionInt(context);
final boolean nameFirst = mPreferences.getBoolean(KEY_NAME_FIRST, true);
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 StatusListViewHolder(view);
mLinkify.setHighlightOption(highlightOption);
mHolder.setDisplayNameFirst(nameFirst);
mHolder.setNicknameOnly(nickname_only);
mHolder.setShowAsGap(false);
mHolder.setIsMyStatus(false);
mHolder.setTextSize(mPreferences.getInt(KEY_TEXT_SIZE, getDefaultTextSize(context)));
mHolder.image_preview_container.setVisibility(display_image_preview ? View.VISIBLE : View.GONE);
mHolder.profile_image.setVisibility(display_profile_image ? View.VISIBLE : View.GONE);
mHolder.image_preview_progress.setVisibility(View.GONE);
@Override
protected void onBindView(final View view) {
if (mPreferences == null) return;
mCompactModeChanged = false;
final Context context = getContext();
final int highlightOption = getLinkHighlightOptionInt(context);
final boolean nameFirst = mPreferences.getBoolean(KEY_NAME_FIRST, true);
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 StatusListViewHolder(view);
mLinkify.setHighlightOption(highlightOption);
mHolder.setDisplayNameFirst(nameFirst);
mHolder.setNicknameOnly(nickname_only);
mHolder.setShowAsGap(false);
mHolder.setIsMyStatus(false);
mHolder.setTextSize(mPreferences.getInt(KEY_TEXT_SIZE, getDefaultTextSize(context)));
mHolder.image_preview_container.setVisibility(display_image_preview ? View.VISIBLE : View.GONE);
mHolder.profile_image.setVisibility(display_profile_image ? View.VISIBLE : View.GONE);
mHolder.image_preview_progress.setVisibility(View.GONE);
if (mHolder.profile_image instanceof ForegroundImageView) {
((ForegroundImageView) mHolder.profile_image).setForeground(null);
}
if (mHolder.image_preview instanceof ForegroundImageView) {
((ForegroundImageView) mHolder.image_preview).setForeground(null);
}
if (mHolder.content instanceof CardItemLinearLayout) {
((CardItemLinearLayout) mHolder.content).setItemSelector(null);
}
mHolder.profile_image.setImageResource(R.drawable.ic_launcher);
mHolder.image_preview.setImageResource(R.drawable.twidere_feature_graphic);
mHolder.name.setText(nickname_only ? TWIDERE_PREVIEW_NICKNAME : context.getString(R.string.name_with_nickname,
TWIDERE_PREVIEW_NAME, TWIDERE_PREVIEW_NICKNAME));
mHolder.screen_name.setText("@" + TWIDERE_PREVIEW_SCREEN_NAME);
if (highlightOption != VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) {
mHolder.text.setText(Html.fromHtml(TWIDERE_PREVIEW_TEXT_HTML));
mLinkify.applyAllLinks(mHolder.text, 0, false);
mLinkify.applyUserProfileLinkNoHighlight(mHolder.name, 0, 0, TWIDERE_PREVIEW_SCREEN_NAME);
mLinkify.applyUserProfileLinkNoHighlight(mHolder.screen_name, 0, 0, TWIDERE_PREVIEW_SCREEN_NAME);
} else {
mHolder.text.setText(toPlainText(TWIDERE_PREVIEW_TEXT_HTML));
}
final String display_name = getSampleDisplayName(context, nameFirst, nickname_only);
mHolder.reply_retweet_status.setText(context.getString(R.string.retweeted_by_name, display_name));
mHolder.reply_retweet_status.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_indicator_retweet, 0, 0, 0);
mHolder.time.setTime(System.currentTimeMillis() - 360000);
mHolder.time.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_indicator_media, 0);
super.onBindView(view);
}
if (mHolder.profile_image instanceof ForegroundImageView) {
((ForegroundImageView) mHolder.profile_image).setForeground(null);
}
if (mHolder.image_preview instanceof ForegroundImageView) {
((ForegroundImageView) mHolder.image_preview).setForeground(null);
}
mHolder.content.setItemSelector(null);
mHolder.profile_image.setImageResource(R.drawable.ic_launcher);
mHolder.image_preview.setImageResource(R.drawable.twidere_feature_graphic);
mHolder.name.setText(nickname_only ? TWIDERE_PREVIEW_NICKNAME : context.getString(R.string.name_with_nickname,
TWIDERE_PREVIEW_NAME, TWIDERE_PREVIEW_NICKNAME));
mHolder.screen_name.setText("@" + TWIDERE_PREVIEW_SCREEN_NAME);
if (highlightOption != VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) {
mHolder.text.setText(Html.fromHtml(TWIDERE_PREVIEW_TEXT_HTML));
mLinkify.applyAllLinks(mHolder.text, 0, false);
mLinkify.applyUserProfileLinkNoHighlight(mHolder.name, 0, 0, TWIDERE_PREVIEW_SCREEN_NAME);
mLinkify.applyUserProfileLinkNoHighlight(mHolder.screen_name, 0, 0, TWIDERE_PREVIEW_SCREEN_NAME);
} else {
mHolder.text.setText(toPlainText(TWIDERE_PREVIEW_TEXT_HTML));
}
final String display_name = getSampleDisplayName(context, nameFirst, nickname_only);
mHolder.reply_retweet_status.setText(context.getString(R.string.retweeted_by_name, display_name));
mHolder.reply_retweet_status.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_indicator_retweet, 0, 0, 0);
mHolder.time.setTime(System.currentTimeMillis() - 360000);
mHolder.time.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_indicator_media, 0);
super.onBindView(view);
}
@Override
protected View onCreateView(final ViewGroup parent) {
if (mPreferences != null && mPreferences.getBoolean(KEY_COMPACT_CARDS, false))
return mInflater.inflate(R.layout.list_item_status_compact_deprecated, parent, false);
return mInflater.inflate(R.layout.list_item_status_deprecated, parent, false);
}
@Override
protected View onCreateView(final ViewGroup parent) {
if (mPreferences != null && mPreferences.getBoolean(KEY_COMPACT_CARDS, false))
return mInflater.inflate(R.layout.list_item_status_compact_deprecated, parent, false);
return mInflater.inflate(R.layout.list_item_status_deprecated, parent, false);
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.service;
import android.annotation.TargetApi;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.os.Build;
/**
* Created by mariotaku on 14/12/12.
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class BackgroundJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}

View File

@ -59,11 +59,13 @@ import org.mariotaku.twidere.service.BackgroundOperationService;
import org.mariotaku.twidere.task.CacheUsersStatusesTask;
import org.mariotaku.twidere.task.ManagedAsyncTask;
import org.mariotaku.twidere.task.TwidereAsyncTask;
import org.mariotaku.twidere.util.collection.LongSparseMap;
import org.mariotaku.twidere.util.message.FavoriteCreatedEvent;
import org.mariotaku.twidere.util.message.FavoriteDestroyedEvent;
import org.mariotaku.twidere.util.message.FriendshipUpdatedEvent;
import org.mariotaku.twidere.util.message.ProfileUpdatedEvent;
import org.mariotaku.twidere.util.message.StatusDestroyedEvent;
import org.mariotaku.twidere.util.message.StatusListChangedEvent;
import org.mariotaku.twidere.util.message.StatusRetweetedEvent;
import java.io.FileNotFoundException;
@ -116,6 +118,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
private int mGetReceivedDirectMessagesTaskId, mGetSentDirectMessagesTaskId;
private int mGetLocalTrendsTaskId;
private LongSparseMap<Long> mCreatingFavoriteIds = new LongSparseMap<>();
private LongSparseMap<Long> mDestroyingFavoriteIds = new LongSparseMap<>();
public AsyncTwitterWrapper(final Context context) {
mContext = context;
final TwidereApplication app = TwidereApplication.getInstance(context);
@ -135,6 +140,15 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
return mAsyncTaskManager.add(task, true);
}
public boolean isCreatingFavorite(final long accountId, final long statusId) {
return mCreatingFavoriteIds.has(accountId, statusId);
}
public boolean isDestroyingFavorite(final long accountId, final long statusId) {
return mDestroyingFavoriteIds.has(accountId, statusId);
}
public void clearNotificationAsync(final int notificationType) {
clearNotificationAsync(notificationType, 0);
}
@ -849,8 +863,17 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
}
}
@Override
protected void onPreExecute() {
super.onPreExecute();
mCreatingFavoriteIds.put(account_id, status_id);
final Bus bus = TwidereApplication.getInstance(mContext).getMessageBus();
bus.post(new StatusListChangedEvent());
}
@Override
protected void onPostExecute(final SingleResponse<ParcelableStatus> result) {
mCreatingFavoriteIds.remove(account_id, status_id);
if (result.hasData()) {
final Bus bus = TwidereApplication.getInstance(mContext).getMessageBus();
bus.post(new FavoriteCreatedEvent(result.getData()));
@ -1367,8 +1390,17 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
return SingleResponse.getInstance();
}
@Override
protected void onPreExecute() {
super.onPreExecute();
mDestroyingFavoriteIds.put(account_id, status_id);
final Bus bus = TwidereApplication.getInstance(mContext).getMessageBus();
bus.post(new StatusListChangedEvent());
}
@Override
protected void onPostExecute(final SingleResponse<ParcelableStatus> result) {
mDestroyingFavoriteIds.remove(account_id, status_id);
if (result.hasData()) {
final Bus bus = TwidereApplication.getInstance(mContext).getMessageBus();
bus.post(new FavoriteDestroyedEvent(result.getData()));

View File

@ -0,0 +1,66 @@
/*
* 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.util.collection;
import android.support.v4.util.LongSparseArray;
import java.util.HashSet;
/**
* Created by mariotaku on 14/12/12.
*/
public class LongSparseMap<T> {
private final LongSparseArray<HashSet<T>> internalArray;
public LongSparseMap() {
internalArray = new LongSparseArray<>();
}
public boolean put(long key, T value) {
final int idx = internalArray.indexOfKey(key);
final HashSet<T> set;
if (idx < 0) {
set = new HashSet<>();
internalArray.put(key, set);
} else {
set = internalArray.valueAt(idx);
}
return set.add(value);
}
public boolean clear(long key) {
final int idx = internalArray.indexOfKey(key);
if (idx < 0) return false;
internalArray.valueAt(idx).clear();
return true;
}
public boolean remove(long key, T value) {
final int idx = internalArray.indexOfKey(key);
return idx >= 0 && internalArray.valueAt(idx).remove(value);
}
public boolean has(long key, T value) {
final int idx = internalArray.indexOfKey(key);
return idx >= 0 && internalArray.valueAt(idx).contains(value);
}
}

View File

@ -0,0 +1,26 @@
/*
* 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.util.message;
/**
* Created by mariotaku on 14/12/12.
*/
public class StatusListChangedEvent {
}

View File

@ -1,7 +1,12 @@
package org.mariotaku.twidere.view;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.support.annotation.IntDef;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPager;
@ -14,39 +19,56 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.view.iface.PagerIndicator;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Created by mariotaku on 14/10/21.
*/
public class TabPagerIndicator extends RecyclerView implements PagerIndicator {
public static final int ICON = 0x1;
public static final int LABEL = 0x2;
public static final int BOTH = ICON | LABEL;
private final int mStripHeight;
private ViewPager mViewPager;
private final TabPagerIndicatorAdapter mIndicatorAdapter;
private ViewPager mViewPager;
private PagerAdapter mPagerProvider;
private OnPageChangeListener mPageChangeListener;
private int mOption;
private boolean mTabExpandEnabled;
private int mHorizontalPadding, mVerticalPadding;
public TabPagerIndicator(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final Resources res = getResources();
mIndicatorAdapter = new TabPagerIndicatorAdapter(this);
mStripHeight = getResources().getDimensionPixelSize(R.dimen.element_spacing_small);
ViewCompat.setOverScrollMode(this, ViewCompat.OVER_SCROLL_NEVER);
setHorizontalScrollBarEnabled(false);
setVerticalScrollBarEnabled(false);
setLayoutManager(new TabLayoutManager(this));
mIndicatorAdapter = new TabPagerIndicatorAdapter(this);
setItemContext(context);
setAdapter(mIndicatorAdapter);
mStripHeight = getResources().getDimensionPixelSize(R.dimen.element_spacing_small);
setTabDisplayOption(ICON);
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabPagerIndicator);
setTabExpandEnabled(a.getBoolean(R.styleable.TabPagerIndicator_tabExpandEnabled, false));
setHorizontalPadding(a.getDimensionPixelSize(R.styleable.TabPagerIndicator_tabHorizontalPadding, 0));
setVerticalPadding(a.getDimensionPixelSize(R.styleable.TabPagerIndicator_tabHorizontalPadding, 0));
setStripColor(a.getColor(R.styleable.TabPagerIndicator_tabStripColor, 0));
setIconColor(a.getColor(R.styleable.TabPagerIndicator_tabIconColor, 0));
setTabDisplayOption(a.getInt(R.styleable.TabPagerIndicator_tabDisplayOption, ICON));
a.recycle();
}
public void setStripColor(int color) {
mIndicatorAdapter.setStripColor(color);
}
public TabPagerIndicator(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@ -56,11 +78,91 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator {
this(context, null);
}
public int getCount() {
return mIndicatorAdapter.getItemCount();
}
public Context getItemContext() {
return mIndicatorAdapter.getItemContext();
}
public void setItemContext(Context context) {
mIndicatorAdapter.setItemContext(context);
}
public int getStripHeight() {
return mStripHeight;
}
@Override
public void notifyDataSetChanged() {
mIndicatorAdapter.notifyDataSetChanged();
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mPageChangeListener == null) return;
mPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
@Override
public void onPageSelected(int position) {
mIndicatorAdapter.notifyDataSetChanged();
if (mPageChangeListener == null) return;
mPageChangeListener.onPageSelected(position);
}
@Override
public void onPageScrollStateChanged(int state) {
if (mPageChangeListener == null) return;
mPageChangeListener.onPageScrollStateChanged(state);
}
public void setBadge(int position, int count) {
mIndicatorAdapter.setBadge(position, count);
}
public void setDisplayBadge(boolean display) {
mIndicatorAdapter.setDisplayBadge(display);
}
public void setIconColor(int color) {
mIndicatorAdapter.setIconColor(color);
}
public void setStripColor(int color) {
mIndicatorAdapter.setStripColor(color);
}
@DisplayOption
public void setTabDisplayOption(int flags) {
mOption = flags;
notifyDataSetChanged();
}
private void dispatchTabClick(int position) {
final int currentItem = getCurrentItem();
setCurrentItem(position);
if (mPagerProvider instanceof TabListener) {
if (currentItem != position) {
((TabListener) mPagerProvider).onPageSelected(position);
} else {
((TabListener) mPagerProvider).onPageReselected(position);
}
}
}
private boolean dispatchTabLongClick(int position) {
if (mPagerProvider instanceof TabListener) {
return ((TabListener) mPagerProvider).onTabLongClick(position);
}
return false;
}
private int getCurrentItem() {
return mViewPager.getCurrentItem();
}
@Override
public void setCurrentItem(int item) {
mViewPager.setCurrentItem(item);
@ -88,62 +190,46 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator {
mIndicatorAdapter.setTabProvider((TabProvider) adapter);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mPageChangeListener == null) return;
mPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
private int getTabHorizontalPadding() {
return mHorizontalPadding;
}
@Override
public void onPageSelected(int position) {
mIndicatorAdapter.notifyDataSetChanged();
if (mPageChangeListener == null) return;
mPageChangeListener.onPageSelected(position);
private int getTabVerticalPadding() {
return mVerticalPadding;
}
@Override
public void onPageScrollStateChanged(int state) {
if (mPageChangeListener == null) return;
mPageChangeListener.onPageScrollStateChanged(state);
private boolean isIconDisplayed() {
return (mOption & ICON) != 0;
}
public int getCount() {
return mIndicatorAdapter.getItemCount();
private boolean isLabelDisplayed() {
return (mOption & LABEL) != 0;
}
public void setBadge(int position, int count) {
mIndicatorAdapter.setBadge(position, count);
private boolean isTabExpandEnabled() {
return mTabExpandEnabled;
}
public void setDisplayLabel(boolean display) {
public void setTabExpandEnabled(boolean expandEnabled) {
mTabExpandEnabled = expandEnabled;
}
public void setDisplayIcon(boolean display) {
private void setHorizontalPadding(int padding) {
mHorizontalPadding = padding;
notifyDataSetChanged();
}
public int getStripHeight() {
return mStripHeight;
private void setVerticalPadding(int padding) {
mVerticalPadding = padding;
notifyDataSetChanged();
}
public void setIconColor(int color) {
mIndicatorAdapter.setIconColor(color);
@IntDef({ICON, LABEL, BOTH})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayOption {
}
public Context getItemContext() {
return mIndicatorAdapter.getItemContext();
}
public void setItemContext(Context context) {
mIndicatorAdapter.setItemContext(context);
}
public void setDisplayBadge(boolean display) {
mIndicatorAdapter.setDisplayBadge(display);
}
private static class TabPagerIndicatorAdapter extends Adapter<TabItemHolder> implements OnClickListener, OnLongClickListener {
private static class TabPagerIndicatorAdapter extends Adapter<TabItemHolder> {
private final TabPagerIndicator mIndicator;
private final SparseIntArray mUnreadCounts;
@ -159,61 +245,8 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator {
mUnreadCounts = new SparseIntArray();
}
@Override
public TabItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = mInflater.inflate(R.layout.tab_item_home, parent, false);
view.setOnClickListener(this);
view.setOnLongClickListener(this);
final View selectedIndicator = view.findViewById(R.id.selected_indicator);
final ViewGroup.LayoutParams lp = selectedIndicator.getLayoutParams();
lp.height = mIndicator.getStripHeight();
selectedIndicator.setLayoutParams(lp);
return new TabItemHolder(view);
}
@Override
public void onBindViewHolder(TabItemHolder holder, int position) {
final Drawable icon = mTabProvider.getPageIcon(position);
final CharSequence title = mTabProvider.getPageTitle(position);
holder.setTabData(position, icon, title, mIndicator.getCurrentItem() == position);
holder.setStripColor(mStripColor);
holder.setIconColor(mIconColor);
holder.setBadge(mUnreadCounts.get(position, 0), mDisplayBadge);
}
@Override
public int getItemCount() {
if (mTabProvider == null) return 0;
return mTabProvider.getCount();
}
public void setTabProvider(TabProvider tabProvider) {
mTabProvider = tabProvider;
notifyDataSetChanged();
}
@Override
public void onClick(View v) {
final Object tag = v.getTag();
if (!(tag instanceof Integer)) return;
mIndicator.dispatchTabClick((Integer) tag);
}
@Override
public boolean onLongClick(View v) {
final Object tag = v.getTag();
if (!(tag instanceof Integer)) return false;
return mIndicator.dispatchTabLongClick((Integer) tag);
}
public void setStripColor(int color) {
mStripColor = color;
notifyDataSetChanged();
}
public void setIconColor(int color) {
mIconColor = color;
notifyDataSetChanged();
public Context getItemContext() {
return mItemContext;
}
public void setItemContext(Context itemContext) {
@ -221,8 +254,30 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator {
mInflater = LayoutInflater.from(itemContext);
}
public Context getItemContext() {
return mItemContext;
@Override
public TabItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (mIndicator == null) throw new IllegalStateException("item context not set");
final View view = mInflater.inflate(R.layout.layout_tab_item, parent, false);
return new TabItemHolder(mIndicator, view);
}
@Override
public void onBindViewHolder(TabItemHolder holder, int position) {
final Drawable icon = mTabProvider.getPageIcon(position);
final CharSequence title = mTabProvider.getPageTitle(position);
holder.setTabData(icon, title, mIndicator.getCurrentItem() == position);
holder.setPadding(mIndicator.getTabHorizontalPadding(), mIndicator.getTabVerticalPadding());
holder.setStripHeight(mIndicator.getStripHeight());
holder.setStripColor(mStripColor);
holder.setIconColor(mIconColor);
holder.setBadge(mUnreadCounts.get(position, 0), mDisplayBadge);
holder.setDisplayOption(mIndicator.isIconDisplayed(), mIndicator.isLabelDisplayed());
}
@Override
public int getItemCount() {
if (mTabProvider == null) return 0;
return mTabProvider.getCount();
}
public void setBadge(int position, int count) {
@ -234,67 +289,88 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator {
mDisplayBadge = display;
notifyDataSetChanged();
}
}
private void dispatchTabClick(int position) {
final int currentItem = getCurrentItem();
setCurrentItem(position);
if (mPagerProvider instanceof TabListener) {
if (currentItem != position) {
((TabListener) mPagerProvider).onPageSelected(position);
} else {
((TabListener) mPagerProvider).onPageReselected(position);
}
}
}
private boolean dispatchTabLongClick(int position) {
if (mPagerProvider instanceof TabListener) {
return ((TabListener) mPagerProvider).onTabLongClick(position);
}
return false;
}
private int getCurrentItem() {
return mViewPager.getCurrentItem();
}
private static class TabItemHolder extends ViewHolder {
private final View itemView;
private final ImageView iconView;
private final View selectedIndicator;
private final BadgeView badgeView;
public TabItemHolder(View itemView) {
super(itemView);
this.itemView = itemView;
selectedIndicator = itemView.findViewById(R.id.selected_indicator);
iconView = (ImageView) itemView.findViewById(R.id.tab_icon);
badgeView = (BadgeView) itemView.findViewById(R.id.unread_indicator);
}
public void setTabData(int position, Drawable icon, CharSequence title, boolean activated) {
itemView.setTag(position);
itemView.setContentDescription(title);
iconView.setImageDrawable(icon);
iconView.setContentDescription(title);
selectedIndicator.setVisibility(activated ? VISIBLE : INVISIBLE);
public void setIconColor(int color) {
mIconColor = color;
notifyDataSetChanged();
}
public void setStripColor(int color) {
selectedIndicator.setBackgroundColor(color);
mStripColor = color;
notifyDataSetChanged();
}
public void setIconColor(int color) {
iconView.setColorFilter(color);
public void setTabProvider(TabProvider tabProvider) {
mTabProvider = tabProvider;
notifyDataSetChanged();
}
}
private static class TabItemHolder extends ViewHolder implements OnClickListener, OnLongClickListener {
private final TabPagerIndicator indicator;
private final ItemLayout itemView;
private final ImageView iconView;
private final TextView labelView;
private final BadgeView badgeView;
public TabItemHolder(TabPagerIndicator indicator, View itemView) {
super(itemView);
this.indicator = indicator;
this.itemView = (ItemLayout) itemView;
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
iconView = (ImageView) itemView.findViewById(R.id.tab_icon);
labelView = (TextView) itemView.findViewById(R.id.tab_label);
badgeView = (BadgeView) itemView.findViewById(R.id.unread_indicator);
}
@Override
public void onClick(View v) {
indicator.dispatchTabClick(getPosition());
}
@Override
public boolean onLongClick(View v) {
return indicator.dispatchTabLongClick(getPosition());
}
public void setBadge(int count, boolean display) {
badgeView.setText(String.valueOf(count));
badgeView.setVisibility(display && count > 0 ? VISIBLE : GONE);
}
public void setDisplayOption(boolean iconDisplayed, boolean labelDisplayed) {
iconView.setVisibility(iconDisplayed ? VISIBLE : GONE);
labelView.setVisibility(labelDisplayed ? VISIBLE : GONE);
}
public void setIconColor(int color) {
if (color != 0) {
iconView.setColorFilter(color);
} else {
iconView.clearColorFilter();
}
}
public void setPadding(int horizontalPadding, int verticalPadding) {
itemView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
}
public void setStripColor(int color) {
itemView.setStripColor(color);
}
public void setStripHeight(int stripHeight) {
itemView.setStripHeight(stripHeight);
}
public void setTabData(Drawable icon, CharSequence title, boolean activated) {
itemView.setContentDescription(title);
iconView.setImageDrawable(icon);
labelView.setText(title);
itemView.setIsCurrent(activated);
}
}
public static class TabLayoutManager extends LinearLayoutManager {
@ -308,13 +384,59 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator {
@Override
public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
// first get default measured size
super.measureChildWithMargins(child, widthUsed, heightUsed);
if (!mIndicator.isTabExpandEnabled()) return;
final int count = mIndicator.getCount();
if (count == 0) return;
final int parentHeight = mIndicator.getHeight(), parentWidth = mIndicator.getWidth();
final int width = Math.max(parentWidth / count, parentHeight);
final int width = Math.max(parentWidth / count, child.getMeasuredWidth());
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.EXACTLY);
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
child.measure(widthMeasureSpec, heightMeasureSpec);
}
}
public static final class ItemLayout extends RelativeLayout {
private final Paint mStripPaint;
private boolean mIsCurrent;
private int mStripColor;
private int mStripHeight;
public ItemLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mStripPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
setWillNotDraw(false);
}
public void setIsCurrent(boolean isCurrent) {
if (mIsCurrent == isCurrent) return;
mIsCurrent = isCurrent;
invalidate();
}
public void setStripColor(int stripColor) {
if (mStripColor == stripColor) return;
mStripColor = stripColor;
mStripPaint.setColor(stripColor);
invalidate();
}
public void setStripHeight(int stripHeight) {
if (mStripHeight == stripHeight) return;
mStripHeight = stripHeight;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
if (mIsCurrent) {
final int width = canvas.getWidth(), height = canvas.getHeight();
canvas.drawRect(0, height - mStripHeight, width, height, mStripPaint);
}
super.onDraw(canvas);
}
}
}

View File

@ -14,6 +14,7 @@ import org.mariotaku.twidere.adapter.iface.IStatusesAdapter;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.ParcelableStatus.CursorIndices;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.ImageLoadingHandler;
import org.mariotaku.twidere.util.UserColorNicknameUtils;
@ -84,11 +85,12 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick
public void displayStatus(final ParcelableStatus status) {
displayStatus(adapter.getContext(), adapter.getImageLoader(),
adapter.getImageLoadingHandler(), status);
adapter.getImageLoadingHandler(), adapter.getTwitterWrapper(), status);
}
public void displayStatus(final Context context, final ImageLoaderWrapper loader,
final ImageLoadingHandler handler, final ParcelableStatus status) {
final ImageLoadingHandler handler, final AsyncTwitterWrapper twitter,
final ParcelableStatus status) {
final ParcelableMedia[] media = status.media;
if (status.retweet_id > 0) {
@ -163,12 +165,18 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick
retweetCountView.setEnabled(!status.user_is_protected);
retweetCountView.setActivated(Utils.isMyRetweet(status));
favoriteCountView.setActivated(status.is_favorite);
if (twitter.isDestroyingFavorite(status.account_id, status.id)) {
favoriteCountView.setActivated(false);
} else {
favoriteCountView.setActivated(twitter.isCreatingFavorite(status.account_id, status.id)
|| status.is_favorite);
}
}
public void displayStatus(Cursor cursor, CursorIndices indices) {
final ImageLoaderWrapper loader = adapter.getImageLoader();
final AsyncTwitterWrapper twitter = adapter.getTwitterWrapper();
final Context context = adapter.getContext();
final int reply_count = cursor.getInt(indices.reply_count);
@ -178,6 +186,7 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick
final long account_id = cursor.getLong(indices.account_id);
final long timestamp = cursor.getLong(indices.status_timestamp);
final long user_id = cursor.getLong(indices.user_id);
final long status_id = cursor.getLong(indices.status_id);
final long retweet_id = cursor.getLong(indices.retweet_id);
final long my_retweet_id = cursor.getLong(indices.my_retweet_id);
final long retweeted_by_id = cursor.getLong(indices.retweeted_by_user_id);
@ -269,6 +278,12 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick
retweetCountView.setEnabled(!user_is_protected);
retweetCountView.setActivated(Utils.isMyRetweet(account_id, retweeted_by_id, my_retweet_id));
favoriteCountView.setActivated(cursor.getInt(indices.is_favorite) == 1);
if (twitter.isDestroyingFavorite(account_id, status_id)) {
favoriteCountView.setActivated(false);
} else {
favoriteCountView.setActivated(twitter.isCreatingFavorite(account_id, status_id)
|| cursor.getInt(indices.is_favorite) == 1);
}
}
public CardView getCardView() {

View File

@ -42,387 +42,387 @@ import org.mariotaku.twidere.util.ThemeUtils;
public interface ICardItemView extends IColorLabelView {
public View getFakeOverflowButton();
public boolean isGap();
public void setActivatedIndicator(Drawable activatedIndicator);
public void setIsGap(boolean isGap);
public void setItemBackground(Drawable itemBackground);
public void setItemSelector(Drawable itemSelector);
public void setOnOverflowIconClickListener(final OnOverflowIconClickListener listener);
public void setOverflowIcon(Drawable overflowIcon);
public static final class DrawingHelper {
private final View mView;
private final int mCardGapHeight;
private final String mCardGapText;
private final Paint mGapTextPaint;
private final Rect mGapTextBounds = new Rect();
private final Rect mBackgroundPadding = new Rect();
private final Rect mOverflowIconBounds = new Rect();
private Drawable mBackground;
private Drawable mItemSelector;
private Drawable mActivatedIndicator;
private Drawable mOverflowIcon;
private Drawable mPaddedOverflowIcon;
private boolean mIsGap;
private final int mThemeColor;
private final GestureDetector mOverflowIconGestureDetector;
private OnOverflowIconClickListener mOnOverflowIconClickListener;
private final FakeOverflowButton mFakeOverflowButton;
private float mBackgroundAlpha;
public DrawingHelper(final View view, final Context context, final AttributeSet attrs, final int defStyleAttr) {
mView = view;
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CardItemView, defStyleAttr,
R.style.Widget_CardItemView);
mGapTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCardGapHeight = a.getDimensionPixelSize(R.styleable.CardItemView_cardGapHeight, 0);
mCardGapText = a.getString(R.styleable.CardItemView_cardGapText);
mGapTextPaint.setColor(a.getColor(R.styleable.CardItemView_cardGapTextColor, Color.GRAY));
mGapTextPaint.setTextSize(a.getDimensionPixelSize(R.styleable.CardItemView_cardGapTextSize, 18));
mGapTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mThemeColor = ThemeUtils.getUserAccentColor(context);
mOverflowIconGestureDetector = new GestureDetector(context, new OverflowIconGestureListener(this));
mFakeOverflowButton = new FakeOverflowButton(this);
mBackgroundAlpha = a.getFraction(R.styleable.CardItemView_cardBackgroundAlpha, 1, 1, 1.0f);
setItemBackground(a.getDrawable(R.styleable.CardItemView_cardBackground));
setItemSelector(a.getDrawable(R.styleable.CardItemView_cardSelector));
setActivatedIndicator(a.getDrawable(R.styleable.CardItemView_cardActivatedIndicator));
setOverflowIcon(a.getDrawable(R.styleable.CardItemView_cardOverflowIcon));
a.recycle();
}
public void dispatchDrawableStateChanged() {
final int[] state = mView.getDrawableState();
if (mBackground != null) {
mBackground.setState(state);
}
if (mItemSelector != null) {
mItemSelector.setState(state);
}
if (mActivatedIndicator != null) {
mActivatedIndicator.setState(state);
}
}
public void dispatchOnSizeChanged(final int w, final int h, final int oldw, final int oldh) {
final int paddingLeft = mView.getPaddingLeft();
final int paddingTop = mView.getPaddingTop();
final int paddingRight = mView.getPaddingRight();
final int paddingBottom = mView.getPaddingBottom();
final int l = paddingLeft, t = paddingTop, r = w - paddingRight, b = h - paddingBottom;
if (mBackground != null) {
if (mBackground instanceof NinePatchDrawable) {
final NinePatchDrawable npd = (NinePatchDrawable) mBackground;
npd.getPadding(mBackgroundPadding);
npd.setBounds(l - mBackgroundPadding.left, t - mBackgroundPadding.top,
r + mBackgroundPadding.right, b + mBackgroundPadding.bottom);
} else {
mBackground.setBounds(l, t, r, b);
}
}
if (mItemSelector != null) {
if (mIsGap) {
mItemSelector.setBounds(0, 0, w, h);
} else {
mItemSelector.setBounds(l, t, r, b);
}
}
if (mActivatedIndicator != null) {
mActivatedIndicator.setBounds(l, t, r, b);
}
if (mOverflowIcon != null) {
mPaddedOverflowIcon = new PaddingDrawable(mOverflowIcon, paddingTop, 0, paddingRight, 0);
final int iw = mPaddedOverflowIcon.getIntrinsicWidth();
final int ih = mPaddedOverflowIcon.getIntrinsicHeight();
mOverflowIconBounds.set(w - iw, 0, w, ih);
mPaddedOverflowIcon.setBounds(mOverflowIconBounds);
} else {
mPaddedOverflowIcon = null;
}
}
public void drawBackground(final Canvas canvas) {
if (mBackground != null && !mIsGap) {
mBackground.draw(canvas);
}
}
public void drawGap(final Canvas canvas) {
if (mIsGap) {
final int centerX = canvas.getWidth() / 2, centerY = canvas.getHeight() / 2;
if (mCardGapText != null) {
mGapTextPaint.getTextBounds(mCardGapText, 0, mCardGapText.length(), mGapTextBounds);
final float xPos = centerX - mGapTextBounds.width() / 2;
final float yPos = centerY - (mGapTextPaint.descent() + mGapTextPaint.ascent()) / 2;
canvas.drawText(mCardGapText, xPos, yPos, mGapTextPaint);
}
}
}
public void drawOverflowIcon(final Canvas canvas) {
if (mPaddedOverflowIcon != null && mOnOverflowIconClickListener != null) {
mPaddedOverflowIcon.draw(canvas);
}
}
public void drawSelector(final Canvas canvas) {
if (mActivatedIndicator != null) {
mActivatedIndicator.draw(canvas);
}
if (mItemSelector != null) {
mItemSelector.draw(canvas);
}
}
public int getCardGapHeight() {
return mCardGapHeight;
}
public View getFakeOverflowButton() {
return mFakeOverflowButton;
}
public boolean handleOverflowTouchEvent(final MotionEvent ev) {
return mOverflowIconGestureDetector.onTouchEvent(ev);
}
public boolean isGap() {
return mIsGap;
}
public boolean isOverflowIconClicked(final MotionEvent ev) {
if (mOverflowIcon == null || mOnOverflowIconClickListener == null) return false;
final int x = Math.round(ev.getX()), y = Math.round(ev.getY());
if (mOverflowIconBounds.contains(x, y)) return true;
return false;
}
public void setActivatedIndicator(final Drawable activatedIndicator) {
preSetDrawable(mActivatedIndicator);
mActivatedIndicator = activatedIndicator;
if (activatedIndicator != null) {
activatedIndicator.setAlpha(0x80);
}
postSetDrawable(activatedIndicator);
}
public void setIsGap(final boolean isGap) {
mIsGap = isGap;
mView.requestLayout();
}
public void setItemBackground(final Drawable itemBackground) {
preSetDrawable(mBackground);
mBackground = itemBackground;
updateBackgroundAlpha();
postSetDrawable(itemBackground);
}
public void setItemBackgroundAlpha(final float alpha) {
mBackgroundAlpha = alpha;
updateBackgroundAlpha();
}
public void setItemSelector(final Drawable itemSelector) {
preSetDrawable(mItemSelector);
mItemSelector = itemSelector;
if (itemSelector != null) {
itemSelector.setAlpha(0x80);
}
postSetDrawable(itemSelector);
}
public void setOnOverflowIconClickListener(final OnOverflowIconClickListener listener) {
mOnOverflowIconClickListener = listener;
}
public void setOverflowIcon(final Drawable overflowIcon) {
preSetDrawable(mOverflowIcon);
mOverflowIcon = overflowIcon;
if (mOverflowIcon != null) {
mOverflowIcon.mutate();
}
postSetDrawable(overflowIcon);
}
public boolean verifyDrawable(final Drawable who) {
return who == mBackground || who == mItemSelector || who == mActivatedIndicator || who == mOverflowIcon;
}
private void postSetDrawable(final Drawable curr) {
mView.setWillNotDraw(verifyDrawable(curr));
if (curr != null) {
if (curr.isStateful()) {
curr.setState(mView.getDrawableState());
}
curr.setCallback(mView);
}
}
private void preSetDrawable(final Drawable prev) {
if (prev != null) {
mView.unscheduleDrawable(prev);
prev.setCallback(null);
}
}
private void updateBackgroundAlpha() {
if (mBackground != null) {
mBackground.setAlpha(Math.round(mBackgroundAlpha * 0xff));
}
}
static class FakeOverflowButton extends View {
private final DrawingHelper mHelper;
public FakeOverflowButton(final DrawingHelper helper) {
super(helper.mView.getContext());
mHelper = helper;
}
@Override
public void getLocationInWindow(final int[] location) {
mHelper.mView.getLocationInWindow(location);
location[0] += mHelper.mOverflowIconBounds.left;
location[1] += mHelper.mOverflowIconBounds.top;
}
@Override
public void getLocationOnScreen(final int[] location) {
mHelper.mView.getLocationOnScreen(location);
location[0] += mHelper.mOverflowIconBounds.left;
location[1] += mHelper.mOverflowIconBounds.top;
}
@Override
public View getRootView() {
return mHelper.mView.getRootView();
}
@Override
public Object getTag() {
return mHelper.mView.getTag();
}
@Override
public IBinder getWindowToken() {
return mHelper.mView.getWindowToken();
}
@Override
public void getWindowVisibleDisplayFrame(final Rect outRect) {
mHelper.mView.getWindowVisibleDisplayFrame(outRect);
}
@Override
protected void onLayout(final boolean changed, final int left, final int top, final int right,
final int bottom) {
final Rect bounds = mHelper.mOverflowIconBounds;
layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final int width = mHelper.mOverflowIconBounds.width();
final int height = mHelper.mOverflowIconBounds.height();
final int wSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.getMode(widthMeasureSpec));
final int hSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.getMode(heightMeasureSpec));
setMeasuredDimension(width, height);
super.onMeasure(wSpec, hSpec);
}
}
static class OverflowIconGestureListener extends SimpleOnGestureListener {
private final DrawingHelper mDrawingHelper;
public OverflowIconGestureListener(final DrawingHelper drawingHelper) {
mDrawingHelper = drawingHelper;
}
@Override
public boolean onDown(final MotionEvent e) {
final Drawable d = mDrawingHelper.mPaddedOverflowIcon;
final OnOverflowIconClickListener l = mDrawingHelper.mOnOverflowIconClickListener;
if (d == null || l == null) return false;
return true;
}
@Override
public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX,
final float velocityY) {
return clearHighlight();
}
@Override
public void onLongPress(final MotionEvent e) {
clearHighlight();
}
@Override
public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
final float distanceY) {
return clearHighlight();
}
@Override
public void onShowPress(final MotionEvent e) {
final Drawable d = mDrawingHelper.mPaddedOverflowIcon;
final int c = mDrawingHelper.mThemeColor;
if (d != null) {
d.setColorFilter(c, PorterDuff.Mode.SRC_ATOP);
}
}
@Override
public boolean onSingleTapConfirmed(final MotionEvent e) {
final OnOverflowIconClickListener l = mDrawingHelper.mOnOverflowIconClickListener;
if (clearHighlight() && l != null) {
l.onOverflowIconClick(mDrawingHelper.mFakeOverflowButton);
}
return false;
}
@Override
public boolean onSingleTapUp(final MotionEvent e) {
return clearHighlight();
}
private boolean clearHighlight() {
final Drawable d = mDrawingHelper.mPaddedOverflowIcon;
if (d != null) {
d.clearColorFilter();
return true;
}
return false;
}
}
}
public static interface OnOverflowIconClickListener {
public void onOverflowIconClick(View view);
}
public View getFakeOverflowButton();
public boolean isGap();
public void setActivatedIndicator(Drawable activatedIndicator);
public void setIsGap(boolean isGap);
public void setItemBackground(Drawable itemBackground);
public void setItemSelector(Drawable itemSelector);
public void setOnOverflowIconClickListener(final OnOverflowIconClickListener listener);
public void setOverflowIcon(Drawable overflowIcon);
public static final class DrawingHelper {
private final View mView;
private final int mCardGapHeight;
private final String mCardGapText;
private final Paint mGapTextPaint;
private final Rect mGapTextBounds = new Rect();
private final Rect mBackgroundPadding = new Rect();
private final Rect mOverflowIconBounds = new Rect();
private Drawable mBackground;
private Drawable mItemSelector;
private Drawable mActivatedIndicator;
private Drawable mOverflowIcon;
private Drawable mPaddedOverflowIcon;
private boolean mIsGap;
private final int mThemeColor;
private final GestureDetector mOverflowIconGestureDetector;
private OnOverflowIconClickListener mOnOverflowIconClickListener;
private final FakeOverflowButton mFakeOverflowButton;
private float mBackgroundAlpha;
public DrawingHelper(final View view, final Context context, final AttributeSet attrs, final int defStyleAttr) {
mView = view;
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CardItemView, defStyleAttr,
R.style.Widget_CardItemView);
mGapTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCardGapHeight = a.getDimensionPixelSize(R.styleable.CardItemView_cardGapHeight, 0);
mCardGapText = a.getString(R.styleable.CardItemView_cardGapText);
mGapTextPaint.setColor(a.getColor(R.styleable.CardItemView_cardGapTextColor, Color.GRAY));
mGapTextPaint.setTextSize(a.getDimensionPixelSize(R.styleable.CardItemView_cardGapTextSize, 18));
mGapTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mThemeColor = view.isInEditMode() ? 0 : ThemeUtils.getUserAccentColor(context);
mOverflowIconGestureDetector = new GestureDetector(context, new OverflowIconGestureListener(this));
mFakeOverflowButton = new FakeOverflowButton(this);
mBackgroundAlpha = a.getFraction(R.styleable.CardItemView_cardBackgroundAlpha, 1, 1, 1.0f);
setItemBackground(a.getDrawable(R.styleable.CardItemView_cardBackground));
setItemSelector(a.getDrawable(R.styleable.CardItemView_cardSelector));
setActivatedIndicator(a.getDrawable(R.styleable.CardItemView_cardActivatedIndicator));
setOverflowIcon(a.getDrawable(R.styleable.CardItemView_cardOverflowIcon));
a.recycle();
}
public void dispatchDrawableStateChanged() {
final int[] state = mView.getDrawableState();
if (mBackground != null) {
mBackground.setState(state);
}
if (mItemSelector != null) {
mItemSelector.setState(state);
}
if (mActivatedIndicator != null) {
mActivatedIndicator.setState(state);
}
}
public void dispatchOnSizeChanged(final int w, final int h, final int oldw, final int oldh) {
final int paddingLeft = mView.getPaddingLeft();
final int paddingTop = mView.getPaddingTop();
final int paddingRight = mView.getPaddingRight();
final int paddingBottom = mView.getPaddingBottom();
final int l = paddingLeft, t = paddingTop, r = w - paddingRight, b = h - paddingBottom;
if (mBackground != null) {
if (mBackground instanceof NinePatchDrawable) {
final NinePatchDrawable npd = (NinePatchDrawable) mBackground;
npd.getPadding(mBackgroundPadding);
npd.setBounds(l - mBackgroundPadding.left, t - mBackgroundPadding.top,
r + mBackgroundPadding.right, b + mBackgroundPadding.bottom);
} else {
mBackground.setBounds(l, t, r, b);
}
}
if (mItemSelector != null) {
if (mIsGap) {
mItemSelector.setBounds(0, 0, w, h);
} else {
mItemSelector.setBounds(l, t, r, b);
}
}
if (mActivatedIndicator != null) {
mActivatedIndicator.setBounds(l, t, r, b);
}
if (mOverflowIcon != null) {
mPaddedOverflowIcon = new PaddingDrawable(mOverflowIcon, paddingTop, 0, paddingRight, 0);
final int iw = mPaddedOverflowIcon.getIntrinsicWidth();
final int ih = mPaddedOverflowIcon.getIntrinsicHeight();
mOverflowIconBounds.set(w - iw, 0, w, ih);
mPaddedOverflowIcon.setBounds(mOverflowIconBounds);
} else {
mPaddedOverflowIcon = null;
}
}
public void drawBackground(final Canvas canvas) {
if (mBackground != null && !mIsGap) {
mBackground.draw(canvas);
}
}
public void drawGap(final Canvas canvas) {
if (mIsGap) {
final int centerX = canvas.getWidth() / 2, centerY = canvas.getHeight() / 2;
if (mCardGapText != null) {
mGapTextPaint.getTextBounds(mCardGapText, 0, mCardGapText.length(), mGapTextBounds);
final float xPos = centerX - mGapTextBounds.width() / 2;
final float yPos = centerY - (mGapTextPaint.descent() + mGapTextPaint.ascent()) / 2;
canvas.drawText(mCardGapText, xPos, yPos, mGapTextPaint);
}
}
}
public void drawOverflowIcon(final Canvas canvas) {
if (mPaddedOverflowIcon != null && mOnOverflowIconClickListener != null) {
mPaddedOverflowIcon.draw(canvas);
}
}
public void drawSelector(final Canvas canvas) {
if (mActivatedIndicator != null) {
mActivatedIndicator.draw(canvas);
}
if (mItemSelector != null) {
mItemSelector.draw(canvas);
}
}
public int getCardGapHeight() {
return mCardGapHeight;
}
public View getFakeOverflowButton() {
return mFakeOverflowButton;
}
public boolean handleOverflowTouchEvent(final MotionEvent ev) {
return mOverflowIconGestureDetector.onTouchEvent(ev);
}
public boolean isGap() {
return mIsGap;
}
public boolean isOverflowIconClicked(final MotionEvent ev) {
if (mOverflowIcon == null || mOnOverflowIconClickListener == null) return false;
final int x = Math.round(ev.getX()), y = Math.round(ev.getY());
if (mOverflowIconBounds.contains(x, y)) return true;
return false;
}
public void setActivatedIndicator(final Drawable activatedIndicator) {
preSetDrawable(mActivatedIndicator);
mActivatedIndicator = activatedIndicator;
if (activatedIndicator != null) {
activatedIndicator.setAlpha(0x80);
}
postSetDrawable(activatedIndicator);
}
public void setIsGap(final boolean isGap) {
mIsGap = isGap;
mView.requestLayout();
}
public void setItemBackground(final Drawable itemBackground) {
preSetDrawable(mBackground);
mBackground = itemBackground;
updateBackgroundAlpha();
postSetDrawable(itemBackground);
}
public void setItemBackgroundAlpha(final float alpha) {
mBackgroundAlpha = alpha;
updateBackgroundAlpha();
}
public void setItemSelector(final Drawable itemSelector) {
preSetDrawable(mItemSelector);
mItemSelector = itemSelector;
if (itemSelector != null) {
itemSelector.setAlpha(0x80);
}
postSetDrawable(itemSelector);
}
public void setOnOverflowIconClickListener(final OnOverflowIconClickListener listener) {
mOnOverflowIconClickListener = listener;
}
public void setOverflowIcon(final Drawable overflowIcon) {
preSetDrawable(mOverflowIcon);
mOverflowIcon = overflowIcon;
if (mOverflowIcon != null) {
mOverflowIcon.mutate();
}
postSetDrawable(overflowIcon);
}
public boolean verifyDrawable(final Drawable who) {
return who == mBackground || who == mItemSelector || who == mActivatedIndicator || who == mOverflowIcon;
}
private void postSetDrawable(final Drawable curr) {
mView.setWillNotDraw(verifyDrawable(curr));
if (curr != null) {
if (curr.isStateful()) {
curr.setState(mView.getDrawableState());
}
curr.setCallback(mView);
}
}
private void preSetDrawable(final Drawable prev) {
if (prev != null) {
mView.unscheduleDrawable(prev);
prev.setCallback(null);
}
}
private void updateBackgroundAlpha() {
if (mBackground != null) {
mBackground.setAlpha(Math.round(mBackgroundAlpha * 0xff));
}
}
static class FakeOverflowButton extends View {
private final DrawingHelper mHelper;
public FakeOverflowButton(final DrawingHelper helper) {
super(helper.mView.getContext());
mHelper = helper;
}
@Override
public void getLocationInWindow(final int[] location) {
mHelper.mView.getLocationInWindow(location);
location[0] += mHelper.mOverflowIconBounds.left;
location[1] += mHelper.mOverflowIconBounds.top;
}
@Override
public void getLocationOnScreen(final int[] location) {
mHelper.mView.getLocationOnScreen(location);
location[0] += mHelper.mOverflowIconBounds.left;
location[1] += mHelper.mOverflowIconBounds.top;
}
@Override
public View getRootView() {
return mHelper.mView.getRootView();
}
@Override
public Object getTag() {
return mHelper.mView.getTag();
}
@Override
public IBinder getWindowToken() {
return mHelper.mView.getWindowToken();
}
@Override
public void getWindowVisibleDisplayFrame(final Rect outRect) {
mHelper.mView.getWindowVisibleDisplayFrame(outRect);
}
@Override
protected void onLayout(final boolean changed, final int left, final int top, final int right,
final int bottom) {
final Rect bounds = mHelper.mOverflowIconBounds;
layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final int width = mHelper.mOverflowIconBounds.width();
final int height = mHelper.mOverflowIconBounds.height();
final int wSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.getMode(widthMeasureSpec));
final int hSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.getMode(heightMeasureSpec));
setMeasuredDimension(width, height);
super.onMeasure(wSpec, hSpec);
}
}
static class OverflowIconGestureListener extends SimpleOnGestureListener {
private final DrawingHelper mDrawingHelper;
public OverflowIconGestureListener(final DrawingHelper drawingHelper) {
mDrawingHelper = drawingHelper;
}
@Override
public boolean onDown(final MotionEvent e) {
final Drawable d = mDrawingHelper.mPaddedOverflowIcon;
final OnOverflowIconClickListener l = mDrawingHelper.mOnOverflowIconClickListener;
if (d == null || l == null) return false;
return true;
}
@Override
public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX,
final float velocityY) {
return clearHighlight();
}
@Override
public void onLongPress(final MotionEvent e) {
clearHighlight();
}
@Override
public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
final float distanceY) {
return clearHighlight();
}
@Override
public void onShowPress(final MotionEvent e) {
final Drawable d = mDrawingHelper.mPaddedOverflowIcon;
final int c = mDrawingHelper.mThemeColor;
if (d != null) {
d.setColorFilter(c, PorterDuff.Mode.SRC_ATOP);
}
}
@Override
public boolean onSingleTapConfirmed(final MotionEvent e) {
final OnOverflowIconClickListener l = mDrawingHelper.mOnOverflowIconClickListener;
if (clearHighlight() && l != null) {
l.onOverflowIconClick(mDrawingHelper.mFakeOverflowButton);
}
return false;
}
@Override
public boolean onSingleTapUp(final MotionEvent e) {
return clearHighlight();
}
private boolean clearHighlight() {
final Drawable d = mDrawingHelper.mPaddedOverflowIcon;
if (d != null) {
d.clearColorFilter();
return true;
}
return false;
}
}
}
public static interface OnOverflowIconClickListener {
public void onOverflowIconClick(View view);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 481 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 587 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 413 B

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_card_item_message_incoming_normal_dark" android:state_window_focused="false"/>
<item android:drawable="@drawable/bg_card_item_message_incoming_pressed_dark" android:state_pressed="true" android:state_selected="true"/>
<item android:drawable="@drawable/bg_card_item_message_incoming_pressed_dark" android:state_pressed="true" android:state_selected="false"/>
<item android:drawable="@drawable/bg_card_item_message_incoming_focused_dark" android:state_selected="true"/>
</selector>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_card_item_message_incoming_normal_light" android:state_window_focused="false"/>
<item android:drawable="@drawable/bg_card_item_message_incoming_pressed_light" android:state_pressed="true" android:state_selected="true"/>
<item android:drawable="@drawable/bg_card_item_message_incoming_pressed_light" android:state_pressed="true" android:state_selected="false"/>
<item android:drawable="@drawable/bg_card_item_message_incoming_focused_light" android:state_selected="true"/>
</selector>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_card_item_message_incoming_pressed_dark"/>
<item android:drawable="@drawable/bg_card_item_message_incoming_longpressed_dark"/>
</transition>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_card_item_message_incoming_pressed_light"/>
<item android:drawable="@drawable/bg_card_item_message_incoming_longpressed_light"/>
</transition>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_card_item_message_outgoing_normal_dark" android:state_window_focused="false"/>
<item android:drawable="@drawable/bg_card_item_message_outgoing_pressed_dark" android:state_pressed="true" android:state_selected="true"/>
<item android:drawable="@drawable/bg_card_item_message_outgoing_pressed_dark" android:state_pressed="true" android:state_selected="false"/>
<item android:drawable="@drawable/bg_card_item_message_outgoing_focused_dark" android:state_selected="true"/>
</selector>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_card_item_message_outgoing_normal_light" android:state_window_focused="false"/>
<item android:drawable="@drawable/bg_card_item_message_outgoing_pressed_light" android:state_pressed="true" android:state_selected="true"/>
<item android:drawable="@drawable/bg_card_item_message_outgoing_pressed_light" android:state_pressed="true" android:state_selected="false"/>
<item android:drawable="@drawable/bg_card_item_message_outgoing_focused_light" android:state_selected="true"/>
</selector>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_card_item_message_outgoing_pressed_dark"/>
<item android:drawable="@drawable/bg_card_item_message_outgoing_longpressed_dark"/>
</transition>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_card_item_message_outgoing_pressed_light"/>
<item android:drawable="@drawable/bg_card_item_message_outgoing_longpressed_light"/>
</transition>

View File

@ -2,6 +2,7 @@
<org.mariotaku.twidere.view.MainFrameLayout
android:id="@+id/home_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="match_parent">
@ -23,7 +24,9 @@
style="?android:actionBarStyle"
android:layout_width="match_parent"
android:layout_height="?android:actionBarSize"
android:layout_weight="0"/>
android:layout_weight="0"
app:tabExpandEnabled="true"
app:tabHorizontalPadding="@dimen/element_spacing_normal"/>
<View
android:id="@+id/actionbar_overlay"

View File

@ -25,13 +25,14 @@
android:layout_height="match_parent"
android:orientation="vertical">
<com.astuetz.PagerSlidingTabStrip
<org.mariotaku.twidere.view.TabPagerIndicator
android:id="@+id/view_pager_tabs"
android:layout_width="match_parent"
android:layout_height="@dimen/element_size_normal"
android:textColor="?android:textColorSecondary"
app:pstsTabBackground="?android:selectableItemBackground"
app:pstsTabPaddingLeftRight="@dimen/element_spacing_large"/>
app:tabHorizontalPadding="@dimen/element_spacing_large"
app:tabExpandEnabled="false"
app:tabIconColor="?android:colorForeground"/>
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"

View File

@ -112,13 +112,20 @@
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/element_spacing_small"
android:singleLine="true"
android:text="@string/description"
android:textAppearance="?android:textAppearanceMedium"/>
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/element_spacing_small"
android:paddingRight="@dimen/element_spacing_small"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
@ -135,14 +142,20 @@
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/element_spacing_small"
android:singleLine="true"
android:text="@string/location"
android:textAppearance="?android:textAppearanceMedium"/>
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/element_spacing_small"
android:paddingRight="@dimen/element_spacing_small"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
@ -160,15 +173,21 @@
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/element_spacing_small"
android:singleLine="true"
android:text="@string/url"
android:textAppearance="?android:textAppearanceMedium"/>
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:paddingLeft="@dimen/element_spacing_small"
android:paddingRight="@dimen/element_spacing_small"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
@ -184,16 +203,22 @@
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/element_spacing_small"
android:singleLine="true"
android:text="@string/created_at"
android:textAppearance="?android:textAppearanceMedium"/>
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/created_at"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/element_spacing_small"
android:paddingRight="@dimen/element_spacing_small"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorSecondary"/>
@ -332,7 +357,7 @@
android:alpha="0.2"
android:gravity="center"
android:padding="@dimen/element_spacing_small"
android:text="- Luna meae es -"
android:text="@string/uucky_footer_text"
android:textColor="?android:textColorPrimary"
android:textSize="10sp"
android:textStyle="italic"

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<view
class="org.mariotaku.twidere.view.TabPagerIndicator$ItemLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?android:selectableItemBackground">
<LinearLayout
android:id="@+id/tab_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/tab_icon"
android:layout_width="@dimen/action_icon_size"
android:layout_height="@dimen/action_icon_size"
android:contentDescription="@string/icon"
android:scaleType="centerInside"/>
<TextView
android:id="@+id/tab_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
</LinearLayout>
<org.mariotaku.twidere.view.BadgeView
android:id="@+id/unread_indicator"
android:layout_width="@dimen/unread_indicator_size"
android:layout_height="@dimen/unread_indicator_size"
android:layout_alignRight="@+id/tab_content"
android:layout_alignTop="@+id/tab_content"
android:layout_marginRight="@dimen/element_spacing_minus_small"
android:layout_marginTop="@dimen/element_spacing_minus_small"
android:background="@drawable/bg_unread_indicator"
android:ellipsize="none"
android:gravity="center"
android:padding="@dimen/element_spacing_xsmall"
android:singleLine="true"
android:textColor="?android:textColorPrimary"/>
</view>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<me.imid.swipebacklayout.lib.SwipeBackLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:selectableItemBackground">
<View
android:id="@+id/selected_indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:background="?android:activatedBackgroundIndicator"/>
<ImageView
android:id="@+id/tab_icon"
android:layout_width="@dimen/action_icon_size"
android:layout_height="@dimen/action_icon_size"
android:layout_centerInParent="true"
android:scaleType="centerInside"/>
<org.mariotaku.twidere.view.BadgeView
android:id="@+id/unread_indicator"
android:layout_width="@dimen/unread_indicator_size"
android:layout_height="@dimen/unread_indicator_size"
android:layout_alignRight="@+id/tab_icon"
android:layout_alignTop="@+id/tab_icon"
android:layout_marginRight="@dimen/element_spacing_minus_small"
android:layout_marginTop="@dimen/element_spacing_minus_small"
android:background="@drawable/bg_unread_indicator"
android:ellipsize="none"
android:gravity="center"
android:padding="@dimen/element_spacing_xsmall"
android:singleLine="true"
android:textColor="?android:textColorPrimary"/>
</RelativeLayout>

View File

@ -29,6 +29,7 @@
<autoTransition>
<targets>
<target android:targetId="@id/profile_banner"/>
<target android:targetId="@id/view_pager_tabs"/>
</targets>
</autoTransition>
</transitionSet>

View File

@ -11,10 +11,6 @@
<attr name="messageBubbleColor" format="color"/>
<attr name="cardItemBackground" format="reference"/>
<attr name="cardItemBackgroundColor" format="color"/>
<attr name="cardItemMessageIncomingBackground" format="reference"/>
<attr name="cardItemMessageOutgoingBackground" format="reference"/>
<attr name="cardItemMessageProfileImageIncomingBackground" format="reference"/>
<attr name="cardItemMessageProfileImageOutgoingBackground" format="reference"/>
<attr name="listMenuOverflowButton" format="reference"/>
<attr name="ignorePadding" format="boolean"/>
<attr name="linePageIndicatorStyle" format="reference"/>
@ -32,9 +28,12 @@
<attr name="cardGapTextSize" format="dimension"/>
</declare-styleable>
<declare-styleable name="TabPagerIndicator">
<attr name="tabItemStyle" format="reference"/>
<attr name="tabItemContentStyle" format="reference"/>
<attr name="tabItemTextStyle" format="reference"/>
<attr name="tabStripColor" format="color"/>
<attr name="tabIconColor" format="color"/>
<attr name="tabHorizontalPadding" format="dimension"/>
<attr name="tabVerticalPadding" format="dimension"/>
<attr name="tabExpandEnabled" format="boolean"/>
<attr name="tabDisplayOption"/>
</declare-styleable>
<declare-styleable name="LinePageIndicator">
<!-- Whether or not the indicators should be centered. -->
@ -94,5 +93,8 @@
<flag name="vibration" value="2"/>
<flag name="light" value="4"/>
</attr>
<attr name="tabDisplayOption">
<flag name="label" value="0x1"/>
<flag name="icon" value="0x2"/>
</attr>
</resources>

View File

@ -12,5 +12,6 @@
<string name="font_family_light" translatable="false">Light</string>
<string name="font_family_thin" translatable="false">Thin</string>
<string name="easter_egg_triggered_message" translatable="false">不二对你的膝盖发动了会心一击!</string>
<string name="uucky_footer_text">- Luna meae es -</string>
</resources>

View File

@ -30,27 +30,12 @@
</item>
<!-- Custom view styles -->
<item name="tabItemStyle">@style/Widget.TabPageIndicator.TabItem</item>
<item name="tabItemContentStyle">@style/Widget.TabPageIndicator.TabItem.Content</item>
<item name="tabItemTextStyle">@style/Widget.TabPageIndicator.TabItem.TextView.Dark</item>
<!-- 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">
@drawable/bg_card_item_message_incoming_dark
</item>
<item name="cardItemMessageOutgoingBackground">
@drawable/bg_card_item_message_outgoing_dark
</item>
<item name="cardItemMessageProfileImageIncomingBackground">
@drawable/bg_card_item_message_profile_image_incoming_dark
</item>
<item name="cardItemMessageProfileImageOutgoingBackground">
@drawable/bg_card_item_message_profile_image_outgoing_dark
</item>
<item name="listMenuOverflowButton">@drawable/ic_list_menu_moreoverflow_normal_holo_dark
</item>
<item name="cardItemViewStyle">@style/Widget.CardItemView</item>
@ -75,33 +60,15 @@
<!--<item name="android:actionBarWidgetTheme">@style/Theme.Twidere.Dark</item>-->
<!-- Custom view styles -->
<item name="tabItemStyle">@style/Widget.TabPageIndicator.TabItem</item>
<item name="tabItemContentStyle">@style/Widget.TabPageIndicator.TabItem.Content</item>
<item name="tabItemTextStyle">
@style/Widget.TabPageIndicator.TabItem.TextView.Light.DarkActionBar
</item>
<!-- 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">
@drawable/bg_card_item_message_incoming_light
</item>
<item name="cardItemMessageOutgoingBackground">
@drawable/bg_card_item_message_outgoing_light
</item>
<item name="cardItemMessageProfileImageIncomingBackground">
@drawable/bg_card_item_message_profile_image_incoming_light
</item>
<item name="cardItemMessageProfileImageOutgoingBackground">
@drawable/bg_card_item_message_profile_image_outgoing_light
</item>
<item name="listMenuOverflowButton">@drawable/ic_list_menu_moreoverflow_normal_holo_light
</item>
<item name="cardItemViewStyle">@style/Widget.CardItemView.Light</item>
<!-- Twidere specific styles -->
<item name="menuIconColor">@color/action_icon_dark</item>
<item name="menuIconColorDisabled">@color/action_icon_dark_disabled</item>
@ -153,27 +120,13 @@
<style name="Theme.Twidere.Dark.Dialog" parent="Theme.Base.Dialog">
<!-- Custom view styles -->
<item name="tabItemStyle">@style/Widget.TabPageIndicator.TabItem</item>
<item name="tabItemContentStyle">@style/Widget.TabPageIndicator.TabItem.Content</item>
<item name="tabItemTextStyle">@style/Widget.TabPageIndicator.TabItem.TextView</item>
<!-- 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">
@drawable/bg_card_item_message_incoming_dark
</item>
<item name="cardItemMessageOutgoingBackground">
@drawable/bg_card_item_message_outgoing_dark
</item>
<item name="cardItemMessageProfileImageIncomingBackground">
@drawable/bg_card_item_message_profile_image_incoming_dark
</item>
<item name="cardItemMessageProfileImageOutgoingBackground">
@drawable/bg_card_item_message_profile_image_outgoing_dark
</item>
<item name="listMenuOverflowButton">@drawable/ic_list_menu_moreoverflow_normal_holo_dark
</item>
<item name="cardItemViewStyle">@style/Widget.CardItemView</item>
@ -187,27 +140,13 @@
<style name="Theme.Twidere.Light.Dialog" parent="Theme.Base.Light.Dialog">
<!-- Custom view styles -->
<item name="tabItemStyle">@style/Widget.TabPageIndicator.TabItem</item>
<item name="tabItemContentStyle">@style/Widget.TabPageIndicator.TabItem.Content</item>
<item name="tabItemTextStyle">@style/Widget.TabPageIndicator.TabItem.TextView</item>
<!-- 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">
@drawable/bg_card_item_message_incoming_light
</item>
<item name="cardItemMessageOutgoingBackground">
@drawable/bg_card_item_message_outgoing_light
</item>
<item name="cardItemMessageProfileImageIncomingBackground">
@drawable/bg_card_item_message_profile_image_incoming_light
</item>
<item name="cardItemMessageProfileImageOutgoingBackground">
@drawable/bg_card_item_message_profile_image_outgoing_light
</item>
<item name="listMenuOverflowButton">@drawable/ic_list_menu_moreoverflow_normal_holo_light
</item>
<item name="cardItemViewStyle">@style/Widget.CardItemView.Light</item>
@ -258,18 +197,6 @@
<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">
@drawable/bg_card_item_message_incoming_dark
</item>
<item name="cardItemMessageOutgoingBackground">
@drawable/bg_card_item_message_outgoing_dark
</item>
<item name="cardItemMessageProfileImageIncomingBackground">
@drawable/bg_card_item_message_profile_image_incoming_dark
</item>
<item name="cardItemMessageProfileImageOutgoingBackground">
@drawable/bg_card_item_message_profile_image_outgoing_dark
</item>
<item name="listMenuOverflowButton">@drawable/ic_list_menu_moreoverflow_normal_holo_dark
</item>
<item name="cardItemViewStyle">@style/Widget.CardItemView</item>
@ -351,27 +278,12 @@
</item>
<!-- Custom view styles -->
<item name="tabItemStyle">@style/Widget.TabPageIndicator.TabItem</item>
<item name="tabItemContentStyle">@style/Widget.TabPageIndicator.TabItem.Content</item>
<item name="tabItemTextStyle">@style/Widget.TabPageIndicator.TabItem.TextView.Dark</item>
<!-- 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">
@drawable/bg_card_item_message_incoming_dark
</item>
<item name="cardItemMessageOutgoingBackground">
@drawable/bg_card_item_message_outgoing_dark
</item>
<item name="cardItemMessageProfileImageIncomingBackground">
@drawable/bg_card_item_message_profile_image_incoming_dark
</item>
<item name="cardItemMessageProfileImageOutgoingBackground">
@drawable/bg_card_item_message_profile_image_outgoing_dark
</item>
<item name="listMenuOverflowButton">@drawable/ic_list_menu_moreoverflow_normal_holo_dark
</item>
<item name="cardItemViewStyle">@style/Widget.CardItemView</item>
@ -392,27 +304,12 @@
<!--<item name="android:windowBackground">@color/bg_color_light</item>-->
<!-- Custom view styles -->
<item name="tabItemStyle">@style/Widget.TabPageIndicator.TabItem</item>
<item name="tabItemContentStyle">@style/Widget.TabPageIndicator.TabItem.Content</item>
<item name="tabItemTextStyle">@style/Widget.TabPageIndicator.TabItem.TextView.Light</item>
<!-- 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">
@drawable/bg_card_item_message_incoming_light
</item>
<item name="cardItemMessageOutgoingBackground">
@drawable/bg_card_item_message_outgoing_light
</item>
<item name="cardItemMessageProfileImageIncomingBackground">
@drawable/bg_card_item_message_profile_image_incoming_light
</item>
<item name="cardItemMessageProfileImageOutgoingBackground">
@drawable/bg_card_item_message_profile_image_outgoing_light
</item>
<item name="listMenuOverflowButton">@drawable/ic_list_menu_moreoverflow_normal_holo_light
</item>
<item name="cardItemViewStyle">@style/Widget.CardItemView.Light</item>
@ -433,27 +330,12 @@
<!--<item name="android:windowBackground">@color/bg_color_light</item>-->
<!-- Custom view styles -->
<item name="tabItemStyle">@style/Widget.TabPageIndicator.TabItem</item>
<item name="tabItemContentStyle">@style/Widget.TabPageIndicator.TabItem.Content</item>
<item name="tabItemTextStyle">@style/Widget.TabPageIndicator.TabItem.TextView.Light</item>
<!-- 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">
@drawable/bg_card_item_message_incoming_light
</item>
<item name="cardItemMessageOutgoingBackground">
@drawable/bg_card_item_message_outgoing_light
</item>
<item name="cardItemMessageProfileImageIncomingBackground">
@drawable/bg_card_item_message_profile_image_incoming_light
</item>
<item name="cardItemMessageProfileImageOutgoingBackground">
@drawable/bg_card_item_message_profile_image_outgoing_light
</item>
<item name="listMenuOverflowButton">@drawable/ic_list_menu_moreoverflow_normal_holo_light
</item>
<item name="cardItemViewStyle">@style/Widget.CardItemView.Light</item>