fixed crashes under 4.4 caused by Paint.setShader(null)

improved fragments
improved status card layout
This commit is contained in:
Mariotaku Lee 2015-01-27 00:09:02 +08:00
parent 6bf22e803f
commit 3e383ce492
44 changed files with 1215 additions and 950 deletions

View File

@ -83,8 +83,8 @@ import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.SupportTabSpec;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.task.TwidereAsyncTask;
import org.mariotaku.twidere.util.ActivityAccessor;
import org.mariotaku.twidere.util.ActivityAccessor.TaskDescriptionCompat;
import org.mariotaku.twidere.util.accessor.ActivityAccessor;
import org.mariotaku.twidere.util.accessor.ActivityAccessor.TaskDescriptionCompat;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ColorUtils;
import org.mariotaku.twidere.util.CustomTabUtils;
@ -115,7 +115,7 @@ import static org.mariotaku.twidere.util.Utils.getAccountIds;
import static org.mariotaku.twidere.util.Utils.getDefaultAccountId;
import static org.mariotaku.twidere.util.Utils.getTabDisplayOptionInt;
import static org.mariotaku.twidere.util.Utils.isDatabaseReady;
import static org.mariotaku.twidere.util.Utils.openDirectMessagesConversation;
import static org.mariotaku.twidere.util.Utils.openMessageConversation;
import static org.mariotaku.twidere.util.Utils.openSearch;
import static org.mariotaku.twidere.util.Utils.showMenuItemToast;
@ -766,7 +766,7 @@ public class HomeActivity extends BaseSupportActivity implements OnClickListener
startActivity(new Intent(INTENT_ACTION_COMPOSE));
} else {
if (classEquals(DirectMessagesFragment.class, tab.cls)) {
openDirectMessagesConversation(this, -1, -1);
openMessageConversation(this, -1, -1);
} else if (classEquals(TrendsSuggectionsFragment.class, tab.cls)) {
openSearchView(null);
} else {

View File

@ -38,12 +38,13 @@ import android.view.WindowManager.LayoutParams;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.iface.IControlBarActivity;
import org.mariotaku.twidere.fragment.iface.IBaseFragment;
import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback;
import org.mariotaku.twidere.fragment.iface.IBasePullToRefreshFragment;
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface;
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback;
import org.mariotaku.twidere.util.ActivityAccessor;
import org.mariotaku.twidere.util.ActivityAccessor.TaskDescriptionCompat;
import org.mariotaku.twidere.util.accessor.ActivityAccessor;
import org.mariotaku.twidere.util.accessor.ActivityAccessor.TaskDescriptionCompat;
import org.mariotaku.twidere.util.FlymeUtils;
import org.mariotaku.twidere.util.MultiSelectEventHandler;
import org.mariotaku.twidere.util.ThemeUtils;
@ -158,16 +159,19 @@ public class LinkHandlerActivity extends BaseSupportActivity implements OnClickL
@Override
public boolean getSystemWindowsInsets(Rect insets) {
final boolean result = super.getSystemWindowsInsets(insets);
if (result) {
insets.bottom = 0;
}
// if (result) {
// insets.bottom = 0;
// }
return result;
}
@Override
public void fitSystemWindows(Rect insets) {
super.fitSystemWindows(insets);
mMainContent.setPadding(insets.left, insets.top, insets.right, insets.bottom);
final Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_content);
if (fragment instanceof IBaseFragment) {
((IBaseFragment) fragment).requestFitSystemWindows();
}
}
@Override

View File

@ -152,8 +152,8 @@ public final class MediaViewerActivity extends BaseSupportActivity implements Co
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
public void onBaseViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mImageView = (TouchImageView) view.findViewById(R.id.image_view);
mProgressBar = (ProgressBar) view.findViewById(R.id.progress);
}

View File

@ -1,162 +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.Cursor;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.MultiSelectManager;
import org.mariotaku.twidere.view.holder.DirectMessageEntryViewHolder;
import static org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries.IDX_ACCOUNT_ID;
import static org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries.IDX_CONVERSATION_ID;
import static org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries.IDX_NAME;
import static org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries.IDX_PROFILE_IMAGE_URL;
import static org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries.IDX_SCREEN_NAME;
import static org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries.IDX_TEXT;
import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
import static org.mariotaku.twidere.util.UserColorNameUtils.getUserColor;
import static org.mariotaku.twidere.util.UserColorNameUtils.getUserNickname;
import static org.mariotaku.twidere.util.Utils.configBaseCardAdapter;
import static org.mariotaku.twidere.util.Utils.getAccountColor;
import static org.mariotaku.twidere.util.Utils.openUserProfile;
public class DirectMessageEntriesAdapter extends BaseCursorAdapter implements IBaseCardAdapter,
OnClickListener {
private final ImageLoaderWrapper mImageLoader;
private final MultiSelectManager mMultiSelectManager;
public DirectMessageEntriesAdapter(final Context context) {
super(context, R.layout.list_item_message_entry, null, new String[0], new int[0], 0);
final TwidereApplication app = TwidereApplication.getInstance(context);
mMultiSelectManager = app.getMultiSelectManager();
mImageLoader = app.getImageLoaderWrapper();
configBaseCardAdapter(context, this);
}
@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
final DirectMessageEntryViewHolder holder = (DirectMessageEntryViewHolder) view.getTag();
final int position = cursor.getPosition();
final long accountId = cursor.getLong(ConversationEntries.IDX_ACCOUNT_ID);
final long conversationId = cursor.getLong(ConversationEntries.IDX_CONVERSATION_ID);
final long timestamp = cursor.getLong(ConversationEntries.IDX_MESSAGE_TIMESTAMP);
final boolean isOutgoing = cursor.getInt(ConversationEntries.IDX_IS_OUTGOING) == 1;
final String name = cursor.getString(IDX_NAME);
final String screenName = cursor.getString(IDX_SCREEN_NAME);
final boolean showAccountColor = isShowAccountColor();
holder.setAccountColorEnabled(showAccountColor);
if (showAccountColor) {
holder.setAccountColor(getAccountColor(mContext, accountId));
}
holder.setUserColor(getUserColor(mContext, conversationId));
holder.setTextSize(getTextSize());
final String nick = getUserNickname(context, conversationId);
holder.name.setText(TextUtils.isEmpty(nick) ? name : isNicknameOnly() ? nick : context.getString(
R.string.name_with_nickname, name, nick));
holder.screen_name.setText("@" + screenName);
holder.text.setText(toPlainText(cursor.getString(IDX_TEXT)));
holder.time.setTime(timestamp);
holder.setIsOutgoing(isOutgoing);
final boolean displayProfileImage = isDisplayProfileImage();
holder.profile_image.setVisibility(displayProfileImage ? View.VISIBLE : View.GONE);
if (displayProfileImage) {
holder.profile_image.setTag(position);
final String profile_image_url_string = cursor.getString(IDX_PROFILE_IMAGE_URL);
mImageLoader.displayProfileImage(holder.profile_image, profile_image_url_string);
} else {
mImageLoader.cancelDisplayTask(holder.profile_image);
}
super.bindView(view, context, cursor);
}
public long getAccountId(final int position) {
final Cursor c = getCursor();
if (c == null || c.isClosed() || !c.moveToPosition(position)) return -1;
return c.getLong(IDX_ACCOUNT_ID);
}
public long getConversationId(final int position) {
final Cursor c = getCursor();
if (c == null || c.isClosed() || !c.moveToPosition(position)) return -1;
return c.getLong(IDX_CONVERSATION_ID);
}
public String getScreenName(final int position) {
final Cursor c = getCursor();
if (c == null || c.isClosed() || !c.moveToPosition(position)) return null;
return c.getString(IDX_SCREEN_NAME);
}
@Override
public View newView(final Context context, final Cursor cursor, final ViewGroup parent) {
final View view = super.newView(context, cursor, parent);
final Object tag = view.getTag();
if (!(tag instanceof DirectMessageEntryViewHolder)) {
final DirectMessageEntryViewHolder holder = new DirectMessageEntryViewHolder(view);
holder.profile_image.setOnClickListener(this);
view.setTag(holder);
}
return view;
}
@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.profile_image: {
if (mContext instanceof Activity) {
final long account_id = getAccountId(position);
final long user_id = getConversationId(position);
final String screen_name = getScreenName(position);
openUserProfile(mContext, account_id, user_id, screen_name, null);
}
break;
}
// case R.id.item_menu: {
// if (position == -1 || mListener == null) return;
// mListener.onMenuButtonClick(view, position, getItemId(position));
// break;
// }
}
}
}

View File

@ -1,168 +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.database.Cursor;
import android.text.Html;
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.IDirectMessagesAdapter;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.model.ParcelableDirectMessage;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.ImageLoadingHandler;
import org.mariotaku.twidere.util.MultiSelectManager;
import org.mariotaku.twidere.view.holder.DirectMessageConversationViewHolder;
import java.util.Locale;
import static org.mariotaku.twidere.util.Utils.configBaseCardAdapter;
import static org.mariotaku.twidere.util.Utils.findDirectMessageInDatabases;
import static org.mariotaku.twidere.util.Utils.formatToLongTimeString;
import static org.mariotaku.twidere.util.Utils.openMedia;
public class DirectMessagesListAdapter extends BaseCursorAdapter implements IDirectMessagesAdapter,
OnClickListener {
private ScaleType mImagePreviewScaleType;
private final ImageLoaderWrapper mImageLoader;
private final Context mContext;
private final MultiSelectManager mMultiSelectManager;
private final ImageLoadingHandler mImageLoadingHandler;
private ParcelableDirectMessage.CursorIndices mIndices;
public DirectMessagesListAdapter(final Context context) {
super(context, R.layout.card_item_message_conversation, null, new String[0], new int[0], 0);
mContext = context;
final TwidereApplication app = TwidereApplication.getInstance(context);
mMultiSelectManager = app.getMultiSelectManager();
mImageLoader = app.getImageLoaderWrapper();
mImageLoadingHandler = new ImageLoadingHandler(R.id.media_preview_progress);
configBaseCardAdapter(context, this);
}
@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
final int position = cursor.getPosition();
final DirectMessageConversationViewHolder holder = (DirectMessageConversationViewHolder) view.getTag();
final String firstMedia = cursor.getString(mIndices.first_media);
final long accountId = cursor.getLong(mIndices.account_id);
final long timestamp = cursor.getLong(mIndices.message_timestamp);
final boolean is_outgoing = cursor.getInt(mIndices.is_outgoing) == 1;
holder.setOutgoing(is_outgoing);
holder.setTextSize(getTextSize());
holder.text.setText(Html.fromHtml(cursor.getString(mIndices.text)));
getLinkify().applyAllLinks(holder.text, accountId, false);
holder.text.setMovementMethod(null);
holder.time.setText(formatToLongTimeString(mContext, timestamp));
if (firstMedia == null) {
mImageLoader.cancelDisplayTask(holder.media_preview);
holder.media_preview_container.setVisibility(View.GONE);
} else {
mImageLoader.cancelDisplayTask(holder.media_preview);
holder.media_preview_container.setVisibility(View.VISIBLE);
if (mImagePreviewScaleType != null) {
holder.media_preview.setScaleType(mImagePreviewScaleType);
}
if (!firstMedia.equals(mImageLoadingHandler.getLoadingUri(holder.media_preview))) {
holder.media_preview.setBackgroundResource(0);
mImageLoader.displayPreviewImageWithCredentials(holder.media_preview, firstMedia,
accountId, mImageLoadingHandler);
}
holder.media_preview.setTag(position);
}
super.bindView(view, context, cursor);
}
@Override
public ParcelableDirectMessage findItem(final long id) {
for (int i = 0, count = getCount(); i < count; i++) {
if (getItemId(i) == id) return getDirectMessage(i);
}
return null;
}
public ParcelableDirectMessage getDirectMessage(final int position) {
final Cursor c = getCursor();
if (c == null || c.isClosed()) return null;
c.moveToPosition(position);
final long account_id = c.getLong(mIndices.account_id);
final long message_id = c.getLong(mIndices.message_id);
return findDirectMessageInDatabases(mContext, account_id, message_id);
}
@Override
public View newView(final Context context, final Cursor cursor, final ViewGroup parent) {
final View view = super.newView(context, cursor, parent);
final Object tag = view.getTag();
if (!(tag instanceof DirectMessageConversationViewHolder)) {
final DirectMessageConversationViewHolder holder = new DirectMessageConversationViewHolder(view);
holder.media_preview.setOnClickListener(this);
view.setTag(holder);
}
return view;
}
@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.media_preview: {
final ParcelableDirectMessage message = getDirectMessage(position);
if (message == null || message.media == null) return;
openMedia(mContext, message.account_id, false, null, message.media);
}
}
}
@Override
public void setDisplayImagePreview(final boolean display) {
// Images in DM are always enabled
}
@Override
public void setImagePreviewScaleType(final String scaleTypeString) {
final ScaleType scaleType = ScaleType.valueOf(scaleTypeString.toUpperCase(Locale.US));
mImagePreviewScaleType = scaleType;
}
@Override
public Cursor swapCursor(final Cursor cursor) {
if (cursor != null) {
mIndices = new ParcelableDirectMessage.CursorIndices(cursor);
} else {
mIndices = null;
}
return super.swapCursor(cursor);
}
}

View File

@ -21,95 +21,140 @@ package org.mariotaku.twidere.adapter;
import android.content.Context;
import android.database.Cursor;
import android.text.Html;
import android.support.v7.widget.RecyclerView.Adapter;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView.ScaleType;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.iface.IDirectMessagesAdapter;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.model.ParcelableDirectMessage;
import org.mariotaku.twidere.model.ParcelableDirectMessage.CursorIndices;
import org.mariotaku.twidere.util.DirectMessageOnLinkClickHandler;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.ImageLoadingHandler;
import org.mariotaku.twidere.util.MultiSelectManager;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereLinkify;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.holder.DirectMessageConversationViewHolder;
import org.mariotaku.twidere.view.holder.MessageConversationViewHolder;
import static org.mariotaku.twidere.util.Utils.configBaseCardAdapter;
import static org.mariotaku.twidere.util.Utils.findDirectMessageInDatabases;
import static org.mariotaku.twidere.util.Utils.formatToLongTimeString;
import static org.mariotaku.twidere.util.Utils.openMedia;
public class DirectMessagesConversationAdapter extends BaseCursorAdapter implements IDirectMessagesAdapter,
OnClickListener {
public class MessageConversationAdapter extends Adapter<ViewHolder>
implements Constants, IDirectMessagesAdapter, OnClickListener {
private static final int ITEM_VIEW_TYPE_MESSAGE_OUTGOING = 1;
private static final int ITEM_VIEW_TYPE_MESSAGE_INCOMING = 2;
private final int mOutgoingMessageColor;
private final int mIncomingMessageColor;
private ScaleType mImagePreviewScaleType;
private final ImageLoaderWrapper mImageLoader;
private final Context mContext;
private final LayoutInflater mInflater;
private final ImageLoaderWrapper mImageLoader;
private final MultiSelectManager mMultiSelectManager;
private final ImageLoadingHandler mImageLoadingHandler;
private ParcelableDirectMessage.CursorIndices mIndices;
private Cursor mCursor;
private CursorIndices mIndices;
private TwidereLinkify mLinkify;
public DirectMessagesConversationAdapter(final Context context) {
super(context, R.layout.card_item_message_conversation, null, new String[0], new int[0], 0);
public MessageConversationAdapter(final Context context) {
mContext = context;
mInflater = LayoutInflater.from(context);
final TwidereApplication app = TwidereApplication.getInstance(context);
mLinkify = new TwidereLinkify(new DirectMessageOnLinkClickHandler(context, null));
mMultiSelectManager = app.getMultiSelectManager();
mImageLoader = app.getImageLoaderWrapper();
mImageLoadingHandler = new ImageLoadingHandler(R.id.media_preview_progress);
configBaseCardAdapter(context, this);
mIncomingMessageColor = ThemeUtils.getUserAccentColor(context);
mOutgoingMessageColor = ThemeUtils.getCardBackgroundColor(context);
}
public Context getContext() {
return mContext;
}
public ImageLoaderWrapper getImageLoader() {
return mImageLoader;
}
public TwidereLinkify getLinkify() {
return mLinkify;
}
@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
final int position = cursor.getPosition();
final DirectMessageConversationViewHolder holder = (DirectMessageConversationViewHolder) view.getTag();
final String firstMedia = cursor.getString(mIndices.first_media);
final long accountId = cursor.getLong(mIndices.account_id);
final long timestamp = cursor.getLong(mIndices.message_timestamp);
final boolean is_outgoing = cursor.getInt(mIndices.is_outgoing) == 1;
holder.setOutgoing(is_outgoing);
holder.setTextSize(getTextSize());
holder.text.setText(Html.fromHtml(cursor.getString(mIndices.text)));
getLinkify().applyAllLinks(holder.text, accountId, false);
holder.text.setMovementMethod(null);
holder.time.setText(formatToLongTimeString(mContext, timestamp));
if (firstMedia == null) {
mImageLoader.cancelDisplayTask(holder.media_preview);
holder.media_preview_container.setVisibility(View.GONE);
} else {
mImageLoader.cancelDisplayTask(holder.media_preview);
holder.media_preview_container.setVisibility(View.VISIBLE);
if (mImagePreviewScaleType != null) {
holder.media_preview.setScaleType(mImagePreviewScaleType);
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case ITEM_VIEW_TYPE_MESSAGE_INCOMING: {
final View view = mInflater.inflate(R.layout.card_item_message_conversation_incoming, parent, false);
final MessageConversationViewHolder holder = new MessageConversationViewHolder(this, view);
holder.setMessageColor(mIncomingMessageColor);
return holder;
}
if (!firstMedia.equals(mImageLoadingHandler.getLoadingUri(holder.media_preview))) {
holder.media_preview.setBackgroundResource(0);
mImageLoader.displayPreviewImageWithCredentials(holder.media_preview, firstMedia,
accountId, mImageLoadingHandler);
case ITEM_VIEW_TYPE_MESSAGE_OUTGOING: {
final View view = mInflater.inflate(R.layout.card_item_message_conversation_outgoing, parent, false);
final MessageConversationViewHolder holder = new MessageConversationViewHolder(this, view);
holder.setMessageColor(mOutgoingMessageColor);
return holder;
}
holder.media_preview.setTag(position);
}
super.bindView(view, context, cursor);
return null;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case ITEM_VIEW_TYPE_MESSAGE_INCOMING:
case ITEM_VIEW_TYPE_MESSAGE_OUTGOING: {
final Cursor c = mCursor;
c.moveToPosition(getCursorPosition(position));
((MessageConversationViewHolder) holder).displayMessage(c, mIndices);
}
}
}
private int getCursorPosition(int position) {
return position;
}
@Override
public int getItemViewType(int position) {
final Cursor c = mCursor;
c.moveToPosition(position);
if (c.getInt(mIndices.is_outgoing) == 1) {
return ITEM_VIEW_TYPE_MESSAGE_OUTGOING;
} else {
return ITEM_VIEW_TYPE_MESSAGE_INCOMING;
}
}
@Override
public int getItemCount() {
final Cursor c = mCursor;
if (c == null) return 0;
return c.getCount();
}
@Override
public ParcelableDirectMessage findItem(final long id) {
for (int i = 0, count = getCount(); i < count; i++) {
for (int i = 0, count = getItemCount(); i < count; i++) {
if (getItemId(i) == id) return getDirectMessage(i);
}
return null;
}
public ParcelableDirectMessage getDirectMessage(final int position) {
final Cursor c = getCursor();
final Cursor c = mCursor;
if (c == null || c.isClosed()) return null;
c.moveToPosition(position);
final long account_id = c.getLong(mIndices.account_id);
@ -117,18 +162,6 @@ public class DirectMessagesConversationAdapter extends BaseCursorAdapter impleme
return findDirectMessageInDatabases(mContext, account_id, message_id);
}
@Override
public View newView(final Context context, final Cursor cursor, final ViewGroup parent) {
final View view = super.newView(context, cursor, parent);
final Object tag = view.getTag();
if (!(tag instanceof DirectMessageConversationViewHolder)) {
final DirectMessageConversationViewHolder holder = new DirectMessageConversationViewHolder(view);
holder.media_preview.setOnClickListener(this);
view.setTag(holder);
}
return view;
}
@Override
public void onClick(final View view) {
if (mMultiSelectManager.isActive()) return;
@ -168,13 +201,13 @@ public class DirectMessagesConversationAdapter extends BaseCursorAdapter impleme
mImagePreviewScaleType = scaleType;
}
@Override
public Cursor swapCursor(final Cursor cursor) {
public void setCursor(final Cursor cursor) {
if (cursor != null) {
mIndices = new ParcelableDirectMessage.CursorIndices(cursor);
mIndices = new CursorIndices(cursor);
} else {
mIndices = null;
}
return super.swapCursor(cursor);
mCursor = cursor;
notifyDataSetChanged();
}
}

View File

@ -0,0 +1,151 @@
/*
* 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.database.Cursor;
import android.support.v7.widget.RecyclerView.Adapter;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.MultiSelectManager;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.view.holder.MessageEntryViewHolder;
import static org.mariotaku.twidere.TwidereConstants.SHARED_PREFERENCES_NAME;
import static org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_NICKNAME_ONLY;
public class MessageEntriesAdapter extends Adapter<ViewHolder> implements OnClickListener {
private final Context mContext;
private final LayoutInflater mInflater;
private final ImageLoaderWrapper mImageLoader;
private final MultiSelectManager mMultiSelectManager;
private final boolean mNicknameOnly;
private Cursor mCursor;
private MessageEntriesAdapterListener mListener;
public Context getContext() {
return mContext;
}
public ImageLoaderWrapper getImageLoader() {
return mImageLoader;
}
public boolean isNicknameOnly() {
return mNicknameOnly;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = mInflater.inflate(R.layout.list_item_message_entry, parent, false);
return new MessageEntryViewHolder(this, view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
final Cursor c = mCursor;
c.moveToPosition(position);
((MessageEntryViewHolder) holder).displayMessage(c);
}
public void onMessageClick(int position) {
if (mListener == null) return;
mListener.onEntryClick(position, getEntry(position));
}
public void setCursor(Cursor cursor) {
mCursor = cursor;
notifyDataSetChanged();
}
@Override
public int getItemCount() {
final Cursor c = mCursor;
if (c == null) return 0;
return c.getCount();
}
public MessageEntriesAdapter(final Context context) {
mContext = context;
mInflater = LayoutInflater.from(context);
final TwidereApplication app = TwidereApplication.getInstance(context);
mMultiSelectManager = app.getMultiSelectManager();
mImageLoader = app.getImageLoaderWrapper();
final SharedPreferencesWrapper prefs = SharedPreferencesWrapper.getInstance(context, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
mNicknameOnly = prefs.getBoolean(KEY_NICKNAME_ONLY, false);
}
public static class DirectMessageEntry {
public final long account_id, conversation_id;
public final String screen_name, name;
DirectMessageEntry(Cursor cursor) {
account_id = cursor.getLong(ConversationEntries.IDX_ACCOUNT_ID);
conversation_id = cursor.getLong(ConversationEntries.IDX_CONVERSATION_ID);
screen_name = cursor.getString(ConversationEntries.IDX_SCREEN_NAME);
name = cursor.getString(ConversationEntries.IDX_NAME);
}
}
public DirectMessageEntry getEntry(final int position) {
final Cursor c = mCursor;
if (c == null || c.isClosed() || !c.moveToPosition(position)) return null;
return new DirectMessageEntry(c);
}
@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.profile_image: {
// if (mContext instanceof Activity) {
// final long account_id = getAccountId(position);
// final long user_id = getConversationId(position);
// final String screen_name = getScreenName(position);
// openUserProfile(mContext, account_id, user_id, screen_name, null);
// }
// break;
// }
// }
}
public void setListener(MessageEntriesAdapterListener listener) {
mListener = listener;
}
public interface MessageEntriesAdapterListener {
public void onEntryClick(int position, DirectMessageEntry entry);
}
}

View File

@ -21,11 +21,11 @@ package org.mariotaku.twidere.adapter.iface;
import org.mariotaku.twidere.model.ParcelableDirectMessage;
public interface IDirectMessagesAdapter extends IBaseCardAdapter {
public interface IDirectMessagesAdapter {
public ParcelableDirectMessage findItem(long id);
public ParcelableDirectMessage findItem(long id);
public void setDisplayImagePreview(boolean display);
public void setDisplayImagePreview(boolean display);
public void setImagePreviewScaleType(String scaleType);
public void setImagePreviewScaleType(String scaleType);
}

View File

@ -21,6 +21,7 @@ package org.mariotaku.twidere.fragment.iface;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.View;
public interface IBaseFragment {
Bundle getExtraConfiguration();
@ -29,6 +30,8 @@ public interface IBaseFragment {
void requestFitSystemWindows();
void onBaseViewCreated(View view, Bundle savedInstanceState);
public interface SystemWindowsInsetsCallback {
boolean getSystemWindowsInsets(Rect insets);
}

View File

@ -216,12 +216,12 @@ public abstract class AbsActivitiesFragment<Data> extends BaseSupportFragment im
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
public void onBaseViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mContentView = view.findViewById(R.id.fragment_content);
mProgressContainer = view.findViewById(R.id.progress_container);
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_layout);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
super.onViewCreated(view, savedInstanceState);
}
@Override

View File

@ -312,12 +312,12 @@ public abstract class AbsStatusesFragment<Data> extends BaseSupportFragment impl
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
public void onBaseViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mContentView = view.findViewById(R.id.fragment_content);
mProgressContainer = view.findViewById(R.id.progress_container);
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_layout);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
super.onViewCreated(view, savedInstanceState);
}
@Override

View File

@ -41,8 +41,9 @@ import org.mariotaku.twidere.util.MultiSelectManager;
public class BaseSupportFragment extends Fragment implements IBaseFragment, Constants {
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
public final void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
onBaseViewCreated(view, savedInstanceState);
requestFitSystemWindows();
}
@ -136,6 +137,11 @@ public class BaseSupportFragment extends Fragment implements IBaseFragment, Cons
}
}
@Override
public void onBaseViewCreated(View view, Bundle savedInstanceState) {
}
@Override
public void onDetach() {
super.onDetach();

View File

@ -137,6 +137,11 @@ public class BaseSupportListFragment extends ListFragment implements IBaseFragme
}
}
@Override
public void onBaseViewCreated(View view, Bundle savedInstanceState) {
}
protected void fitSystemWindows(Rect insets) {
Utils.makeListFragmentFitsSystemWindows(this, insets);
}
@ -205,8 +210,9 @@ public class BaseSupportListFragment extends ListFragment implements IBaseFragme
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
public final void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
onBaseViewCreated(view, savedInstanceState);
requestFitSystemWindows();
}

View File

@ -35,7 +35,9 @@ import android.support.v4.content.Loader;
import android.support.v4.util.Pair;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
@ -69,7 +71,7 @@ import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.support.BaseSupportActivity;
import org.mariotaku.twidere.activity.support.ImagePickerActivity;
import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter;
import org.mariotaku.twidere.adapter.DirectMessagesConversationAdapter;
import org.mariotaku.twidere.adapter.MessageConversationAdapter;
import org.mariotaku.twidere.adapter.SimpleParcelableUsersAdapter;
import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter.MenuButtonClickListener;
import org.mariotaku.twidere.app.TwidereApplication;
@ -99,7 +101,6 @@ import java.util.List;
import java.util.Locale;
import static org.mariotaku.twidere.util.Utils.buildDirectMessageConversationUri;
import static org.mariotaku.twidere.util.Utils.configBaseCardAdapter;
import static org.mariotaku.twidere.util.Utils.showOkMessage;
public class DirectMessagesConversationFragment extends BaseSupportFragment implements
@ -115,7 +116,8 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl
private SharedPreferences mPreferences;
private SharedPreferences mMessageDrafts;
private ListView mMessagesListView, mUsersSearchList;
private RecyclerView mMessagesListView;
private ListView mUsersSearchList;
private EditText mEditText;
private StatusTextCountView mTextCountView;
private View mSendButton;
@ -136,9 +138,8 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl
private boolean mLoadMoreAutomatically;
private String mImageUri;
private Locale mLocale;
private DirectMessagesConversationAdapter mAdapter;
private MessageConversationAdapter mAdapter;
private SimpleParcelableUsersAdapter mUsersSearchAdapter;
private ParcelableAccount mAccount;
@ -191,6 +192,9 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final View view = getView();
if (view == null) throw new AssertionError();
final Context viewContext = view.getContext();
setHasOptionsMenu(true);
final BaseSupportActivity activity = (BaseSupportActivity) getActivity();
final ActionBar actionBar = activity.getSupportActionBar();
@ -203,7 +207,7 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl
mUserQuery = (EditText) actionBarView.findViewById(R.id.user_query);
mQueryButton = actionBarView.findViewById(R.id.query_button);
final List<ParcelableAccount> accounts = ParcelableAccount.getAccountsList(activity, false);
final AccountsSpinnerAdapter accountsSpinnerAdapter = new AccountsSpinnerAdapter(actionBar.getThemedContext(), R.layout.spinner_item_account_icon);
final AccountsSpinnerAdapter accountsSpinnerAdapter = new AccountsSpinnerAdapter(mAccountSpinner.getContext(), R.layout.spinner_item_account_icon);
accountsSpinnerAdapter.setDropDownViewResource(R.layout.list_item_user);
accountsSpinnerAdapter.addAll(accounts);
mAccountSpinner.setAdapter(accountsSpinnerAdapter);
@ -231,14 +235,12 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl
mImageLoader = TwidereApplication.getInstance(getActivity()).getImageLoaderWrapper();
mTwitterWrapper = getTwitterWrapper();
mValidator = new TwidereValidator(getActivity());
mLocale = getResources().getConfiguration().locale;
mAdapter = new DirectMessagesConversationAdapter(getActivity());
mAdapter = new MessageConversationAdapter(getActivity());
final LinearLayoutManager layoutManager = new LinearLayoutManager(viewContext);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
layoutManager.setStackFromEnd(true);
mMessagesListView.setLayoutManager(layoutManager);
mMessagesListView.setAdapter(mAdapter);
mMessagesListView.setDivider(null);
mMessagesListView.setSelector(android.R.color.transparent);
mMessagesListView.setFastScrollEnabled(mPreferences.getBoolean(KEY_FAST_SCROLL_THUMB, false));
mMessagesListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
mMessagesListView.setStackFromBottom(true);
mUsersSearchAdapter = new SimpleParcelableUsersAdapter(activity);
mUsersSearchList.setAdapter(mUsersSearchAdapter);
@ -425,10 +427,9 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl
@Override
protected void fitSystemWindows(Rect insets) {
// final View view = getView();
// if (view != null) {
// view.setPadding(insets.left, view.getPaddingTop(), insets.right, insets.bottom);
// }
final View view = getView();
if (view == null) return;
view.setPadding(insets.left, insets.top, insets.right, insets.bottom);
}
@Override
@ -453,12 +454,12 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl
@Override
public void onLoaderReset(final Loader<Cursor> loader) {
mAdapter.swapCursor(null);
mAdapter.setCursor(null);
}
@Override
public void onLoadFinished(final Loader<Cursor> loader, final Cursor cursor) {
mAdapter.swapCursor(cursor);
mAdapter.setCursor(cursor);
}
@Override
@ -526,7 +527,6 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl
@Override
public void onResume() {
super.onResume();
configBaseCardAdapter(getActivity(), mAdapter);
final String previewScaleType = Utils.getNonEmptyString(mPreferences, KEY_MEDIA_PREVIEW_STYLE,
ScaleType.CENTER_CROP.name());
mAdapter.setImagePreviewScaleType(previewScaleType);
@ -591,13 +591,13 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl
}
@Override
public void onViewCreated(final View view, final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
public void onBaseViewCreated(final View view, final Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mUsersSearchProgress = view.findViewById(R.id.users_search_progress);
mUsersSearchList = (ListView) view.findViewById(R.id.users_search_list);
mUsersSearchEmpty = view.findViewById(R.id.users_search_empty);
mUsersSearchEmptyText = (TextView) view.findViewById(R.id.users_search_empty_text);
mMessagesListView = (ListView) view.findViewById(android.R.id.list);
mMessagesListView = (RecyclerView) view.findViewById(R.id.recycler_view);
final View inputSendContainer = view.findViewById(R.id.input_send_container);
mConversationContainer = view.findViewById(R.id.conversation_container);
mRecipientSelectorContainer = view.findViewById(R.id.recipient_selector_container);

View File

@ -22,18 +22,24 @@ package org.mariotaku.twidere.fragment.support;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.util.LongSparseArray;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;
import android.view.ViewGroup;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
@ -42,14 +48,20 @@ import org.mariotaku.querybuilder.Columns.Column;
import org.mariotaku.querybuilder.Expression;
import org.mariotaku.querybuilder.RawItemArray;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.DirectMessageEntriesAdapter;
import org.mariotaku.twidere.adapter.MessageEntriesAdapter;
import org.mariotaku.twidere.adapter.MessageEntriesAdapter.DirectMessageEntry;
import org.mariotaku.twidere.adapter.MessageEntriesAdapter.MessageEntriesAdapterListener;
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
import org.mariotaku.twidere.task.TwidereAsyncTask;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.MultiSelectManager;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.content.SupportFragmentReloadCursorObserver;
import org.mariotaku.twidere.util.message.TaskStateChangedEvent;
@ -57,51 +69,57 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import static org.mariotaku.twidere.util.Utils.configBaseCardAdapter;
import static org.mariotaku.twidere.util.Utils.getActivatedAccountIds;
import static org.mariotaku.twidere.util.Utils.getNewestMessageIdsFromDatabase;
import static org.mariotaku.twidere.util.Utils.getOldestMessageIdsFromDatabase;
import static org.mariotaku.twidere.util.Utils.openDirectMessagesConversation;
import static org.mariotaku.twidere.util.Utils.openMessageConversation;
public class DirectMessagesFragment extends BasePullToRefreshListFragment implements LoaderCallbacks<Cursor> {
public class DirectMessagesFragment extends BaseSupportFragment implements LoaderCallbacks<Cursor>, RefreshScrollTopInterface, OnRefreshListener, MessageEntriesAdapterListener {
private final SupportFragmentReloadCursorObserver mReloadContentObserver = new SupportFragmentReloadCursorObserver(
this, 0, this);
private MultiSelectManager mMultiSelectManager;
private SharedPreferences mPreferences;
private ListView mListView;
private DirectMessageEntriesAdapter mAdapter;
private int mFirstVisibleItem;
private final LongSparseArray<Set<Long>> mUnreadCountsToRemove = new LongSparseArray<>();
private final Set<Integer> mReadPositions = Collections.synchronizedSet(new HashSet<Integer>());
private MultiSelectManager mMultiSelectManager;
private SharedPreferences mPreferences;
private RecyclerView mRecyclerView;
private MessageEntriesAdapter mAdapter;
private int mFirstVisibleItem;
private RemoveUnreadCountsTask mRemoveUnreadCountsTask;
@Override
public DirectMessageEntriesAdapter getListAdapter() {
return (DirectMessageEntriesAdapter) super.getListAdapter();
}
private SwipeRefreshLayout mSwipeRefreshLayout;
private View mContentView;
private View mProgressContainer;
private LinearLayoutManager mLayoutManager;
public final LongSparseArray<Set<Long>> getUnreadCountsToRemove() {
return mUnreadCountsToRemove;
}
@Subscribe
public void notifyTaskStateChanged(TaskStateChangedEvent event) {
// updateRefreshState();
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
super.onActivityCreated(savedInstanceState);
mMultiSelectManager = getMultiSelectManager();
mAdapter = new DirectMessageEntriesAdapter(getActivity());
setListAdapter(mAdapter);
mListView = getListView();
mListView.setDivider(null);
getLoaderManager().initLoader(0, null, this);
setListShown(false);
public void onEntryClick(int position, DirectMessageEntry entry) {
Utils.openMessageConversation(getActivity(), entry.account_id, entry.conversation_id);
}
@Override
public void onRefresh() {
}
private void setListShown(boolean shown) {
mProgressContainer.setVisibility(shown ? View.GONE : View.VISIBLE);
mSwipeRefreshLayout.setVisibility(shown ? View.VISIBLE : View.GONE);
}
@Override
public void onBaseViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mContentView = view.findViewById(R.id.fragment_content);
mProgressContainer = view.findViewById(R.id.progress_container);
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_layout);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
}
@Override
@ -110,105 +128,70 @@ public class DirectMessagesFragment extends BasePullToRefreshListFragment implem
final long account_id = getAccountId();
final long[] account_ids = account_id > 0 ? new long[]{account_id} : getActivatedAccountIds(getActivity());
final boolean no_account_selected = account_ids.length == 0;
setEmptyText(no_account_selected ? getString(R.string.no_account_selected) : null);
if (!no_account_selected) {
getListView().setEmptyView(null);
}
// setEmptyText(no_account_selected ? getString(R.string.no_account_selected) : null);
// if (!no_account_selected) {
// getListView().setEmptyView(null);
// }
final Expression account_where = Expression.in(new Column(Statuses.ACCOUNT_ID), new RawItemArray(account_ids));
return new CursorLoader(getActivity(), uri, null, account_where.getSQL(), null, null);
}
@Override
public void onListItemClick(final ListView l, final View v, final int position, final long id) {
if (mMultiSelectManager.isActive()) return;
final int pos = position - l.getHeaderViewsCount();
final long conversationId = mAdapter.getConversationId(pos);
final long accountId = mAdapter.getAccountId(pos);
mReadPositions.add(pos);
removeUnreadCounts();
if (conversationId > 0 && accountId > 0) {
openDirectMessagesConversation(getActivity(), accountId, conversationId);
}
}
@Override
public void onLoaderReset(final Loader<Cursor> loader) {
mAdapter.changeCursor(null);
}
@Override
public void onLoadFinished(final Loader<Cursor> loader, final Cursor cursor) {
if (getActivity() == null) return;
mFirstVisibleItem = -1;
mAdapter.changeCursor(cursor);
mAdapter.setShowAccountColor(getActivatedAccountIds(getActivity()).length > 1);
mAdapter.setCursor(cursor);
// mAdapter.setShowAccountColor(getActivatedAccountIds(getActivity()).length > 1);
setListShown(true);
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case MENU_COMPOSE: {
openDirectMessagesConversation(getActivity(), -1, -1);
break;
}
}
return super.onOptionsItemSelected(item);
public void onLoaderReset(final Loader<Cursor> loader) {
mAdapter.setCursor(null);
}
// @Override
// public void onListItemClick(final ListView l, final View v, final int position, final long id) {
// if (mMultiSelectManager.isActive()) return;
// final int pos = position - l.getHeaderViewsCount();
// final long conversationId = mAdapter.getConversationId(pos);
// final long accountId = mAdapter.getAccountId(pos);
// mReadPositions.add(pos);
// removeUnreadCounts();
// if (conversationId > 0 && accountId > 0) {
// openMessageConversation(getActivity(), accountId, conversationId);
// }
// }
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_recycler_view, container, false);
}
@Override
public void onRefresh() {
if (isRefreshing()) return;
new TwidereAsyncTask<Void, Void, long[][]>() {
@Override
protected long[][] doInBackground(final Void... params) {
final long[][] result = new long[2][];
result[0] = getActivatedAccountIds(getActivity());
result[1] = getNewestMessageIdsFromDatabase(getActivity(), DirectMessages.Inbox.CONTENT_URI);
return result;
}
@Override
protected void onPostExecute(final long[][] result) {
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (twitter == null) return;
twitter.getReceivedDirectMessagesAsync(result[0], null, result[1]);
twitter.getSentDirectMessagesAsync(result[0], null, null);
}
}.executeTask();
}
@Override
public void onResume() {
super.onResume();
mListView.setFastScrollEnabled(mPreferences.getBoolean(KEY_FAST_SCROLL_THUMB, false));
configBaseCardAdapter(getActivity(), mAdapter);
}
@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) {
switch (scrollState) {
case SCROLL_STATE_FLING:
case SCROLL_STATE_TOUCH_SCROLL: {
break;
}
case SCROLL_STATE_IDLE: {
for (int i = mListView.getFirstVisiblePosition(), j = mListView.getLastVisiblePosition(); i < j; i++) {
mReadPositions.add(i);
}
removeUnreadCounts();
break;
}
}
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final View view = getView();
if (view == null) throw new AssertionError();
mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final Context viewContext = view.getContext();
mMultiSelectManager = getMultiSelectManager();
mAdapter = new MessageEntriesAdapter(viewContext);
mAdapter.setListener(this);
mLayoutManager = new LinearLayoutManager(viewContext);
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mSwipeRefreshLayout.setOnRefreshListener(this);
mSwipeRefreshLayout.setColorSchemeColors(ThemeUtils.getUserAccentColor(viewContext));
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
final DividerItemDecoration itemDecoration = new DividerItemDecoration(viewContext, mLayoutManager.getOrientation());
final Resources res = viewContext.getResources();
final int decorPaddingLeft = res.getDimensionPixelSize(R.dimen.element_spacing_normal) * 3
+ res.getDimensionPixelSize(R.dimen.icon_size_status_profile_image);
itemDecoration.setPadding(decorPaddingLeft, 0, 0, 0);
mRecyclerView.addItemDecoration(itemDecoration);
getLoaderManager().initLoader(0, null, this);
setListShown(false);
}
@Override
@ -220,6 +203,55 @@ public class DirectMessagesFragment extends BasePullToRefreshListFragment implem
bus.register(this);
}
// @Override
// public void onRefresh() {
// if (isRefreshing()) return;
// new TwidereAsyncTask<Void, Void, long[][]>() {
//
// @Override
// protected long[][] doInBackground(final Void... params) {
// final long[][] result = new long[2][];
// result[0] = getActivatedAccountIds(getActivity());
// result[1] = getNewestMessageIdsFromDatabase(getActivity(), DirectMessages.Inbox.CONTENT_URI);
// return result;
// }
//
// @Override
// protected void onPostExecute(final long[][] result) {
// final AsyncTwitterWrapper twitter = getTwitterWrapper();
// if (twitter == null) return;
// twitter.getReceivedDirectMessagesAsync(result[0], null, result[1]);
// twitter.getSentDirectMessagesAsync(result[0], null, null);
// }
//
// }.executeTask();
// }
// @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) {
// switch (scrollState) {
// case SCROLL_STATE_FLING:
// case SCROLL_STATE_TOUCH_SCROLL: {
// break;
// }
// case SCROLL_STATE_IDLE: {
// for (int i = mRecyclerView.getFirstVisiblePosition(), j = mRecyclerView.getLastVisiblePosition(); i < j; i++) {
// mReadPositions.add(i);
// }
// removeUnreadCounts();
// break;
// }
// }
// }
@Override
public void onStop() {
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
@ -229,6 +261,17 @@ public class DirectMessagesFragment extends BasePullToRefreshListFragment implem
super.onStop();
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case MENU_COMPOSE: {
openMessageConversation(getActivity(), -1, -1);
break;
}
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean scrollToStart() {
final AsyncTwitterWrapper twitter = getTwitterWrapper();
@ -236,47 +279,52 @@ public class DirectMessagesFragment extends BasePullToRefreshListFragment implem
if (twitter != null && tabPosition >= 0) {
twitter.clearUnreadCountAsync(tabPosition);
}
return super.scrollToStart();
mRecyclerView.smoothScrollToPosition(0);
return true;
}
@Override
public boolean triggerRefresh() {
return true;
}
@Override
public void setUserVisibleHint(final boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
updateRefreshState();
// updateRefreshState();
}
}
@Override
protected void fitSystemWindows(Rect insets) {
super.fitSystemWindows(insets);
mContentView.setPadding(insets.left, insets.top, insets.right, insets.bottom);
}
// @Override
// protected void onListTouched() {
// final AsyncTwitterWrapper twitter = getTwitterWrapper();
// if (twitter != null) {
// twitter.clearNotificationAsync(NOTIFICATION_ID_DIRECT_MESSAGES, getAccountId());
// }
// }
protected long getAccountId() {
final Bundle args = getArguments();
return args != null ? args.getLong(EXTRA_ACCOUNT_ID, -1) : -1;
}
@Override
protected void onListTouched() {
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (twitter != null) {
twitter.clearNotificationAsync(NOTIFICATION_ID_DIRECT_MESSAGES, getAccountId());
}
}
// @Override
// protected void onReachedBottom() {
// loadMoreMessages();
// }
@Subscribe
public void notifyTaskStateChanged(TaskStateChangedEvent event) {
updateRefreshState();
}
@Override
protected void onReachedBottom() {
loadMoreMessages();
}
protected void updateRefreshState() {
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (twitter == null || !getUserVisibleHint()) return;
setRefreshing(twitter.isReceivedDirectMessagesRefreshing() || twitter.isSentDirectMessagesRefreshing());
}
// protected void updateRefreshState() {
// final AsyncTwitterWrapper twitter = getTwitterWrapper();
// if (twitter == null || !getUserVisibleHint()) return;
// setRefreshing(twitter.isReceivedDirectMessagesRefreshing() || twitter.isSentDirectMessagesRefreshing());
// }
private void addReadPosition(final int firstVisibleItem) {
if (mFirstVisibleItem != firstVisibleItem) {
@ -295,29 +343,33 @@ public class DirectMessagesFragment extends BasePullToRefreshListFragment implem
counts.add(id);
}
}
//
// private void loadMoreMessages() {
// if (isRefreshing()) return;
// new TwidereAsyncTask<Void, Void, long[][]>() {
//
// @Override
// protected long[][] doInBackground(final Void... params) {
// final long[][] result = new long[3][];
// result[0] = getActivatedAccountIds(getActivity());
// result[1] = getOldestMessageIdsFromDatabase(getActivity(), DirectMessages.Inbox.CONTENT_URI);
// result[2] = getOldestMessageIdsFromDatabase(getActivity(), DirectMessages.Outbox.CONTENT_URI);
// return result;
// }
//
// @Override
// protected void onPostExecute(final long[][] result) {
// final AsyncTwitterWrapper twitter = getTwitterWrapper();
// if (twitter == null) return;
// twitter.getReceivedDirectMessagesAsync(result[0], result[1], null);
// twitter.getSentDirectMessagesAsync(result[0], result[2], null);
// }
//
// }.executeTask();
// }
private void loadMoreMessages() {
if (isRefreshing()) return;
new TwidereAsyncTask<Void, Void, long[][]>() {
@Override
protected long[][] doInBackground(final Void... params) {
final long[][] result = new long[3][];
result[0] = getActivatedAccountIds(getActivity());
result[1] = getOldestMessageIdsFromDatabase(getActivity(), DirectMessages.Inbox.CONTENT_URI);
result[2] = getOldestMessageIdsFromDatabase(getActivity(), DirectMessages.Outbox.CONTENT_URI);
return result;
}
@Override
protected void onPostExecute(final long[][] result) {
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (twitter == null) return;
twitter.getReceivedDirectMessagesAsync(result[0], result[1], null);
twitter.getSentDirectMessagesAsync(result[0], result[2], null);
}
}.executeTask();
private MessageEntriesAdapter getAdapter() {
return mAdapter;
}
private void removeUnreadCounts() {
@ -329,19 +381,20 @@ public class DirectMessagesFragment extends BasePullToRefreshListFragment implem
static class RemoveUnreadCountsTask extends TwidereAsyncTask<Void, Void, Void> {
private final Set<Integer> read_positions;
private final DirectMessageEntriesAdapter adapter;
private final MessageEntriesAdapter adapter;
private final DirectMessagesFragment fragment;
RemoveUnreadCountsTask(final Set<Integer> read_positions, final DirectMessagesFragment fragment) {
this.read_positions = Collections.synchronizedSet(new HashSet<Integer>(read_positions));
this.read_positions = Collections.synchronizedSet(new HashSet<>(read_positions));
this.fragment = fragment;
adapter = fragment.getListAdapter();
adapter = fragment.getAdapter();
}
@Override
protected Void doInBackground(final Void... params) {
for (final int pos : read_positions) {
final long id = adapter.getConversationId(pos), account_id = adapter.getAccountId(pos);
final DirectMessageEntry entry = adapter.getEntry(pos);
final long id = entry.conversation_id, account_id = entry.account_id;
fragment.addUnreadCountsToRemove(account_id, id);
}
return null;

View File

@ -90,8 +90,8 @@ public class QuickMenuFragment extends BaseSupportFragment {
}
@Override
public void onViewCreated(final View view, final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
public void onBaseViewCreated(final View view, final Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mListView = (ListView) view.findViewById(android.R.id.list);
mSlidingUpPanel = (SlidingUpPanelLayout) view.findViewById(R.id.activities_drawer);
mActivitiesConfigButton = (ImageButton) view.findViewById(R.id.activities_config_button);

View File

@ -151,8 +151,8 @@ public class SearchFragment extends BaseSupportFragment implements RefreshScroll
}
@Override
public void onViewCreated(final View view, final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
public void onBaseViewCreated(final View view, final Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mViewPager = (ViewPager) view.findViewById(R.id.view_pager);
mPagerIndicator = (TabPagerIndicator) view.findViewById(R.id.view_pager_tabs);
}

View File

@ -290,8 +290,8 @@ public class StatusFragment extends BaseSupportFragment
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
public void onBaseViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mStatusContent = view.findViewById(R.id.status_content);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
mProgressContainer = view.findViewById(R.id.progress_container);

View File

@ -1086,11 +1086,11 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
}
@Override
public void onViewCreated(final View view, final Bundle savedInstanceState) {
public void onBaseViewCreated(final View view, final Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mHeaderDrawerLayout = (HeaderDrawerLayout) view.findViewById(R.id.user_profile_drawer);
final View headerView = mHeaderDrawerLayout.getHeader();
final View contentView = mHeaderDrawerLayout.getContent();
super.onViewCreated(view, savedInstanceState);
mCardContent = headerView.findViewById(R.id.card_content);
mErrorRetryContainer = headerView.findViewById(R.id.error_retry_container);
mProgressContainer = headerView.findViewById(R.id.progress_container);

View File

@ -479,8 +479,8 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList
}
@Override
public void onViewCreated(final View view, final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
public void onBaseViewCreated(final View view, final Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mHeaderDrawerLayout = (HeaderDrawerLayout) view.findViewById(R.id.details_container);
mErrorRetryContainer = view.findViewById(R.id.error_retry_container);
mProgressContainer = view.findViewById(R.id.progress_container);

View File

@ -90,8 +90,8 @@ public class UserListsFragment extends BaseSupportFragment implements RefreshScr
}
@Override
public void onViewCreated(final View view, final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
public void onBaseViewCreated(final View view, final Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mViewPager = (ViewPager) view.findViewById(R.id.view_pager);
mPagerIndicator = (TabPagerIndicator) view.findViewById(R.id.view_pager_tabs);
}

View File

@ -114,8 +114,8 @@ public class UserMediaTimelineFragment extends BaseSupportFragment
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
public void onBaseViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mProgressContainer = view.findViewById(R.id.progress_container);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
}

View File

@ -27,6 +27,7 @@ import android.support.v7.internal.view.SupportMenuInflater;
import android.support.v7.widget.ActionMenuView;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MotionEvent;
@ -42,6 +43,8 @@ import org.mariotaku.twidere.util.accessor.ViewAccessor;
import org.mariotaku.twidere.view.iface.IExtendedView;
import org.mariotaku.twidere.view.iface.IExtendedView.TouchInterceptor;
import java.lang.reflect.InvocationTargetException;
import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
import static org.mariotaku.twidere.util.Utils.formatToLongTimeString;
import static org.mariotaku.twidere.util.Utils.getDefaultTextSize;
@ -75,9 +78,16 @@ public class ThemePreviewPreference extends Preference implements Constants, OnS
final int themeResource = ThemeUtils.getThemeResource(context);
final Context theme = new ContextThemeWrapper(context, themeResource);
final LayoutInflater inflater = LayoutInflater.from(theme);
final View view = inflater.inflate(R.layout.theme_preview, parent, false);
setPreviewView(theme, view.findViewById(R.id.theme_preview_content), themeResource);
return view;
try {
final View view = inflater.inflate(R.layout.theme_preview, parent, false);
setPreviewView(theme, view.findViewById(R.id.theme_preview_content), themeResource);
return view;
} catch (InflateException e) {
if (e.getCause() instanceof InvocationTargetException) {
e.getCause().getCause().printStackTrace();
}
throw e;
}
}
private static void setPreviewView(final Context context, final View view, final int themeRes) {

View File

@ -26,6 +26,10 @@ import android.graphics.Color;
*/
public class ColorUtils {
public static int getYIQLuminance(int color) {
final int r = Color.red(color), g = Color.green(color), b = Color.blue(color);
return (r * 299 + g * 587 + b * 114) / 1000;
}
public static void colorToYIQ(int color, int[] yiq) {
final int r = Color.red(color), g = Color.green(color), b = Color.blue(color);

View File

@ -2910,8 +2910,8 @@ public final class Utils implements Constants, TwitterConstants {
return LINK_HANDLER_URI_MATCHER.match(uri);
}
public static void openDirectMessagesConversation(final FragmentActivity activity, final long accountId,
final long recipientId) {
public static void openMessageConversation(final FragmentActivity activity, final long accountId,
final long recipientId) {
if (activity == null) return;
final Uri.Builder builder = new Uri.Builder();
builder.scheme(SCHEME_TWIDERE);

View File

@ -1,4 +1,4 @@
package org.mariotaku.twidere.util;
package org.mariotaku.twidere.util.accessor;
import android.annotation.TargetApi;
import android.app.Activity;

View File

@ -21,21 +21,18 @@ package org.mariotaku.twidere.util.accessor;
import android.annotation.TargetApi;
import android.content.res.ColorStateList;
import android.graphics.Outline;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.CompoundButton;
import android.widget.ProgressBar;
public final class ViewAccessor {
public static void enableHwAccelIfNecessary(final View view) {
if (ViewCompat.getLayerType(view) != ViewCompat.LAYER_TYPE_HARDWARE) {
ViewCompat.setLayerType(view, ViewCompat.LAYER_TYPE_HARDWARE, null);
}
}
public static boolean isInLayout(View view) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
return false;
@ -63,11 +60,22 @@ public final class ViewAccessor {
ViewAccessorL.setButtonTintList(view, list);
}
public static void setClipToOutline(View view, boolean clipToOutline) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
ViewAccessorL.setClipToOutline(view, clipToOutline);
}
public static void setIndeterminateTintList(ProgressBar view, ColorStateList list) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
ViewAccessorL.setIndeterminateTintList(view, list);
}
public static void setOutlineProvider(View view, ViewOutlineProviderCompat outlineProvider) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
ViewAccessorL.setOutlineProvider(view, outlineProvider);
}
public static void setProgressBackgroundTintList(ProgressBar view, ColorStateList list) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
ViewAccessorL.setProgressBackgroundTintList(view, list);
@ -96,6 +104,16 @@ public final class ViewAccessor {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
static class ViewAccessorL {
public static void setClipToOutline(View view, boolean clipToOutline) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
view.setClipToOutline(clipToOutline);
}
public static void setOutlineProvider(View view, ViewOutlineProviderCompat outlineProvider) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
view.setOutlineProvider(new ViewOutlineProviderL(outlineProvider));
}
static void setBackgroundTintList(final View view, final ColorStateList list) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
view.setBackgroundTintList(list);
@ -121,4 +139,146 @@ public final class ViewAccessor {
view.setProgressTintList(list);
}
}
/**
* Interface by which a View builds its {@link org.mariotaku.twidere.util.accessor.ViewAccessor.OutlineCompat}, used for shadow casting and clipping.
*/
public static abstract class ViewOutlineProviderCompat {
/**
* Called to get the provider to populate the Outline.
* <p/>
* This method will be called by a View when its owned Drawables are invalidated, when the
* View's size changes, or if {@link View#invalidateOutline()} is called
* explicitly.
* <p/>
* The input outline is empty and has an alpha of <code>1.0f</code>.
*
* @param view The view building the outline.
* @param outline The empty outline to be populated.
*/
public abstract void getOutline(View view, OutlineCompat outline);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static class ViewOutlineProviderL extends ViewOutlineProvider {
private final ViewOutlineProviderCompat providerCompat;
ViewOutlineProviderL(ViewOutlineProviderCompat providerCompat) {
this.providerCompat = providerCompat;
}
@Override
public void getOutline(View view, Outline outline) {
providerCompat.getOutline(view, new OutlineL(outline));
}
}
@SuppressWarnings("unused")
public static abstract class OutlineCompat {
public abstract float getAlpha();
public abstract boolean isEmpty();
public abstract void set(Outline src);
public abstract void setAlpha(float alpha);
public abstract void setConvexPath(Path convexPath);
public abstract void setOval(int left, int top, int right, int bottom);
public abstract void setOval(Rect rect);
public abstract void setEmpty();
public abstract boolean canClip();
public abstract void setRect(int left, int top, int right, int bottom);
public abstract void setRect(Rect rect);
public abstract void setRoundRect(int left, int top, int right, int bottom, float radius);
public abstract void setRoundRect(Rect rect, float radius);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static class OutlineL extends OutlineCompat {
private final Outline outline;
@Override
public void setEmpty() {
outline.setEmpty();
}
@Override
public boolean canClip() {
return outline.canClip();
}
@Override
public void setRect(int left, int top, int right, int bottom) {
outline.setRect(left, top, right, bottom);
}
@Override
public void setOval(int left, int top, int right, int bottom) {
outline.setOval(left, top, right, bottom);
}
@Override
public void setConvexPath(Path convexPath) {
outline.setConvexPath(convexPath);
}
@Override
public void setRect(Rect rect) {
outline.setRect(rect);
}
@Override
public void setOval(Rect rect) {
outline.setOval(rect);
}
@Override
public float getAlpha() {
return outline.getAlpha();
}
@Override
public void set(Outline src) {
outline.set(src);
}
@Override
public void setRoundRect(int left, int top, int right, int bottom, float radius) {
outline.setRoundRect(left, top, right, bottom, radius);
}
@Override
public void setRoundRect(Rect rect, float radius) {
outline.setRoundRect(rect, radius);
}
@Override
public boolean isEmpty() {
return outline.isEmpty();
}
@Override
public void setAlpha(float alpha) {
outline.setAlpha(alpha);
}
public OutlineL(Outline outline) {
this.outline = outline;
}
}
}

View File

@ -1,4 +1,4 @@
package org.mariotaku.twidere.util;
package org.mariotaku.twidere.util.accessor;
import android.annotation.TargetApi;
import android.os.Build;

View File

@ -24,14 +24,12 @@ import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
@ -39,6 +37,8 @@ import android.widget.ProgressBar;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.accessor.ViewAccessor;
import org.mariotaku.twidere.util.accessor.ViewAccessor.OutlineCompat;
import org.mariotaku.twidere.util.accessor.ViewAccessor.ViewOutlineProviderCompat;
import org.mariotaku.twidere.view.helper.PressElevateViewHelper;
import org.mariotaku.twidere.view.iface.IHomeActionButton;
@ -63,7 +63,7 @@ public class HomeActionButton extends FrameLayout implements IHomeActionButton {
inflate(ThemeUtils.getActionBarContext(context), R.layout.action_item_home_actions, this);
mIconView = (ImageView) findViewById(android.R.id.icon);
mProgressBar = (ProgressBar) findViewById(android.R.id.progress);
setOutlineProvider(new HomeActionButtonOutlineProvider());
ViewAccessor.setOutlineProvider(this, new HomeActionButtonOutlineProvider());
setClipToOutline(true);
setButtonColor(Color.WHITE);
}
@ -119,9 +119,10 @@ public class HomeActionButton extends FrameLayout implements IHomeActionButton {
mHelper.updateButtonState();
}
private static class HomeActionButtonOutlineProvider extends ViewOutlineProvider {
private static class HomeActionButtonOutlineProvider extends ViewOutlineProviderCompat {
@Override
public void getOutline(View view, Outline outline) {
public void getOutline(View view, OutlineCompat outline) {
final int width = view.getWidth(), height = view.getHeight();
final int size = Math.min(width, height);
final int left = (width - size) / 2, top = (height - size) / 2;

View File

@ -30,7 +30,6 @@ import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
@ -47,10 +46,12 @@ import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.util.accessor.ViewAccessor;
import org.mariotaku.twidere.util.accessor.ViewAccessor.OutlineCompat;
import org.mariotaku.twidere.util.accessor.ViewAccessor.ViewOutlineProviderCompat;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -269,7 +270,6 @@ public class ShapedImageView extends ImageView {
@Override
protected void onDraw(@NonNull Canvas canvas) {
mDestination.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(),
getHeight() - getPaddingBottom());
@ -381,6 +381,7 @@ public class ShapedImageView extends ImageView {
}
private void drawBorder(@NonNull final Canvas canvas, @NonNull final RectF dest) {
if (mBorderColors == null) return;
final RectF transitionSrc = mTransitionSource, transitionDst = mTransitionDestination;
final float strokeWidth;
if (transitionSrc != null && transitionDst != null) {
@ -421,10 +422,8 @@ public class ShapedImageView extends ImageView {
}
private void initOutlineProvider() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setClipToOutline(true);
setOutlineProvider(new CircularOutlineProvider());
}
ViewAccessor.setClipToOutline(this, true);
ViewAccessor.setOutlineProvider(this, new CircularOutlineProvider());
}
private void setBorderColorsInternal(int alpha, int... colors) {
@ -437,7 +436,6 @@ public class ShapedImageView extends ImageView {
private void updateBorderShader() {
final int[] colors = mBorderColors;
if (colors == null || colors.length == 0) {
mBorderPaint.setShader(null);
mBorderAlpha = 0;
return;
}
@ -500,10 +498,9 @@ public class ShapedImageView extends ImageView {
public @interface ShapeStyle {
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static class CircularOutlineProvider extends ViewOutlineProvider {
private static class CircularOutlineProvider extends ViewOutlineProviderCompat {
@Override
public void getOutline(View view, Outline outline) {
public void getOutline(View view, OutlineCompat outline) {
final int contentLeft = view.getPaddingLeft(), contentTop = view.getPaddingTop(),
contentRight = view.getWidth() - view.getPaddingRight(),
contentBottom = view.getHeight() - view.getPaddingBottom();

View File

@ -1,74 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder;
import android.graphics.LightingColorFilter;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.mariotaku.messagebubbleview.library.MessageBubbleView;
import org.mariotaku.twidere.R;
public class DirectMessageConversationViewHolder extends ViewListHolder {
public final TextView text, time;
public final ImageView media_preview;
public final ViewGroup media_preview_container;
public final ProgressBar media_preview_progress;
private final MessageBubbleView message_content;
private float text_size;
public DirectMessageConversationViewHolder(final View view) {
super(view);
message_content = (MessageBubbleView) findViewById(R.id.message_content);
text = (TextView) findViewById(R.id.text);
time = (TextView) findViewById(R.id.time);
media_preview = (ImageView) findViewById(R.id.media_preview);
media_preview_progress = (ProgressBar) findViewById(R.id.media_preview_progress);
media_preview_container = (ViewGroup) findViewById(R.id.media_preview_container);
}
public void setTextSize(final float text_size) {
if (this.text_size != text_size) {
this.text_size = text_size;
text.setTextSize(text_size);
time.setTextSize(text_size * 0.75f);
}
}
public void setOutgoing(boolean isOutgoing) {
message_content.setCaretPosition(isOutgoing ? MessageBubbleView.BOTTOM_RIGHT : MessageBubbleView.TOP_LEFT);
final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) message_content.getLayoutParams();
lp.gravity = isOutgoing ? Gravity.RIGHT : Gravity.LEFT;
message_content.setLayoutParams(lp);
if (isOutgoing) {
message_content.setBubbleColorFilter(new LightingColorFilter(0xFF90B096, 0x00203324));
} else {
message_content.clearBubbleColorFilter();
}
}
}

View File

@ -1,81 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.ShortTimeView;
public class DirectMessageEntryViewHolder extends ViewListHolder {
public final ImageView profile_image;
public final TextView name, screen_name, text;
public final ShortTimeView time;
private float text_size;
private boolean account_color_enabled;
private final boolean is_rtl;
public DirectMessageEntryViewHolder(final View view) {
super(view);
final Context context = view.getContext();
profile_image = (ImageView) findViewById(R.id.profile_image);
name = (TextView) findViewById(R.id.name);
screen_name = (TextView) findViewById(R.id.screen_name);
text = (TextView) findViewById(R.id.text);
time = (ShortTimeView) findViewById(R.id.time);
is_rtl = Utils.isRTL(context);
}
public void setAccountColor(final int color) {
// content.drawEnd(account_color_enabled ? color : Color.TRANSPARENT);
}
public void setAccountColorEnabled(final boolean enabled) {
if (account_color_enabled == enabled) return;
account_color_enabled = enabled;
if (!account_color_enabled) {
// content.drawEnd(Color.TRANSPARENT);
}
}
public void setIsOutgoing(final boolean is_outgoing) {
if (is_rtl) {
time.setCompoundDrawablesWithIntrinsicBounds(is_outgoing ? R.drawable.ic_indicator_sent : 0, 0, 0, 0);
} else {
time.setCompoundDrawablesWithIntrinsicBounds(0, 0, is_outgoing ? R.drawable.ic_indicator_sent : 0, 0);
}
}
public void setTextSize(final float text_size) {
if (this.text_size == text_size) return;
this.text_size = text_size;
text.setTextSize(text_size);
name.setTextSize(text_size);
}
public void setUserColor(final int color) {
// content.drawStart(color);
}
}

View File

@ -0,0 +1,118 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.Html;
import android.view.View;
import android.widget.TextView;
import org.mariotaku.messagebubbleview.library.MessageBubbleView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.MessageConversationAdapter;
import org.mariotaku.twidere.model.ParcelableDirectMessage.CursorIndices;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.util.ColorUtils;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.TwidereLinkify;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.CardMediaContainer;
public class MessageConversationViewHolder extends ViewHolder {
public final CardMediaContainer mediaContainer;
public final TextView text, time;
private final MessageBubbleView messageContent;
private final MessageConversationAdapter adapter;
private final int textColorPrimary, textColorPrimaryInverse, textColorSecondary, textColorSecondaryInverse;
private float textSize;
public MessageConversationViewHolder(final MessageConversationAdapter adapter, final View itemView) {
super(itemView);
this.adapter = adapter;
final Context context = itemView.getContext();
final TypedArray a = context.obtainStyledAttributes(new int[]{android.R.attr.textColorPrimary,
android.R.attr.textColorPrimaryInverse, android.R.attr.textColorSecondary,
android.R.attr.textColorSecondaryInverse});
textColorPrimary = a.getColor(0, 0);
textColorPrimaryInverse = a.getColor(1, 0);
textColorSecondary = a.getColor(2, 0);
textColorSecondaryInverse = a.getColor(3, 0);
messageContent = (MessageBubbleView) itemView.findViewById(R.id.message_content);
text = (TextView) itemView.findViewById(R.id.text);
time = (TextView) itemView.findViewById(R.id.time);
mediaContainer = (CardMediaContainer) itemView.findViewById(R.id.media_preview_container);
}
public void displayMessage(Cursor cursor, CursorIndices indices) {
final Context context = adapter.getContext();
final TwidereLinkify linkify = adapter.getLinkify();
final ImageLoaderWrapper loader = adapter.getImageLoader();
final long accountId = cursor.getLong(indices.account_id);
final long timestamp = cursor.getLong(indices.message_timestamp);
final ParcelableMedia[] media = ParcelableMedia.fromJSONString(cursor.getString(indices.media));
text.setText(Html.fromHtml(cursor.getString(indices.text)));
linkify.applyAllLinks(text, accountId, false);
text.setMovementMethod(null);
time.setText(Utils.formatToLongTimeString(context, timestamp));
mediaContainer.setVisibility(media != null && media.length > 0 ? View.VISIBLE : View.GONE);
mediaContainer.displayMedia(media, loader, accountId, null, null);
}
public void setMessageColor(int color) {
final ColorStateList colorStateList = ColorStateList.valueOf(color);
messageContent.setBubbleColor(colorStateList);
final int textLuminancePrimary = ColorUtils.getYIQLuminance(textColorPrimary);
final int textPrimaryDark, textPrimaryLight, textSecondaryDark, textSecondaryLight;
if (textLuminancePrimary < 128) {
textPrimaryDark = textColorPrimary;
textPrimaryLight = textColorPrimaryInverse;
textSecondaryDark = textColorSecondary;
textSecondaryLight = textColorSecondaryInverse;
} else {
textPrimaryDark = textColorPrimaryInverse;
textPrimaryLight = textColorPrimary;
textSecondaryDark = textColorSecondaryInverse;
textSecondaryLight = textColorSecondary;
}
final int textContrastPrimary = ColorUtils.getContrastYIQ(color, 192, textPrimaryDark, textPrimaryLight);
final int textContrastSecondary = ColorUtils.getContrastYIQ(color, 192, textSecondaryDark, textSecondaryLight);
text.setTextColor(textContrastPrimary);
text.setLinkTextColor(textContrastSecondary);
time.setTextColor(textContrastSecondary);
}
public void setTextSize(final float textSize) {
if (this.textSize != textSize) {
this.textSize = textSize;
text.setTextSize(textSize);
time.setTextSize(textSize * 0.75f);
}
}
}

View File

@ -0,0 +1,129 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder;
import android.content.Context;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.TextView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.MessageEntriesAdapter;
import org.mariotaku.twidere.fragment.support.DirectMessagesFragment;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.view.ShortTimeView;
import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
import static org.mariotaku.twidere.util.UserColorNameUtils.getUserNickname;
public class MessageEntryViewHolder extends ViewHolder implements OnClickListener {
private final MessageEntriesAdapter adapter;
public final ImageView profileImageView;
public final TextView nameView, screenNameView, textView;
public final ShortTimeView timeView;
private float text_size;
private boolean account_color_enabled;
public MessageEntryViewHolder(final MessageEntriesAdapter adapter, final View itemView) {
super(itemView);
this.adapter = adapter;
final Context context = itemView.getContext();
profileImageView = (ImageView) itemView.findViewById(R.id.profile_image);
nameView = (TextView) itemView.findViewById(R.id.name);
screenNameView = (TextView) itemView.findViewById(R.id.screen_name);
textView = (TextView) itemView.findViewById(R.id.text);
timeView = (ShortTimeView) itemView.findViewById(R.id.time);
itemView.setOnClickListener(this);
}
public void displayMessage(Cursor cursor) {
final Context context = adapter.getContext();
final ImageLoaderWrapper loader = adapter.getImageLoader();
final long accountId = cursor.getLong(ConversationEntries.IDX_ACCOUNT_ID);
final long conversationId = cursor.getLong(ConversationEntries.IDX_CONVERSATION_ID);
final long timestamp = cursor.getLong(ConversationEntries.IDX_MESSAGE_TIMESTAMP);
final boolean isOutgoing = cursor.getInt(ConversationEntries.IDX_IS_OUTGOING) == 1;
final String name = cursor.getString(ConversationEntries.IDX_NAME);
final String screenName = cursor.getString(ConversationEntries.IDX_SCREEN_NAME);
final String nick = getUserNickname(context, conversationId);
nameView.setText(TextUtils.isEmpty(nick) ? name : adapter.isNicknameOnly() ? nick : context.getString(
R.string.name_with_nickname, name, nick));
screenNameView.setText("@" + screenName);
textView.setText(toPlainText(cursor.getString(ConversationEntries.IDX_TEXT)));
timeView.setTime(timestamp);
setIsOutgoing(isOutgoing);
final String profileImage = cursor.getString(ConversationEntries.IDX_PROFILE_IMAGE_URL);
loader.displayProfileImage(profileImageView, profileImage);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.profile_image: {
break;
}
default: {
if (v == itemView) {
adapter.onMessageClick(getPosition());
}
break;
}
}
}
public void setAccountColor(final int color) {
// content.drawEnd(account_color_enabled ? color : Color.TRANSPARENT);
}
public void setAccountColorEnabled(final boolean enabled) {
if (account_color_enabled == enabled) return;
account_color_enabled = enabled;
if (!account_color_enabled) {
// content.drawEnd(Color.TRANSPARENT);
}
}
public void setIsOutgoing(final boolean is_outgoing) {
timeView.setCompoundDrawablesWithIntrinsicBounds(0, 0, is_outgoing ? R.drawable.ic_indicator_sent : 0, 0);
}
public void setTextSize(final float text_size) {
if (this.text_size == text_size) return;
this.text_size = text_size;
textView.setTextSize(text_size);
nameView.setTextSize(text_size);
}
public void setUserColor(final int color) {
// content.drawStart(color);
}
}

View File

@ -299,7 +299,6 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick
final ParcelableMedia[] media = ParcelableMedia.fromJSONString(cursor.getString(indices.media));
replyRetweetIcon.setColorFilter(replyRetweetView.getCurrentTextColor(), Mode.SRC_ATOP);
if (retweet_id > 0) {
final String retweetedBy = UserColorNameUtils.getDisplayName(context, retweeted_by_id,
retweeted_by_name, retweeted_by_screen_name, nameFirst, nicknameOnly);
@ -315,7 +314,6 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick
replyRetweetView.setVisibility(View.VISIBLE);
replyRetweetIcon.setVisibility(View.VISIBLE);
} else {
replyRetweetView.setText(null);
replyRetweetView.setVisibility(View.GONE);
replyRetweetIcon.setVisibility(View.GONE);
}

View File

@ -1,82 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<org.mariotaku.messagebubbleview.library.MessageBubbleView
android:id="@+id/message_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:clickable="true"
app:bubbleColor="?messageBubbleColor"
app:caretHeight="8dp"
app:caretWidth="8dp"
app:cornerRadius="2dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<org.mariotaku.twidere.view.SquareFrameLayout
android:id="@+id/media_preview_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<org.mariotaku.twidere.view.HighlightImageView
android:id="@+id/media_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"/>
<ProgressBar
android:id="@+id/media_preview_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="@dimen/element_spacing_large"/>
</org.mariotaku.twidere.view.SquareFrameLayout>
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/element_spacing_small"
android:paddingTop="@dimen/element_spacing_small"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"/>
</LinearLayout>
</org.mariotaku.messagebubbleview.library.MessageBubbleView>
</FrameLayout>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:showIn="@layout/card_item_message_conversation_incoming">
<org.mariotaku.twidere.view.CardMediaContainer
android:id="@+id/media_preview_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible">
<include layout="@layout/layout_card_media_preview"/>
</org.mariotaku.twidere.view.CardMediaContainer>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
tools:text="@string/sample_status_text"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/element_spacing_small"
android:textColor="?android:attr/textColorSecondary"
tools:text="12:00"/>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<org.mariotaku.messagebubbleview.library.MessageBubbleView
android:id="@+id/message_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:clickable="true"
app:bubbleColor="?colorAccent"
app:caretHeight="8dp"
app:caretPosition="topLeft"
app:caretWidth="8dp"
app:cornerRadius="2dp">
<include layout="@layout/card_item_message_conversation_common"/>
</org.mariotaku.messagebubbleview.library.MessageBubbleView>
</FrameLayout>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<org.mariotaku.messagebubbleview.library.MessageBubbleView
android:id="@+id/message_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:clickable="true"
app:bubbleColor="?messageBubbleColor"
app:caretHeight="8dp"
app:caretPosition="bottomRight"
app:caretWidth="8dp"
app:cornerRadius="2dp">
<include layout="@layout/card_item_message_conversation_common"/>
</org.mariotaku.messagebubbleview.library.MessageBubbleView>
</FrameLayout>

View File

@ -29,24 +29,27 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:paddingTop="@dimen/element_spacing_small"
android:orientation="vertical">
<Space
android:id="@+id/reply_retweet_space"
android:layout_width="@dimen/icon_size_status_profile_image"
android:layout_height="@dimen/element_size_small"
android:layout_alignParentLeft="true"
android:layout_alignTop="@+id/reply_retweet_status"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginRight="@dimen/element_spacing_small"/>
<ImageView
<org.mariotaku.twidere.view.ActionIconView
android:id="@+id/reply_retweet_icon"
style="?profileImageStyle"
android:layout_width="@dimen/element_size_small"
android:layout_height="@dimen/element_size_small"
android:layout_marginBottom="@dimen/element_spacing_small"
android:layout_alignTop="@id/reply_retweet_status"
android:layout_marginLeft="@dimen/element_spacing_small"
android:layout_marginTop="@dimen/element_spacing_small"
android:layout_toLeftOf="@+id/reply_retweet_status"
android:color="?android:textColorSecondary"
android:padding="@dimen/element_spacing_small"
android:scaleType="centerInside"
android:visibility="gone"
@ -56,15 +59,14 @@
<org.mariotaku.twidere.view.ActionIconThemedTextView
android:id="@+id/reply_retweet_status"
android:layout_width="match_parent"
android:layout_height="@dimen/element_size_small"
android:layout_marginBottom="@dimen/element_spacing_minus_normal"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginRight="@dimen/element_spacing_normal"
android:layout_marginTop="@dimen/element_spacing_small"
android:layout_toRightOf="@id/reply_retweet_space"
android:ellipsize="end"
android:gravity="center_vertical"
android:minHeight="@dimen/element_size_small"
tools:text="Mariotaku and 10 others retweeted this"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:visibility="gone"
@ -75,8 +77,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/reply_retweet_status"
android:layout_marginTop="@dimen/element_spacing_normal"
android:layout_below="@+id/reply_retweet_status"
android:paddingLeft="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.ShapedImageView

View File

@ -35,7 +35,7 @@
android:paddingRight="@dimen/element_spacing_normal"
android:paddingTop="@dimen/element_spacing_normal">
<ImageView
<org.mariotaku.twidere.view.ActionIconView
android:id="@+id/reply_retweet_icon"
style="?profileImageStyle"
android:layout_width="@dimen/element_size_small"
@ -43,6 +43,7 @@
android:layout_alignRight="@id/profile_image"
android:layout_alignTop="@id/reply_retweet_status"
android:layout_marginLeft="@dimen/element_spacing_small"
android:color="?android:textColorSecondary"
android:padding="@dimen/element_spacing_small"
android:scaleType="centerInside"
android:visibility="gone"

View File

@ -19,6 +19,7 @@
-->
<FrameLayout
android:id="@+id/fragment_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@ -29,7 +30,9 @@
android:id="@+id/conversation_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="?android:dividerVertical"
android:orientation="vertical"
android:showDividers="middle"
android:visibility="gone"
tools:visibility="visible">
@ -39,8 +42,8 @@
android:layout_height="0dp"
android:layout_weight="1">
<ListView
android:id="@android:id/list"
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
@ -80,8 +83,8 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:layout_gravity="center">
<TextView
android:id="@+id/users_search_empty_text"

View File

@ -20,7 +20,9 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
tools:showIn="@layout/fragment_messages_conversation"
android:layout_height="wrap_content"
android:gravity="bottom"
android:orientation="horizontal">
@ -31,6 +33,7 @@
android:layout_width="?android:actionBarSize"
android:layout_height="?android:actionBarSize"
android:layout_weight="0"
android:color="?android:textColorSecondary"
android:contentDescription="@string/add_image"
android:scaleType="centerInside"
android:src="@drawable/ic_action_gallery"/>
@ -59,6 +62,7 @@
<org.mariotaku.twidere.view.ActionIconView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:color="?android:textColorSecondary"
android:contentDescription="@string/send"
android:scaleType="centerInside"
android:src="@drawable/ic_action_send"/>

View File

@ -25,8 +25,11 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:minHeight="?android:listPreferredItemHeight"
android:orientation="horizontal"
android:padding="@dimen/element_spacing_normal"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal"
app:ignorePadding="true"
tools:context=".adapter.DirectMessagesEntryAdapter">
@ -37,75 +40,74 @@
android:layout_height="@dimen/icon_size_card_list_item"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_margin="@dimen/element_spacing_normal"
android:contentDescription="@string/profile_image"/>
android:layout_marginBottom="@dimen/element_spacing_small"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginRight="@dimen/element_spacing_normal"
android:layout_marginTop="@dimen/element_spacing_small"
android:contentDescription="@string/profile_image"
tools:src="@drawable/ic_profile_image_default"/>
<org.mariotaku.twidere.view.BottomDividerFrameLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_toRightOf="@id/profile_image"
android:divider="?android:dividerVertical"
android:gravity="center_vertical"
android:minHeight="?android:listPreferredItemHeight"
android:orientation="vertical"
android:paddingBottom="@dimen/element_spacing_large"
android:paddingBottom="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal"
android:paddingTop="@dimen/element_spacing_large">
android:paddingTop="@dimen/element_spacing_normal">
<RelativeLayout
<LinearLayout
android:id="@+id/name_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical">
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/time"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/name_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/time"
android:orientation="horizontal">
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textColor="?android:attr/textColorPrimary"/>
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/screen_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_small"
android:singleLine="true"
android:textColor="?android:attr/textColorPrimary"/>
</LinearLayout>
<org.mariotaku.twidere.view.ShortTimeView
android:id="@+id/time"
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:drawablePadding="4dp"
android:gravity="center"
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"/>
android:textColor="?android:attr/textColorPrimary"
tools:text="Mariotaku Lee"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/text"
android:layout_width="match_parent"
<org.mariotaku.twidere.view.HandleSpanClickTextView
android:id="@+id/screen_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/name_container"
android:layout_below="@id/name_container"
android:layout_marginTop="@dimen/element_spacing_xsmall"
android:layout_marginLeft="@dimen/element_spacing_small"
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"/>
</RelativeLayout>
android:textColor="?android:attr/textColorPrimary"
tools:text="\@mariotaku"/>
</LinearLayout>
</org.mariotaku.twidere.view.BottomDividerFrameLayout>
<org.mariotaku.twidere.view.ShortTimeView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:drawablePadding="4dp"
android:gravity="center"
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"
tools:text="12:00"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/name_container"
android:layout_below="@id/name_container"
android:layout_marginTop="@dimen/element_spacing_xsmall"
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"
tools:text="@string/sample_status_text"/>
</RelativeLayout>
</org.mariotaku.twidere.view.ColorLabelRelativeLayout>