This commit is contained in:
Mariotaku Lee 2017-01-07 22:45:33 +08:00
parent 2bab27f68c
commit ad1f14c20c
29 changed files with 867 additions and 976 deletions

View File

@ -1,188 +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.graphics.PorterDuff.Mode;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.support.v4.widget.SimpleCursorAdapter;
import android.text.TextUtils;
import android.view.View;
import android.widget.FilterQueryProvider;
import android.widget.ImageView;
import android.widget.TextView;
import org.apache.commons.lang3.StringUtils;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
import javax.inject.Inject;
public class ComposeAutoCompleteAdapter extends SimpleCursorAdapter implements Constants {
private static final String[] FROM = new String[0];
private static final int[] TO = new int[0];
@Inject
MediaLoaderWrapper mProfileImageLoader;
@Inject
SharedPreferencesWrapper mPreferences;
@Inject
UserColorNameManager mUserColorNameManager;
private final boolean mDisplayProfileImage;
private int mTypeIdx, mIconIdx, mTitleIdx, mSummaryIdx, mExtraIdIdx, mValueIdx;
private UserKey accountKey;
private char mToken;
public ComposeAutoCompleteAdapter(final Context context) {
super(context, R.layout.list_item_auto_complete, null, FROM, TO, 0);
GeneralComponentHelper.build(context).inject(this);
mDisplayProfileImage = mPreferences.getBoolean(KEY_DISPLAY_PROFILE_IMAGE, true);
}
@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
final TextView text1 = (TextView) view.findViewById(android.R.id.text1);
final TextView text2 = (TextView) view.findViewById(android.R.id.text2);
final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
// Clear images in order to prevent images in recycled view shown.
icon.setImageDrawable(null);
if (Suggestions.AutoComplete.TYPE_USERS.equals(cursor.getString(mTypeIdx))) {
text1.setText(mUserColorNameManager.getUserNickname(cursor.getString(mExtraIdIdx),
cursor.getString(mTitleIdx)));
text2.setText(String.format("@%s", cursor.getString(mSummaryIdx)));
if (mDisplayProfileImage) {
final String profileImageUrl = cursor.getString(mIconIdx);
mProfileImageLoader.displayProfileImage(icon, profileImageUrl);
} else {
mProfileImageLoader.cancelDisplayTask(icon);
}
icon.clearColorFilter();
} else {
text1.setText(String.format("#%s", cursor.getString(mTitleIdx)));
text2.setText(R.string.hashtag);
icon.setImageResource(R.drawable.ic_action_hashtag);
icon.setColorFilter(text1.getCurrentTextColor(), Mode.SRC_ATOP);
}
icon.setVisibility(mDisplayProfileImage ? View.VISIBLE : View.GONE);
super.bindView(view, context, cursor);
}
public void closeCursor() {
final Cursor cursor = swapCursor(null);
if (cursor == null) return;
if (!cursor.isClosed()) {
cursor.close();
}
}
@Override
public CharSequence convertToString(final Cursor cursor) {
switch (StringUtils.defaultIfEmpty(cursor.getString(mTypeIdx), "")) {
case Suggestions.AutoComplete.TYPE_HASHTAGS: {
return '#' + cursor.getString(mValueIdx);
}
case Suggestions.AutoComplete.TYPE_USERS: {
return '@' + cursor.getString(mValueIdx);
}
}
return cursor.getString(mValueIdx);
}
@Override
public Cursor runQueryOnBackgroundThread(final CharSequence constraint) {
if (TextUtils.isEmpty(constraint)) return null;
char token = constraint.charAt(0);
if (getNormalizedSymbol(token) == getNormalizedSymbol(mToken)) {
final FilterQueryProvider filter = getFilterQueryProvider();
if (filter != null) return filter.runQuery(constraint);
}
mToken = token;
final Uri.Builder builder = Suggestions.AutoComplete.CONTENT_URI.buildUpon();
builder.appendQueryParameter(QUERY_PARAM_QUERY, String.valueOf(constraint.subSequence(1, constraint.length())));
switch (getNormalizedSymbol(token)) {
case '#': {
builder.appendQueryParameter(QUERY_PARAM_TYPE, Suggestions.AutoComplete.TYPE_HASHTAGS);
break;
}
case '@': {
builder.appendQueryParameter(QUERY_PARAM_TYPE, Suggestions.AutoComplete.TYPE_USERS);
break;
}
default: {
return null;
}
}
builder.appendQueryParameter(QUERY_PARAM_ACCOUNT_KEY, String.valueOf(accountKey));
return mContext.getContentResolver().query(builder.build(), Suggestions.AutoComplete.COLUMNS,
null, null, null);
}
public void setAccountKey(UserKey accountKey) {
this.accountKey = accountKey;
}
public UserKey getAccountKey() {
return accountKey;
}
@Override
@Nullable
public Cursor swapCursor(@Nullable final Cursor cursor) {
if (cursor != null) {
mTypeIdx = cursor.getColumnIndex(Suggestions.AutoComplete.TYPE);
mTitleIdx = cursor.getColumnIndex(Suggestions.AutoComplete.TITLE);
mSummaryIdx = cursor.getColumnIndex(Suggestions.AutoComplete.SUMMARY);
mExtraIdIdx = cursor.getColumnIndex(Suggestions.AutoComplete.EXTRA_ID);
mIconIdx = cursor.getColumnIndex(Suggestions.AutoComplete.ICON);
mValueIdx = cursor.getColumnIndex(Suggestions.AutoComplete.VALUE);
}
return super.swapCursor(cursor);
}
private static char getNormalizedSymbol(final char character) {
switch (character) {
case '\uff20':
case '@':
return '@';
case '\uff03':
case '#':
return '#';
}
return '\0';
}
}

View File

@ -24,8 +24,6 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.internal.widget.PreferenceImageView;
@ -35,19 +33,14 @@ import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceViewHolder;
import android.support.v7.widget.SwitchCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.model.AccountDetails;
import org.mariotaku.twidere.model.util.AccountUtils;
import org.mariotaku.twidere.util.BitmapUtils;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
@ -101,8 +94,7 @@ public abstract class AccountsListPreference extends PreferenceCategory implemen
return mSwitchDefault;
}
public static final class AccountItemPreference extends Preference implements ImageLoadingListener,
OnSharedPreferenceChangeListener {
public static final class AccountItemPreference extends Preference implements OnSharedPreferenceChangeListener {
private final AccountDetails mAccount;
@Nullable
@ -125,33 +117,9 @@ public abstract class AccountsListPreference extends PreferenceCategory implemen
mSwitchPreference.registerOnSharedPreferenceChangeListener(this);
setTitle(mAccount.user.name);
setSummary(String.format("@%s", mAccount.user.screen_name));
mImageLoader.loadProfileImage(mAccount, this);
setWidgetLayoutResource(R.layout.layout_preference_switch_indicator);
}
@Override
public void onLoadingCancelled(final String imageUri, final View view) {
// setIcon(R.drawable.ic_profile_image_default);
}
@Override
public void onLoadingComplete(final String imageUri, final View view, final Bitmap loadedImage) {
final Bitmap roundedBitmap = BitmapUtils.getCircleBitmap(loadedImage);
final BitmapDrawable icon = new BitmapDrawable(getContext().getResources(), roundedBitmap);
icon.setGravity(Gravity.FILL);
setIcon(icon);
}
@Override
public void onLoadingFailed(final String imageUri, final View view, final FailReason failReason) {
// setIcon(R.drawable.ic_profile_image_default);
}
@Override
public void onLoadingStarted(final String imageUri, final View view) {
// setIcon(R.drawable.ic_profile_image_default);
}
@Override
public void onSharedPreferenceChanged(final SharedPreferences preferences, final String key) {
notifyChanged();

View File

@ -17,15 +17,7 @@
package org.mariotaku.twidere.util;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.net.Uri;
import android.support.annotation.Nullable;
@ -38,27 +30,6 @@ public class BitmapUtils {
private BitmapUtils() {
}
public static Bitmap getCircleBitmap(Bitmap bitmap) {
final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final int color = Color.RED;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawOval(rectF, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
@Nullable
public static String getImageMimeType(ContentResolver cr, final Uri uri) {
if (uri == null) return null;

View File

@ -1,145 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder;
import android.content.Context;
import android.support.v4.text.BidiFormatter;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.iface.IGroupsAdapter;
import org.mariotaku.twidere.model.ParcelableGroup;
import org.mariotaku.twidere.model.util.UserKeyUtils;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.NameView;
import org.mariotaku.twidere.view.iface.IColorLabelView;
import java.util.Locale;
/**
* Created by mariotaku on 15/4/29.
*/
public class GroupViewHolder extends ViewHolder implements View.OnClickListener, View.OnLongClickListener {
private final IGroupsAdapter<?> adapter;
private final IColorLabelView itemContent;
private final ImageView profileImageView;
private final NameView nameView;
private final TextView externalIndicator;
private final TextView descriptionView;
private final TextView membersCountView;
private final TextView adminsCountView;
private IGroupsAdapter.GroupAdapterListener groupClickListener;
public GroupViewHolder(IGroupsAdapter<?> adapter, View itemView) {
super(itemView);
itemContent = (IColorLabelView) itemView.findViewById(R.id.itemContent);
this.adapter = adapter;
profileImageView = (ImageView) itemView.findViewById(R.id.profileImage);
nameView = (NameView) itemView.findViewById(R.id.name);
externalIndicator = (TextView) itemView.findViewById(R.id.externalIndicator);
descriptionView = (TextView) itemView.findViewById(R.id.description);
membersCountView = (TextView) itemView.findViewById(R.id.membersCount);
adminsCountView = (TextView) itemView.findViewById(R.id.adminsCount);
}
public void displayGroup(ParcelableGroup group) {
final Context context = itemView.getContext();
final MediaLoaderWrapper loader = adapter.getMediaLoader();
final BidiFormatter formatter = adapter.getBidiFormatter();
nameView.setName(group.fullname);
nameView.setScreenName("!" + group.nickname);
nameView.updateText(formatter);
final String groupHost = UserKeyUtils.getUserHost(group.url, group.account_key.getHost());
if (UserKeyUtils.isSameHost(group.account_key.getHost(), groupHost)) {
externalIndicator.setVisibility(View.GONE);
} else {
externalIndicator.setVisibility(View.VISIBLE);
externalIndicator.setText(context.getString(R.string.external_group_host_format,
groupHost));
}
if (adapter.getProfileImageEnabled()) {
profileImageView.setVisibility(View.VISIBLE);
loader.displayProfileImage(profileImageView, group.homepage_logo);
} else {
profileImageView.setVisibility(View.GONE);
loader.cancelDisplayTask(profileImageView);
}
descriptionView.setVisibility(TextUtils.isEmpty(group.description) ? View.GONE : View.VISIBLE);
descriptionView.setText(formatter.unicodeWrap(group.description));
membersCountView.setText(Utils.getLocalizedNumber(Locale.getDefault(), group.member_count));
adminsCountView.setText(Utils.getLocalizedNumber(Locale.getDefault(), group.admin_count));
}
public void setOnClickListeners() {
setGroupClickListener(adapter.getGroupAdapterListener());
}
@Override
public void onClick(View v) {
if (groupClickListener == null) return;
switch (v.getId()) {
case R.id.itemContent: {
groupClickListener.onGroupClick(this, getLayoutPosition());
break;
}
}
}
@Override
public boolean onLongClick(View v) {
if (groupClickListener == null) return false;
switch (v.getId()) {
case R.id.itemContent: {
return groupClickListener.onGroupLongClick(this, getLayoutPosition());
}
}
return false;
}
public void setGroupClickListener(IGroupsAdapter.GroupAdapterListener listener) {
groupClickListener = listener;
((View) itemContent).setOnClickListener(this);
((View) itemContent).setOnLongClickListener(this);
}
public void setupViewOptions() {
setTextSize(adapter.getTextSize());
}
public void setTextSize(final float textSize) {
descriptionView.setTextSize(textSize);
externalIndicator.setTextSize(textSize);
nameView.setPrimaryTextSize(textSize);
nameView.setSecondaryTextSize(textSize * 0.75f);
membersCountView.setTextSize(textSize);
adminsCountView.setTextSize(textSize);
}
}

View File

@ -1,57 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder;
import android.database.Cursor;
import android.view.View;
import android.widget.ImageView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.MessageConversationAdapter;
import org.mariotaku.twidere.model.ParcelableDirectMessageCursorIndices;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
/**
* Created by mariotaku on 15/4/25.
*/
public class IncomingMessageViewHolder extends MessageViewHolder {
private final ImageView profileImageView;
public IncomingMessageViewHolder(MessageConversationAdapter adapter, View itemView) {
super(adapter, itemView);
profileImageView = (ImageView) itemView.findViewById(R.id.profileImage);
}
@Override
public void displayMessage(Cursor cursor, ParcelableDirectMessageCursorIndices indices) {
super.displayMessage(cursor, indices);
final MediaLoaderWrapper wrapper = adapter.getMediaLoader();
if (adapter.getProfileImageEnabled()) {
profileImageView.setVisibility(View.VISIBLE);
wrapper.displayProfileImage(profileImageView, cursor.getString(indices.sender_profile_image_url));
} else {
profileImageView.setVisibility(View.GONE);
wrapper.cancelDisplayTask(profileImageView);
}
}
}

View File

@ -1,127 +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.database.Cursor;
import android.graphics.Typeface;
import android.support.annotation.UiThread;
import android.support.v7.widget.RecyclerView.ViewHolder;
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.model.UserKey;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.view.NameView;
import org.mariotaku.twidere.view.ShortTimeView;
import org.mariotaku.twidere.view.iface.IColorLabelView;
import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
public class MessageEntryViewHolder extends ViewHolder implements OnClickListener {
public final ImageView profileImage;
public final NameView nameView;
public final TextView textView;
public final ShortTimeView timeView;
private final MessageEntriesAdapter adapter;
private final IColorLabelView content;
public MessageEntryViewHolder(final MessageEntriesAdapter adapter, final View itemView) {
super(itemView);
this.adapter = adapter;
content = (IColorLabelView) itemView.findViewById(R.id.content);
profileImage = (ImageView) itemView.findViewById(R.id.profileImage);
nameView = (NameView) itemView.findViewById(R.id.name);
textView = (TextView) itemView.findViewById(R.id.text);
timeView = (ShortTimeView) itemView.findViewById(R.id.time);
setTextSize(adapter.getTextSize());
itemView.setOnClickListener(this);
profileImage.setOnClickListener(this);
}
@UiThread
public void displayMessage(Cursor cursor, boolean isUnread) {
final Context context = adapter.getContext();
final MediaLoaderWrapper loader = adapter.getMediaLoader();
final UserColorNameManager manager = adapter.getUserColorNameManager();
final UserKey accountKey = UserKey.valueOf(cursor.getString(ConversationEntries.IDX_ACCOUNT_KEY));
final UserKey conversationId = UserKey.valueOf(cursor.getString(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);
nameView.setName(manager.getUserNickname(conversationId, name));
nameView.setScreenName("@" + screenName);
nameView.updateText(adapter.getBidiFormatter());
textView.setText(toPlainText(cursor.getString(ConversationEntries.IDX_TEXT_UNESCAPED)));
timeView.setTime(timestamp);
if (isOutgoing) {
timeView.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_indicator_sent, 0);
} else {
timeView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
nameView.setTypeface(null, isUnread && !isOutgoing ? Typeface.BOLD : Typeface.NORMAL);
textView.setTypeface(null, isUnread && !isOutgoing ? Typeface.BOLD : Typeface.NORMAL);
if (adapter.shouldShowAccountsColor()) {
// FIXME draw account color
} else {
content.drawEnd();
}
content.drawStart(manager.getUserColor(conversationId));
final String profileImage = cursor.getString(ConversationEntries.IDX_PROFILE_IMAGE_URL);
loader.displayProfileImage(this.profileImage, profileImage);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.profileImage: {
adapter.onUserProfileClick(getLayoutPosition());
break;
}
default: {
if (v == itemView) {
adapter.onMessageClick(getLayoutPosition());
}
break;
}
}
}
public void setTextSize(final float textSize) {
nameView.setPrimaryTextSize(textSize * 1.1f);
nameView.setSecondaryTextSize(textSize);
textView.setTextSize(textSize);
timeView.setTextSize(textSize * 0.85f);
}
}

View File

@ -1,140 +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.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.SpannableStringBuilder;
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.ParcelableDirectMessageCursorIndices;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.SpanItem;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.util.ParcelableStatusUtils;
import org.mariotaku.twidere.util.JsonSerializer;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereColorUtils;
import org.mariotaku.twidere.util.TwidereLinkify;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.CardMediaContainer;
public class MessageViewHolder extends ViewHolder {
public final CardMediaContainer mediaContainer;
public final TextView textView, time;
private final MessageBubbleView messageContent;
protected final MessageConversationAdapter adapter;
private final int textColorPrimary, textColorPrimaryInverse, textColorSecondary, textColorSecondaryInverse;
public MessageViewHolder(final MessageConversationAdapter adapter, final View itemView) {
super(itemView);
this.adapter = adapter;
final Context context = itemView.getContext();
final TypedArray a = context.obtainStyledAttributes(R.styleable.MessageViewHolder);
textColorPrimary = a.getColor(R.styleable.MessageViewHolder_android_textColorPrimary, 0);
textColorPrimaryInverse = a.getColor(R.styleable.MessageViewHolder_android_textColorPrimaryInverse, 0);
textColorSecondary = a.getColor(R.styleable.MessageViewHolder_android_textColorSecondary, 0);
textColorSecondaryInverse = a.getColor(R.styleable.MessageViewHolder_android_textColorSecondaryInverse, 0);
a.recycle();
messageContent = (MessageBubbleView) itemView.findViewById(R.id.messageContent);
messageContent.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
itemView.getParent().showContextMenuForChild(itemView);
return true;
}
});
messageContent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
itemView.getParent().showContextMenuForChild(itemView);
}
});
textView = (TextView) itemView.findViewById(R.id.text);
time = (TextView) itemView.findViewById(R.id.time);
mediaContainer = (CardMediaContainer) itemView.findViewById(R.id.media_preview_container);
mediaContainer.setStyle(adapter.getMediaPreviewStyle());
}
public void displayMessage(Cursor cursor, ParcelableDirectMessageCursorIndices indices) {
final Context context = adapter.getContext();
final TwidereLinkify linkify = adapter.getLinkify();
final MediaLoaderWrapper loader = adapter.getMediaLoader();
final UserKey accountKey = UserKey.valueOf(cursor.getString(indices.account_key));
final long timestamp = cursor.getLong(indices.timestamp);
final ParcelableMedia[] media = JsonSerializer.parseArray(cursor.getString(indices.media),
ParcelableMedia.class);
final SpanItem[] spans = JsonSerializer.parseArray(cursor.getString(indices.spans),
SpanItem.class);
final SpannableStringBuilder text = SpannableStringBuilder.valueOf(cursor.getString(indices.text_unescaped));
ParcelableStatusUtils.INSTANCE.applySpans(text, spans);
// Detect entity support
linkify.applyAllLinks(text, accountKey, false, true);
textView.setText(text);
time.setText(Utils.formatToLongTimeString(context, timestamp));
mediaContainer.setVisibility(media != null && media.length > 0 ? View.VISIBLE : View.GONE);
mediaContainer.displayMedia(loader, media, accountKey, adapter.getOnMediaClickListener(), adapter.getMediaLoadingHandler(), getLayoutPosition(), true
);
}
public void setMessageColor(int color) {
final ColorStateList colorStateList = ColorStateList.valueOf(color);
messageContent.setBubbleColor(colorStateList);
final int textLuminancePrimary = TwidereColorUtils.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 = TwidereColorUtils.getContrastYIQ(color,
ThemeUtils.ACCENT_COLOR_THRESHOLD, textPrimaryDark, textPrimaryLight);
final int textContrastSecondary = TwidereColorUtils.getContrastYIQ(color,
ThemeUtils.ACCENT_COLOR_THRESHOLD, textSecondaryDark, textSecondaryLight);
textView.setTextColor(textContrastPrimary);
textView.setLinkTextColor(textContrastSecondary);
time.setTextColor(textContrastSecondary);
}
public void setTextSize(final float textSize) {
textView.setTextSize(textSize);
time.setTextSize(textSize * 0.75f);
}
}

View File

@ -1,134 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder;
import android.content.Context;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.iface.IUserListsAdapter;
import org.mariotaku.twidere.model.ParcelableUserList;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.iface.IColorLabelView;
import java.util.Locale;
/**
* Created by mariotaku on 15/4/29.
*/
public class UserListViewHolder extends ViewHolder implements View.OnClickListener, View.OnLongClickListener {
private final IUserListsAdapter<?> adapter;
private final IColorLabelView itemContent;
private final ImageView profileImageView;
private final TextView nameView;
private final TextView createdByView;
private final TextView descriptionView;
private final TextView membersCountView;
private final TextView subscribersCountView;
private IUserListsAdapter.UserListClickListener userListClickListener;
public UserListViewHolder(IUserListsAdapter<?> adapter, View itemView) {
super(itemView);
itemContent = (IColorLabelView) itemView.findViewById(R.id.itemContent);
this.adapter = adapter;
profileImageView = (ImageView) itemView.findViewById(R.id.profileImage);
nameView = (TextView) itemView.findViewById(R.id.name);
createdByView = (TextView) itemView.findViewById(R.id.createdBy);
descriptionView = (TextView) itemView.findViewById(R.id.description);
membersCountView = (TextView) itemView.findViewById(R.id.membersCount);
subscribersCountView = (TextView) itemView.findViewById(R.id.subscribersCount);
}
public void displayUserList(ParcelableUserList userList) {
final Context context = itemView.getContext();
final MediaLoaderWrapper loader = adapter.getMediaLoader();
final UserColorNameManager manager = adapter.getUserColorNameManager();
itemContent.drawStart(manager.getUserColor(userList.user_key));
nameView.setText(userList.name);
final boolean nameFirst = adapter.getNameFirst();
final String createdByDisplayName = manager.getDisplayName(userList, nameFirst);
createdByView.setText(context.getString(R.string.created_by, createdByDisplayName));
if (adapter.getProfileImageEnabled()) {
profileImageView.setVisibility(View.VISIBLE);
loader.displayProfileImage(profileImageView, userList.user_profile_image_url);
} else {
profileImageView.setVisibility(View.GONE);
loader.cancelDisplayTask(profileImageView);
}
descriptionView.setVisibility(TextUtils.isEmpty(userList.description) ? View.GONE : View.VISIBLE);
descriptionView.setText(userList.description);
membersCountView.setText(Utils.getLocalizedNumber(Locale.getDefault(), userList.members_count));
subscribersCountView.setText(Utils.getLocalizedNumber(Locale.getDefault(), userList.subscribers_count));
}
public void setOnClickListeners() {
setUserListClickListener(adapter.getUserListClickListener());
}
@Override
public void onClick(View v) {
if (userListClickListener == null) return;
switch (v.getId()) {
case R.id.itemContent: {
userListClickListener.onUserListClick(this, getLayoutPosition());
break;
}
}
}
@Override
public boolean onLongClick(View v) {
if (userListClickListener == null) return false;
switch (v.getId()) {
case R.id.itemContent: {
return userListClickListener.onUserListLongClick(this, getLayoutPosition());
}
}
return false;
}
public void setUserListClickListener(IUserListsAdapter.UserListClickListener listener) {
userListClickListener = listener;
((View) itemContent).setOnClickListener(this);
((View) itemContent).setOnLongClickListener(this);
}
public void setupViewOptions() {
setTextSize(adapter.getTextSize());
}
public void setTextSize(final float textSize) {
nameView.setTextSize(textSize);
createdByView.setTextSize(textSize * 0.75f);
}
}

View File

@ -624,6 +624,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
if (mediaMenuItem != null && mediaMenuItem.hasSubMenu()) {
MenuUtils.addIntentToMenu(this, mediaMenuItem.subMenu, imageExtensionsIntent, MENU_GROUP_IMAGE_EXTENSION)
}
updateViewStyle()
setMenu()
updateLocationState()
notifyAccountSelectionChanged()
@ -633,6 +634,10 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
updateAttachedMediaView()
}
private fun updateViewStyle() {
accountProfileImage.style = preferences[profileImageStyleKey]
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
val keyCode = event.keyCode
if (KeyEvent.isModifierKey(keyCode)) {

View File

@ -19,35 +19,22 @@
package org.mariotaku.twidere.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.list_item_simple_user.view.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants
import org.mariotaku.twidere.constant.SharedPreferenceConstants
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.util.MediaLoaderWrapper
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import javax.inject.Inject
class AccountsSpinnerAdapter(
context: Context,
itemViewResource: Int = R.layout.list_item_simple_user
) : ArrayAdapter<AccountDetails>(context, itemViewResource) {
) : BaseArrayAdapter<AccountDetails>(context, itemViewResource) {
@Inject
lateinit var mediaLoader: MediaLoaderWrapper
private val displayProfileImage: Boolean
private var dummyItemText: String? = null
init {
GeneralComponentHelper.build(context).inject(this)
displayProfileImage = context.getSharedPreferences(TwidereConstants.SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE).getBoolean(SharedPreferenceConstants.KEY_DISPLAY_PROFILE_IMAGE, true)
}
constructor(context: Context, accounts: Collection<AccountDetails>) : this(context) {
addAll(accounts)
}
@ -73,34 +60,26 @@ class AccountsSpinnerAdapter(
val text2 = view.screenName
val icon = view.profileImage
if (!item.dummy) {
if (text1 != null) {
text1.visibility = View.VISIBLE
text1.text = item.user.name
}
if (text2 != null) {
text2.visibility = View.VISIBLE
text2.text = String.format("@%s", item.user.screen_name)
}
text1?.visibility = View.VISIBLE
text1?.text = item.user.name
text2?.visibility = View.VISIBLE
@SuppressLint("SetTextI18n")
text2?.text = "@${item.user.screen_name}"
if (icon != null) {
icon.visibility = View.VISIBLE
if (displayProfileImage) {
if (profileImageEnabled) {
icon.visibility = View.VISIBLE
icon.style = profileImageStyle
mediaLoader.displayProfileImage(icon, item.user)
} else {
icon.visibility = View.GONE
mediaLoader.cancelDisplayTask(icon)
// icon.setImageResource(R.drawable.ic_profile_image_default);
}
}
} else {
if (text1 != null) {
text1.visibility = View.VISIBLE
text1.text = dummyItemText
}
if (text2 != null) {
text2.visibility = View.GONE
}
if (icon != null) {
icon.visibility = View.GONE
}
text1?.visibility = View.VISIBLE
text1?.text = dummyItemText
text2?.visibility = View.GONE
icon?.visibility = View.GONE
}
}

View File

@ -48,10 +48,10 @@ open class BaseArrayAdapter<T>(
@Inject
lateinit var preferences: SharedPreferencesWrapper
override val profileImageStyle: Int
override val textSize: Float
override val profileImageEnabled: Boolean
override val isShowAbsoluteTime: Boolean
final override val profileImageStyle: Int
final override val textSize: Float
final override val profileImageEnabled: Boolean
final override val isShowAbsoluteTime: Boolean
val nameFirst: Boolean
init {

View File

@ -0,0 +1,170 @@
/*
* 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.graphics.PorterDuff.Mode
import android.support.v4.widget.SimpleCursorAdapter
import android.text.TextUtils
import android.view.View
import android.widget.TextView
import org.apache.commons.lang3.StringUtils
import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.constant.displayProfileImageKey
import org.mariotaku.twidere.constant.profileImageStyleKey
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions
import org.mariotaku.twidere.util.MediaLoaderWrapper
import org.mariotaku.twidere.util.SharedPreferencesWrapper
import org.mariotaku.twidere.util.UserColorNameManager
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.view.ProfileImageView
import javax.inject.Inject
class ComposeAutoCompleteAdapter(context: Context) : SimpleCursorAdapter(context,
R.layout.list_item_auto_complete, null, emptyArray(), intArrayOf(), 0) {
@Inject
lateinit var mediaLoader: MediaLoaderWrapper
@Inject
lateinit var preferences: SharedPreferencesWrapper
@Inject
lateinit var userColorNameManager: UserColorNameManager
private val displayProfileImage: Boolean
private val profileImageStyle: Int
private var mTypeIdx: Int = 0
private var mIconIdx: Int = 0
private var mTitleIdx: Int = 0
private var mSummaryIdx: Int = 0
private var mExtraIdIdx: Int = 0
private var mValueIdx: Int = 0
var accountKey: UserKey? = null
private var mToken: Char = ' '
init {
GeneralComponentHelper.build(context).inject(this)
displayProfileImage = preferences[displayProfileImageKey]
profileImageStyle = preferences[profileImageStyleKey]
}
override fun bindView(view: View, context: Context?, cursor: Cursor) {
val text1 = view.findViewById(android.R.id.text1) as TextView
val text2 = view.findViewById(android.R.id.text2) as TextView
val icon = view.findViewById(android.R.id.icon) as ProfileImageView
icon.style = profileImageStyle
if (Suggestions.AutoComplete.TYPE_USERS == cursor.getString(mTypeIdx)) {
text1.text = userColorNameManager.getUserNickname(cursor.getString(mExtraIdIdx),
cursor.getString(mTitleIdx))
text2.text = String.format("@%s", cursor.getString(mSummaryIdx))
if (displayProfileImage) {
val profileImageUrl = cursor.getString(mIconIdx)
mediaLoader.displayProfileImage(icon, profileImageUrl)
} else {
mediaLoader.cancelDisplayTask(icon)
}
icon.clearColorFilter()
} else {
text1.text = String.format("#%s", cursor.getString(mTitleIdx))
text2.setText(R.string.hashtag)
icon.setImageResource(R.drawable.ic_action_hashtag)
icon.setColorFilter(text1.currentTextColor, Mode.SRC_ATOP)
}
icon.visibility = if (displayProfileImage) View.VISIBLE else View.GONE
super.bindView(view, context, cursor)
}
fun closeCursor() {
val cursor = swapCursor(null) ?: return
if (!cursor.isClosed) {
cursor.close()
}
}
override fun convertToString(cursor: Cursor?): CharSequence {
when (StringUtils.defaultIfEmpty(cursor!!.getString(mTypeIdx), "")) {
Suggestions.AutoComplete.TYPE_HASHTAGS -> {
return '#' + cursor.getString(mValueIdx)
}
Suggestions.AutoComplete.TYPE_USERS -> {
return '@' + cursor.getString(mValueIdx)
}
}
return cursor.getString(mValueIdx)
}
override fun runQueryOnBackgroundThread(constraint: CharSequence): Cursor? {
if (TextUtils.isEmpty(constraint)) return null
val token = constraint[0]
if (getNormalizedSymbol(token) == getNormalizedSymbol(mToken)) {
val filter = filterQueryProvider
if (filter != null) return filter.runQuery(constraint)
}
mToken = token
val builder = Suggestions.AutoComplete.CONTENT_URI.buildUpon()
builder.appendQueryParameter(QUERY_PARAM_QUERY, constraint.subSequence(1, constraint.length).toString())
when (getNormalizedSymbol(token)) {
'#' -> {
builder.appendQueryParameter(QUERY_PARAM_TYPE, Suggestions.AutoComplete.TYPE_HASHTAGS)
}
'@' -> {
builder.appendQueryParameter(QUERY_PARAM_TYPE, Suggestions.AutoComplete.TYPE_USERS)
}
else -> {
return null
}
}
builder.appendQueryParameter(QUERY_PARAM_ACCOUNT_KEY, accountKey.toString())
return mContext.contentResolver.query(builder.build(), Suggestions.AutoComplete.COLUMNS,
null, null, null)
}
override fun swapCursor(cursor: Cursor?): Cursor? {
if (cursor != null) {
mTypeIdx = cursor.getColumnIndex(Suggestions.AutoComplete.TYPE)
mTitleIdx = cursor.getColumnIndex(Suggestions.AutoComplete.TITLE)
mSummaryIdx = cursor.getColumnIndex(Suggestions.AutoComplete.SUMMARY)
mExtraIdIdx = cursor.getColumnIndex(Suggestions.AutoComplete.EXTRA_ID)
mIconIdx = cursor.getColumnIndex(Suggestions.AutoComplete.ICON)
mValueIdx = cursor.getColumnIndex(Suggestions.AutoComplete.VALUE)
}
return super.swapCursor(cursor)
}
companion object {
private fun getNormalizedSymbol(character: Char): Char {
when (character) {
'\uff20', '@' -> return '@'
'\uff03', '#' -> return '#'
}
return '\u0000'
}
}
}

View File

@ -200,10 +200,9 @@ class ParcelableActivitiesAdapter(
}
ITEM_VIEW_TYPE_TITLE_SUMMARY -> {
val view = inflater.inflate(R.layout.card_item_activity_summary_compact, parent, false)
val holder = ActivityTitleSummaryViewHolder(this,
view)
val holder = ActivityTitleSummaryViewHolder(view, this)
holder.setOnClickListeners()
holder.setTextSize(textSize)
holder.setupViewOptions()
return holder
}
ITEM_VIEW_TYPE_GAP -> {

View File

@ -63,7 +63,7 @@ class ParcelableGroupsAdapter(context: Context) : LoadMoreSupportAdapter<Recycle
}
private fun bindGroup(holder: GroupViewHolder, position: Int) {
holder.displayGroup(getGroup(position))
holder.displayGroup(getGroup(position)!!)
}
override fun getItemCount(): Int {

View File

@ -22,7 +22,6 @@ package org.mariotaku.twidere.adapter
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.R
@ -37,19 +36,14 @@ import org.mariotaku.twidere.view.holder.UserListViewHolder
class ParcelableUserListsAdapter(
context: Context
) : LoadMoreSupportAdapter<RecyclerView.ViewHolder>(context), IUserListsAdapter<List<ParcelableUserList>> {
override val showAccountsColor: Boolean
get() = false
override val nameFirst: Boolean
override val showAccountsColor: Boolean = false
override val nameFirst: Boolean = preferences[nameFirstKey]
override var userListClickListener: IUserListsAdapter.UserListClickListener? = null
private val inflater: LayoutInflater = LayoutInflater.from(context)
private var data: List<ParcelableUserList>? = null
init {
nameFirst = preferences[nameFirstKey]
}
fun getData(): List<ParcelableUserList>? {
return data
}
@ -62,7 +56,7 @@ class ParcelableUserListsAdapter(
}
private fun bindUserList(holder: UserListViewHolder, position: Int) {
holder.displayUserList(getUserList(position))
holder.displayUserList(getUserList(position)!!)
}
override fun getItemCount(): Int {
@ -131,9 +125,8 @@ class ParcelableUserListsAdapter(
fun createUserListViewHolder(adapter: IUserListsAdapter<*>,
inflater: LayoutInflater,
parent: ViewGroup): UserListViewHolder {
val view: View
view = inflater.inflate(R.layout.list_item_user_list, parent, false)
val holder = UserListViewHolder(adapter, view)
val view = inflater.inflate(R.layout.list_item_user_list, parent, false)
val holder = UserListViewHolder(view, adapter)
holder.setOnClickListeners()
holder.setupViewOptions()
return holder

View File

@ -19,19 +19,22 @@
package org.mariotaku.twidere.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.support.v4.widget.SimpleCursorAdapter
import android.text.TextUtils
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import org.mariotaku.kpreferences.get
import org.mariotaku.sqliteqb.library.Columns
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.sqliteqb.library.OrderBy
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_DISPLAY_PROFILE_IMAGE
import org.mariotaku.twidere.constant.displayProfileImageKey
import org.mariotaku.twidere.constant.profileImageStyleKey
import org.mariotaku.twidere.model.ParcelableUserCursorIndices
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers
import org.mariotaku.twidere.util.MediaLoaderWrapper
@ -39,49 +42,53 @@ import org.mariotaku.twidere.util.SharedPreferencesWrapper
import org.mariotaku.twidere.util.UserColorNameManager
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.view.ProfileImageView
import javax.inject.Inject
class UserAutoCompleteAdapter(context: Context) : SimpleCursorAdapter(context, R.layout.list_item_auto_complete, null, UserAutoCompleteAdapter.FROM, UserAutoCompleteAdapter.TO, 0) {
class UserAutoCompleteAdapter(val context: Context) : SimpleCursorAdapter(context,
R.layout.list_item_auto_complete, null, emptyArray(),
intArrayOf(), 0) {
@Inject
lateinit var profileImageLoader: MediaLoaderWrapper
@Inject
lateinit var mPreferences: SharedPreferencesWrapper
lateinit var preferences: SharedPreferencesWrapper
@Inject
lateinit var mUserColorNameManager: UserColorNameManager
lateinit var userColorNameManager: UserColorNameManager
private val mDisplayProfileImage: Boolean
private val displayProfileImage: Boolean
private var profileImageStyle: Int
private var mIdIdx: Int = 0
private var mNameIdx: Int = 0
private var mScreenNameIdx: Int = 0
private var mProfileImageIdx: Int = 0
private var mAccountKey: UserKey? = null
private var indices: ParcelableUserCursorIndices? = null
private var accountKey: UserKey? = null
init {
GeneralComponentHelper.build(context).inject(this)
mDisplayProfileImage = mPreferences.getBoolean(KEY_DISPLAY_PROFILE_IMAGE, true)
displayProfileImage = preferences[displayProfileImageKey]
profileImageStyle = preferences[profileImageStyleKey]
}
override fun bindView(view: View, context: Context?, cursor: Cursor) {
val indices = this.indices!!
val text1 = view.findViewById(android.R.id.text1) as TextView
val text2 = view.findViewById(android.R.id.text2) as TextView
val icon = view.findViewById(android.R.id.icon) as ImageView
val icon = view.findViewById(android.R.id.icon) as ProfileImageView
// Clear images in order to prevent images in recycled view shown.
icon.setImageDrawable(null)
icon.style = profileImageStyle
text1.text = mUserColorNameManager.getUserNickname(cursor.getString(mIdIdx), cursor.getString(mNameIdx))
text2.text = String.format("@%s", cursor.getString(mScreenNameIdx))
if (mDisplayProfileImage) {
val profileImageUrl = cursor.getString(mProfileImageIdx)
text1.text = userColorNameManager.getUserNickname(cursor.getString(indices.key), cursor.getString(indices.name))
@SuppressLint("SetTextI18n")
text2.text = "@${cursor.getString(indices.screen_name)}"
if (displayProfileImage) {
val profileImageUrl = cursor.getString(indices.profile_image_url)
profileImageLoader.displayProfileImage(icon, profileImageUrl)
} else {
profileImageLoader.cancelDisplayTask(icon)
}
icon.visibility = if (mDisplayProfileImage) View.VISIBLE else View.GONE
icon.visibility = if (displayProfileImage) View.VISIBLE else View.GONE
super.bindView(view, context, cursor)
}
@ -93,7 +100,7 @@ class UserAutoCompleteAdapter(context: Context) : SimpleCursorAdapter(context, R
}
override fun convertToString(cursor: Cursor?): CharSequence {
return cursor!!.getString(mScreenNameIdx)
return cursor!!.getString(indices!!.screen_name)
}
override fun runQueryOnBackgroundThread(constraint: CharSequence): Cursor? {
@ -102,7 +109,7 @@ class UserAutoCompleteAdapter(context: Context) : SimpleCursorAdapter(context, R
if (filter != null) return filter.runQuery(constraint)
val query = constraint.toString()
val queryEscaped = query.replace("_", "^_")
val nicknameKeys = Utils.getMatchedNicknameKeys(query, mUserColorNameManager)
val nicknameKeys = Utils.getMatchedNicknameKeys(query, userColorNameManager)
val usersSelection = Expression.or(
Expression.likeRaw(Columns.Column(CachedUsers.SCREEN_NAME), "?||'%'", "^"),
Expression.likeRaw(Columns.Column(CachedUsers.NAME), "?||'%'", "^"),
@ -111,31 +118,23 @@ class UserAutoCompleteAdapter(context: Context) : SimpleCursorAdapter(context, R
val order = arrayOf(CachedUsers.LAST_SEEN, CachedUsers.SCORE, CachedUsers.SCREEN_NAME, CachedUsers.NAME)
val ascending = booleanArrayOf(false, false, true, true)
val orderBy = OrderBy(order, ascending)
val uri = Uri.withAppendedPath(CachedUsers.CONTENT_URI_WITH_SCORE, mAccountKey.toString())
return mContext.contentResolver.query(uri, CachedUsers.COLUMNS, usersSelection.sql,
val uri = Uri.withAppendedPath(CachedUsers.CONTENT_URI_WITH_SCORE, accountKey.toString())
@SuppressLint("Recycle")
val cursor = context.contentResolver.query(uri, CachedUsers.COLUMNS, usersSelection.sql,
selectionArgs, orderBy.sql)
return cursor
}
fun setAccountKey(accountKey: UserKey) {
mAccountKey = accountKey
this.accountKey = accountKey
}
override fun swapCursor(cursor: Cursor?): Cursor? {
if (cursor != null) {
mIdIdx = cursor.getColumnIndex(CachedUsers.USER_KEY)
mNameIdx = cursor.getColumnIndex(CachedUsers.NAME)
mScreenNameIdx = cursor.getColumnIndex(CachedUsers.SCREEN_NAME)
mProfileImageIdx = cursor.getColumnIndex(CachedUsers.PROFILE_IMAGE_URL)
indices = ParcelableUserCursorIndices(cursor)
}
return super.swapCursor(cursor)
}
companion object {
private val FROM = arrayOfNulls<String>(0)
private val TO = IntArray(0)
}
}

View File

@ -53,6 +53,7 @@ import android.view.View.OnClickListener
import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
import kotlinx.android.synthetic.main.header_drawer_account_selector.view.*
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe
import org.mariotaku.ktextension.convert
import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe
@ -65,6 +66,7 @@ import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.annotation.CustomTabType
import org.mariotaku.twidere.annotation.Referral
import org.mariotaku.twidere.constant.KeyboardShortcutConstants.*
import org.mariotaku.twidere.constant.profileImageStyleKey
import org.mariotaku.twidere.extension.model.setActivated
import org.mariotaku.twidere.fragment.AccountsDashboardFragment.AccountsInfo
import org.mariotaku.twidere.menu.AccountToggleProvider
@ -159,7 +161,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
})
profileContainer.setOnClickListener(this)
accountProfileImageView.style = preferences[profileImageStyleKey]
accountProfileBanner.setInAnimation(context, android.R.anim.fade_in)
accountProfileBanner.setOutAnimation(context, android.R.anim.fade_out)
accountProfileBanner.setFactory {
@ -574,13 +576,17 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
internal class AccountSpaceViewHolder(itemView: View) : RecyclerPagerAdapter.ViewHolder(itemView)
internal class AccountProfileImageViewHolder(val adapter: AccountSelectorAdapter, itemView: View) : RecyclerPagerAdapter.ViewHolder(itemView), OnClickListener {
internal class AccountProfileImageViewHolder(
val adapter: AccountSelectorAdapter,
itemView: View
) : RecyclerPagerAdapter.ViewHolder(itemView), OnClickListener {
val iconView: ShapedImageView
init {
itemView.setOnClickListener(this)
iconView = itemView.findViewById(android.R.id.icon) as ShapedImageView
iconView.style = adapter.profileImageStyle
}
override fun onClick(v: View) {
@ -593,7 +599,9 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
private val inflater: LayoutInflater,
private val fragment: AccountsDashboardFragment
) : RecyclerPagerAdapter() {
private val mediaLoader: MediaLoaderWrapper
internal var profileImageStyle: Int = fragment.preferences[profileImageStyleKey]
internal var mediaLoader: MediaLoaderWrapper = fragment.mediaLoader
var accounts: Array<AccountDetails>? = null
set(value) {
@ -619,9 +627,6 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
notifyPagesChanged(invalidateCache = true)
}
init {
mediaLoader = fragment.mediaLoader
}
fun getAdapterAccount(position: Int): AccountDetails? {
return accounts?.getOrNull(position - accountStart + 1)

View File

@ -301,11 +301,11 @@ class StatusFragment : BaseSupportFragment(), LoaderCallbacks<SingleResponse<Par
HotMobiLogger.getInstance(activity).log(status.account_key, event)
}
override fun onGapClick(holder: GapViewHolder, position: Int) {
}
override fun onItemActionClick(holder: ViewHolder, id: Int, position: Int) {
val status = adapter.getStatus(position)
AbsStatusesFragment.handleStatusActionClick(context, fragmentManager, twitterWrapper,
@ -398,7 +398,6 @@ class StatusFragment : BaseSupportFragment(), LoaderCallbacks<SingleResponse<Par
repeatCount, event, metaState)
}
override fun onCreateLoader(id: Int, args: Bundle): Loader<SingleResponse<ParcelableStatus>> {
val fragmentArgs = arguments
val accountKey = fragmentArgs.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY)
@ -406,6 +405,7 @@ class StatusFragment : BaseSupportFragment(), LoaderCallbacks<SingleResponse<Par
return ParcelableStatusLoader(activity, false, fragmentArgs, accountKey, statusId)
}
override fun onLoadFinished(loader: Loader<SingleResponse<ParcelableStatus>>,
data: SingleResponse<ParcelableStatus>) {
val activity = activity ?: return
@ -746,7 +746,10 @@ class StatusFragment : BaseSupportFragment(), LoaderCallbacks<SingleResponse<Par
}
}
private class DetailStatusViewHolder(private val adapter: StatusAdapter, itemView: View) : ViewHolder(itemView), OnClickListener, ActionMenuView.OnMenuItemClickListener {
private class DetailStatusViewHolder(
private val adapter: StatusAdapter,
itemView: View
) : ViewHolder(itemView), OnClickListener, ActionMenuView.OnMenuItemClickListener {
private val linkClickHandler: StatusLinkClickHandler
private val linkify: TwidereLinkify
@ -1144,6 +1147,7 @@ class StatusFragment : BaseSupportFragment(), LoaderCallbacks<SingleResponse<Par
itemView.quotedMediaPreview.setStyle(adapter.mediaPreviewStyle)
itemView.text.customSelectionActionModeCallback = StatusActionModeCallback(itemView.text, activity)
itemView.profileImage.style = adapter.profileImageStyle
val layoutManager = LinearLayoutManager(adapter.context)
layoutManager.orientation = LinearLayoutManager.HORIZONTAL

View File

@ -81,6 +81,7 @@ import nl.komponents.kovenant.ui.successUi
import org.apache.commons.lang3.ObjectUtils
import org.mariotaku.chameleon.Chameleon
import org.mariotaku.chameleon.ChameleonUtils
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.Bundle
import org.mariotaku.ktextension.empty
import org.mariotaku.ktextension.set
@ -99,6 +100,7 @@ import org.mariotaku.twidere.adapter.SupportTabsAdapter
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.annotation.Referral
import org.mariotaku.twidere.constant.KeyboardShortcutConstants.*
import org.mariotaku.twidere.constant.profileImageStyleKey
import org.mariotaku.twidere.fragment.AbsStatusesFragment.StatusesFragmentDelegate
import org.mariotaku.twidere.fragment.UserTimelineFragment.UserTimelineFragmentDelegate
import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback
@ -706,11 +708,13 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
ViewCompat.setElevation(toolbarTabs, actionBarElevation)
setupBaseActionBar()
setupViewStyle()
setupUserPages()
getUserInfo(accountId, userId, screenName, false)
}
override fun onStart() {
super.onStart()
bus.register(this)
@ -1332,6 +1336,11 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
actionBar.setBackgroundDrawable(actionBarBackground)
}
private fun setupViewStyle() {
profileImage.style = preferences[profileImageStyleKey]
}
private fun setupUserPages() {
val args = arguments
val tabArgs = Bundle()

View File

@ -24,7 +24,6 @@ import android.support.v4.view.MarginLayoutParamsCompat
import android.support.v7.widget.RecyclerView.ViewHolder
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter
@ -35,13 +34,17 @@ import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.util.ParcelableActivityUtils
import org.mariotaku.twidere.view.BadgeView
import org.mariotaku.twidere.view.IconActionView
import org.mariotaku.twidere.view.ProfileImageView
import org.mariotaku.twidere.view.ShortTimeView
import org.mariotaku.twidere.view.iface.IColorLabelView
/**
* Created by mariotaku on 15/1/3.
*/
class ActivityTitleSummaryViewHolder(private val adapter: ParcelableActivitiesAdapter, itemView: View) : ViewHolder(itemView), View.OnClickListener {
class ActivityTitleSummaryViewHolder(
itemView: View,
private val adapter: ParcelableActivitiesAdapter
) : ViewHolder(itemView), View.OnClickListener {
private val itemContent: IColorLabelView
private val activityTypeView: IconActionView
@ -50,7 +53,7 @@ class ActivityTitleSummaryViewHolder(private val adapter: ParcelableActivitiesAd
private val timeView: ShortTimeView
private val profileImagesContainer: ViewGroup
private val profileImageMoreNumber: BadgeView
private val profileImageViews: Array<ImageView>
private val profileImageViews: Array<ProfileImageView>
private val profileImageSpace: View
private var mActivityEventListener: IActivitiesAdapter.ActivityEventListener? = null
@ -66,11 +69,11 @@ class ActivityTitleSummaryViewHolder(private val adapter: ParcelableActivitiesAd
profileImagesContainer = itemView.findViewById(R.id.profile_images_container) as ViewGroup
profileImageViews = arrayOf(
itemView.findViewById(R.id.activity_profile_image_0) as ImageView,
itemView.findViewById(R.id.activity_profile_image_1) as ImageView,
itemView.findViewById(R.id.activity_profile_image_2) as ImageView,
itemView.findViewById(R.id.activity_profile_image_3) as ImageView,
itemView.findViewById(R.id.activity_profile_image_4) as ImageView
itemView.findViewById(R.id.activity_profile_image_0) as ProfileImageView,
itemView.findViewById(R.id.activity_profile_image_1) as ProfileImageView,
itemView.findViewById(R.id.activity_profile_image_2) as ProfileImageView,
itemView.findViewById(R.id.activity_profile_image_3) as ProfileImageView,
itemView.findViewById(R.id.activity_profile_image_4) as ProfileImageView
)
profileImageMoreNumber = itemView.findViewById(R.id.activity_profile_image_more_number) as BadgeView
@ -110,10 +113,15 @@ class ActivityTitleSummaryViewHolder(private val adapter: ParcelableActivitiesAd
}
fun setTextSize(textSize: Float) {
fun setupViewOptions() {
val textSize = adapter.textSize
titleView.textSize = textSize
summaryView.textSize = textSize * 0.85f
timeView.textSize = textSize * 0.80f
profileImageViews.forEach {
it.style = adapter.profileImageStyle
}
}
private fun displayUserProfileImages(statuses: Array<ParcelableUser>?) {

View File

@ -0,0 +1,135 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder
import android.support.v7.widget.RecyclerView.ViewHolder
import android.text.TextUtils
import android.view.View
import android.widget.TextView
import kotlinx.android.synthetic.main.card_item_group_compact.view.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.iface.IGroupsAdapter
import org.mariotaku.twidere.model.ParcelableGroup
import org.mariotaku.twidere.model.util.UserKeyUtils
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.view.ColorLabelRelativeLayout
import org.mariotaku.twidere.view.NameView
import org.mariotaku.twidere.view.ProfileImageView
import java.util.*
/**
* Created by mariotaku on 15/4/29.
*/
class GroupViewHolder(private val adapter: IGroupsAdapter<*>, itemView: View) : ViewHolder(itemView), View.OnClickListener, View.OnLongClickListener {
private val itemContent: ColorLabelRelativeLayout
private val profileImageView: ProfileImageView
private val nameView: NameView
private val externalIndicator: TextView
private val descriptionView: TextView
private val membersCountView: TextView
private val adminsCountView: TextView
private var groupClickListener: IGroupsAdapter.GroupAdapterListener? = null
init {
itemContent = itemView.itemContent
profileImageView = itemView.profileImage
nameView = itemView.name
externalIndicator = itemView.externalIndicator
descriptionView = itemView.description
membersCountView = itemView.membersCount
adminsCountView = itemView.adminsCount
}
fun displayGroup(group: ParcelableGroup) {
val context = itemView.context
val loader = adapter.mediaLoader
val formatter = adapter.bidiFormatter
nameView.setName(group.fullname)
nameView.setScreenName("!" + group.nickname)
nameView.updateText(formatter)
val groupHost = UserKeyUtils.getUserHost(group.url, group.account_key.host)
if (UserKeyUtils.isSameHost(group.account_key.host, groupHost)) {
externalIndicator.visibility = View.GONE
} else {
externalIndicator.visibility = View.VISIBLE
externalIndicator.text = context.getString(R.string.external_group_host_format,
groupHost)
}
if (adapter.profileImageEnabled) {
profileImageView.visibility = View.VISIBLE
loader.displayProfileImage(profileImageView, group.homepage_logo)
} else {
profileImageView.visibility = View.GONE
loader.cancelDisplayTask(profileImageView)
}
descriptionView.visibility = if (TextUtils.isEmpty(group.description)) View.GONE else View.VISIBLE
descriptionView.text = formatter.unicodeWrap(group.description)
membersCountView.text = Utils.getLocalizedNumber(Locale.getDefault(), group.member_count)
adminsCountView.text = Utils.getLocalizedNumber(Locale.getDefault(), group.admin_count)
}
fun setOnClickListeners() {
setGroupClickListener(adapter.groupAdapterListener)
}
override fun onClick(v: View) {
val listener = groupClickListener ?: return
when (v.id) {
R.id.itemContent -> {
listener.onGroupClick(this, layoutPosition)
}
}
}
override fun onLongClick(v: View): Boolean {
val listener = groupClickListener ?: return false
when (v.id) {
R.id.itemContent -> {
return listener.onGroupLongClick(this, layoutPosition)
}
}
return false
}
fun setGroupClickListener(listener: IGroupsAdapter.GroupAdapterListener?) {
groupClickListener = listener
itemContent.setOnClickListener(this)
itemContent.setOnLongClickListener(this)
}
fun setupViewOptions() {
profileImageView.style = adapter.profileImageStyle
setTextSize(adapter.textSize)
}
fun setTextSize(textSize: Float) {
descriptionView.textSize = textSize
externalIndicator.textSize = textSize
nameView.setPrimaryTextSize(textSize)
nameView.setSecondaryTextSize(textSize * 0.75f)
membersCountView.textSize = textSize
adminsCountView.textSize = textSize
}
}

View File

@ -0,0 +1,56 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder
import android.database.Cursor
import android.view.View
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.MessageConversationAdapter
import org.mariotaku.twidere.model.ParcelableDirectMessageCursorIndices
import org.mariotaku.twidere.view.ProfileImageView
/**
* Created by mariotaku on 15/4/25.
*/
class IncomingMessageViewHolder(
adapter: MessageConversationAdapter,
itemView: View
) : MessageViewHolder(adapter, itemView) {
private val profileImageView = itemView.findViewById(R.id.profileImage) as ProfileImageView
init {
profileImageView.style = adapter.profileImageStyle
}
override fun displayMessage(cursor: Cursor, indices: ParcelableDirectMessageCursorIndices) {
super.displayMessage(cursor, indices)
val wrapper = adapter.mediaLoader
if (adapter.profileImageEnabled) {
profileImageView.visibility = View.VISIBLE
wrapper.displayProfileImage(profileImageView, cursor.getString(indices.sender_profile_image_url))
} else {
profileImageView.visibility = View.GONE
wrapper.cancelDisplayTask(profileImageView)
}
}
}

View File

@ -0,0 +1,115 @@
/*
* 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.database.Cursor
import android.graphics.Typeface
import android.support.annotation.UiThread
import android.support.v7.widget.RecyclerView.ViewHolder
import android.view.View
import android.view.View.OnClickListener
import android.widget.TextView
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.MessageEntriesAdapter
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries
import org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText
import org.mariotaku.twidere.view.NameView
import org.mariotaku.twidere.view.ProfileImageView
import org.mariotaku.twidere.view.ShortTimeView
import org.mariotaku.twidere.view.iface.IColorLabelView
class MessageEntryViewHolder(private val adapter: MessageEntriesAdapter, itemView: View) : ViewHolder(itemView), OnClickListener {
val profileImageView: ProfileImageView
val nameView: NameView
val textView: TextView
val timeView: ShortTimeView
private val content: IColorLabelView
init {
content = itemView.findViewById(R.id.content) as IColorLabelView
profileImageView = itemView.findViewById(R.id.profileImage) as ProfileImageView
nameView = itemView.findViewById(R.id.name) as NameView
textView = itemView.findViewById(R.id.text) as TextView
timeView = itemView.findViewById(R.id.time) as ShortTimeView
profileImageView.style = adapter.profileImageStyle
setTextSize(adapter.textSize)
itemView.setOnClickListener(this)
profileImageView.setOnClickListener(this)
}
@UiThread
fun displayMessage(cursor: Cursor, isUnread: Boolean) {
val context = adapter.context
val loader = adapter.mediaLoader
val manager = adapter.userColorNameManager
val accountKey = UserKey.valueOf(cursor.getString(ConversationEntries.IDX_ACCOUNT_KEY))
val conversationId = UserKey.valueOf(cursor.getString(ConversationEntries.IDX_CONVERSATION_ID))
val timestamp = cursor.getLong(ConversationEntries.IDX_MESSAGE_TIMESTAMP)
val isOutgoing = cursor.getInt(ConversationEntries.IDX_IS_OUTGOING) == 1
val name = cursor.getString(ConversationEntries.IDX_NAME)
val screenName = cursor.getString(ConversationEntries.IDX_SCREEN_NAME)
nameView.setName(manager.getUserNickname(conversationId, name))
nameView.setScreenName("@" + screenName)
nameView.updateText(adapter.bidiFormatter)
textView.text = toPlainText(cursor.getString(ConversationEntries.IDX_TEXT_UNESCAPED))
timeView.setTime(timestamp)
if (isOutgoing) {
timeView.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_indicator_sent, 0)
} else {
timeView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0)
}
nameView.setTypeface(null, if (isUnread && !isOutgoing) Typeface.BOLD else Typeface.NORMAL)
textView.setTypeface(null, if (isUnread && !isOutgoing) Typeface.BOLD else Typeface.NORMAL)
if (adapter.shouldShowAccountsColor()) {
// FIXME draw account color
} else {
content.drawEnd()
}
content.drawStart(manager.getUserColor(conversationId))
val profileImage = cursor.getString(ConversationEntries.IDX_PROFILE_IMAGE_URL)
loader.displayProfileImage(this.profileImageView, profileImage)
}
override fun onClick(v: View) {
when (v) {
profileImageView -> {
adapter.onUserProfileClick(layoutPosition)
}
itemView -> {
adapter.onMessageClick(layoutPosition)
}
}
}
fun setTextSize(textSize: Float) {
nameView.setPrimaryTextSize(textSize * 1.1f)
nameView.setSecondaryTextSize(textSize)
textView.textSize = textSize
timeView.textSize = textSize * 0.85f
}
}

View File

@ -0,0 +1,134 @@
/*
* 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.res.ColorStateList
import android.database.Cursor
import android.support.v7.widget.RecyclerView.ViewHolder
import android.text.SpannableStringBuilder
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.ParcelableDirectMessageCursorIndices
import org.mariotaku.twidere.model.ParcelableMedia
import org.mariotaku.twidere.model.SpanItem
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
import org.mariotaku.twidere.util.JsonSerializer
import org.mariotaku.twidere.util.ThemeUtils
import org.mariotaku.twidere.util.TwidereColorUtils
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.view.CardMediaContainer
open class MessageViewHolder(
protected val adapter: MessageConversationAdapter,
itemView: View
) : ViewHolder(itemView) {
val mediaContainer: CardMediaContainer
val textView: TextView
val time: TextView
private val messageContent: MessageBubbleView
private val textColorPrimary: Int
private val textColorPrimaryInverse: Int
private val textColorSecondary: Int
private val textColorSecondaryInverse: Int
init {
val context = itemView.context
val a = context.obtainStyledAttributes(R.styleable.MessageViewHolder)
textColorPrimary = a.getColor(R.styleable.MessageViewHolder_android_textColorPrimary, 0)
textColorPrimaryInverse = a.getColor(R.styleable.MessageViewHolder_android_textColorPrimaryInverse, 0)
textColorSecondary = a.getColor(R.styleable.MessageViewHolder_android_textColorSecondary, 0)
textColorSecondaryInverse = a.getColor(R.styleable.MessageViewHolder_android_textColorSecondaryInverse, 0)
a.recycle()
messageContent = itemView.findViewById(R.id.messageContent) as MessageBubbleView
messageContent.setOnLongClickListener {
itemView.parent.showContextMenuForChild(itemView)
true
}
messageContent.setOnClickListener { itemView.parent.showContextMenuForChild(itemView) }
textView = itemView.findViewById(R.id.text) as TextView
time = itemView.findViewById(R.id.time) as TextView
mediaContainer = itemView.findViewById(R.id.media_preview_container) as CardMediaContainer
mediaContainer.setStyle(adapter.mediaPreviewStyle)
}
open fun displayMessage(cursor: Cursor, indices: ParcelableDirectMessageCursorIndices) {
val context = adapter.context
val linkify = adapter.linkify
val loader = adapter.mediaLoader
val accountKey = UserKey.valueOf(cursor.getString(indices.account_key))
val timestamp = cursor.getLong(indices.timestamp)
val media = JsonSerializer.parseArray(cursor.getString(indices.media),
ParcelableMedia::class.java)
val spans = JsonSerializer.parseArray(cursor.getString(indices.spans),
SpanItem::class.java)
val text = SpannableStringBuilder.valueOf(cursor.getString(indices.text_unescaped))
ParcelableStatusUtils.applySpans(text, spans)
// Detect entity support
linkify.applyAllLinks(text, accountKey, false, true)
textView.text = text
time.text = Utils.formatToLongTimeString(context, timestamp)
mediaContainer.visibility = if (media != null && media.isNotEmpty()) View.VISIBLE else View.GONE
mediaContainer.displayMedia(loader, media, accountKey, adapter.onMediaClickListener, adapter.mediaLoadingHandler, layoutPosition.toLong(), true
)
}
fun setMessageColor(color: Int) {
val colorStateList = ColorStateList.valueOf(color)
messageContent.bubbleColor = colorStateList
val textLuminancePrimary = TwidereColorUtils.getYIQLuminance(textColorPrimary)
val textPrimaryDark: Int
val textPrimaryLight: Int
val textSecondaryDark: Int
val textSecondaryLight: Int
if (textLuminancePrimary < 128) {
textPrimaryDark = textColorPrimary
textPrimaryLight = textColorPrimaryInverse
textSecondaryDark = textColorSecondary
textSecondaryLight = textColorSecondaryInverse
} else {
textPrimaryDark = textColorPrimaryInverse
textPrimaryLight = textColorPrimary
textSecondaryDark = textColorSecondaryInverse
textSecondaryLight = textColorSecondary
}
val textContrastPrimary = TwidereColorUtils.getContrastYIQ(color,
ThemeUtils.ACCENT_COLOR_THRESHOLD, textPrimaryDark, textPrimaryLight)
val textContrastSecondary = TwidereColorUtils.getContrastYIQ(color,
ThemeUtils.ACCENT_COLOR_THRESHOLD, textSecondaryDark, textSecondaryLight)
textView.setTextColor(textContrastPrimary)
textView.setLinkTextColor(textContrastSecondary)
time.setTextColor(textContrastSecondary)
}
fun setTextSize(textSize: Float) {
textView.textSize = textSize
time.textSize = textSize * 0.75f
}
}

View File

@ -24,6 +24,7 @@ import org.mariotaku.twidere.model.util.ParcelableStatusUtils
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText
import org.mariotaku.twidere.util.Utils.getUserTypeIconRes
import org.mariotaku.twidere.view.ProfileImageView
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder
import java.lang.ref.WeakReference
@ -35,7 +36,7 @@ import java.lang.ref.WeakReference
*/
class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) : ViewHolder(itemView), Constants, IStatusViewHolder {
override val profileImageView: ImageView by lazy { itemView.profileImage }
override val profileImageView: ProfileImageView by lazy { itemView.profileImage }
override val profileTypeView: ImageView by lazy { itemView.profileType }
private val itemContent by lazy { itemView.itemContent }
@ -460,6 +461,8 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
fun setupViewOptions() {
setTextSize(adapter.textSize)
profileImageView.style = adapter.profileImageStyle
mediaPreview.setStyle(adapter.mediaPreviewStyle)
quotedMediaPreview.setStyle(adapter.mediaPreviewStyle)
// profileImageView.setStyle(adapter.getProfileImageStyle());

View File

@ -0,0 +1,126 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder
import android.support.v7.widget.RecyclerView.ViewHolder
import android.text.TextUtils
import android.view.View
import android.widget.TextView
import kotlinx.android.synthetic.main.list_item_user_list.view.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.iface.IUserListsAdapter
import org.mariotaku.twidere.model.ParcelableUserList
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.view.ColorLabelRelativeLayout
import org.mariotaku.twidere.view.ProfileImageView
import java.util.*
/**
* Created by mariotaku on 15/4/29.
*/
class UserListViewHolder(
itemView: View,
private val adapter: IUserListsAdapter<*>
) : ViewHolder(itemView), View.OnClickListener, View.OnLongClickListener {
private val itemContent: ColorLabelRelativeLayout
private val profileImageView: ProfileImageView
private val nameView: TextView
private val createdByView: TextView
private val descriptionView: TextView
private val membersCountView: TextView
private val subscribersCountView: TextView
private var userListClickListener: IUserListsAdapter.UserListClickListener? = null
init {
itemContent = itemView.itemContent
profileImageView = itemView.profileImage
nameView = itemView.name
createdByView = itemView.createdBy
descriptionView = itemView.description
membersCountView = itemView.membersCount
subscribersCountView = itemView.subscribersCount
}
fun displayUserList(userList: ParcelableUserList) {
val context = itemView.context
val loader = adapter.mediaLoader
val manager = adapter.userColorNameManager
itemContent.drawStart(manager.getUserColor(userList.user_key))
nameView.text = userList.name
val nameFirst = adapter.nameFirst
val createdByDisplayName = manager.getDisplayName(userList, nameFirst)
createdByView.text = context.getString(R.string.created_by, createdByDisplayName)
if (adapter.profileImageEnabled) {
profileImageView.visibility = View.VISIBLE
loader.displayProfileImage(profileImageView, userList.user_profile_image_url)
} else {
profileImageView.visibility = View.GONE
loader.cancelDisplayTask(profileImageView)
}
descriptionView.visibility = if (TextUtils.isEmpty(userList.description)) View.GONE else View.VISIBLE
descriptionView.text = userList.description
membersCountView.text = Utils.getLocalizedNumber(Locale.getDefault(), userList.members_count)
subscribersCountView.text = Utils.getLocalizedNumber(Locale.getDefault(), userList.subscribers_count)
}
fun setOnClickListeners() {
setUserListClickListener(adapter.userListClickListener)
}
override fun onClick(v: View) {
val listener = userListClickListener ?: return
when (v.id) {
R.id.itemContent -> {
listener.onUserListClick(this, layoutPosition)
}
}
}
override fun onLongClick(v: View): Boolean {
val listener = userListClickListener ?: return false
when (v.id) {
R.id.itemContent -> {
return listener.onUserListLongClick(this, layoutPosition)
}
}
return false
}
fun setUserListClickListener(listener: IUserListsAdapter.UserListClickListener?) {
userListClickListener = listener
itemContent.setOnClickListener(this)
itemContent.setOnLongClickListener(this)
}
fun setupViewOptions() {
profileImageView.style = adapter.profileImageStyle
setTextSize(adapter.textSize)
}
fun setTextSize(textSize: Float) {
nameView.textSize = textSize
createdByView.textSize = textSize * 0.75f
}
}

View File

@ -34,6 +34,7 @@ import org.mariotaku.twidere.model.util.UserKeyUtils
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.Utils.getUserTypeIconRes
import org.mariotaku.twidere.view.NameView
import org.mariotaku.twidere.view.ProfileImageView
import org.mariotaku.twidere.view.iface.IColorLabelView
import java.util.*
@ -44,7 +45,7 @@ class UserViewHolder(
) : ViewHolder(itemView), OnClickListener, OnLongClickListener {
private val itemContent: IColorLabelView
val profileImageView: ImageView
val profileImageView: ProfileImageView
val profileTypeView: ImageView
private val nameView: NameView
private val externalIndicator: TextView
@ -270,6 +271,7 @@ class UserViewHolder(
}
fun setupViewOptions() {
profileImageView.style = adapter.profileImageStyle
setTextSize(adapter.textSize)
}

View File

@ -17,7 +17,8 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
@ -31,7 +32,7 @@
android:layout_width="@dimen/icon_size_list_item_small"
android:layout_height="@dimen/icon_size_list_item_small"
android:contentDescription="@string/icon"
android:scaleType="fitCenter" />
android:scaleType="fitCenter"/>
<LinearLayout
android:layout_width="match_parent"
@ -47,15 +48,15 @@
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:maxLines="1"/>
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<TextView
android:id="@android:id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:maxLines="1"/>
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceSmall"/>
</LinearLayout>
</LinearLayout>

View File

@ -33,7 +33,7 @@
app:ignorePadding="true"
tools:context=".adapter.MessageEntriesAdapter">
<org.mariotaku.twidere.view.ShapedImageView
<org.mariotaku.twidere.view.ProfileImageView
android:id="@+id/profileImage"
style="?profileImageStyle"
android:layout_width="@dimen/icon_size_card_list_item"
@ -86,9 +86,9 @@
android:layout_alignParentRight="true"
android:drawablePadding="4dp"
android:gravity="center"
android:maxLines="1"
android:textColor="?android:attr/textColorSecondary"
tools:text="12:00"
android:maxLines="1"/>
tools:text="12:00"/>
<TextView
android:id="@+id/text"
@ -98,9 +98,9 @@
android:layout_alignStart="@+id/name"
android:layout_below="@+id/name"
android:layout_marginTop="@dimen/element_spacing_xsmall"
android:maxLines="1"
android:textColor="?android:attr/textColorSecondary"
tools:text="@string/sample_status_text"
android:maxLines="1"/>
tools:text="@string/sample_status_text"/>
</RelativeLayout>
</org.mariotaku.twidere.view.ColorLabelRelativeLayout>