1
0
mirror of https://github.com/TwidereProject/Twidere-Android synced 2025-02-01 09:16:47 +01:00

updated version

This commit is contained in:
Mariotaku Lee 2016-05-28 01:32:43 +08:00
parent 183a2ae752
commit 5e0d4544a5
16 changed files with 207 additions and 205 deletions

View File

@ -33,15 +33,19 @@ import org.mariotaku.library.objectcursor.annotation.CursorField;
import org.mariotaku.library.objectcursor.annotation.CursorObject;
import org.mariotaku.twidere.model.util.UserKeyConverter;
import org.mariotaku.twidere.model.util.UserKeyCursorFieldConverter;
import org.mariotaku.twidere.provider.TwidereDataStore;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
import java.util.Arrays;
@ParcelablePlease(allFields = false)
@JsonObject
@CursorObject(valuesCreator = true)
@CursorObject(valuesCreator = true, tableInfo = true)
public class ParcelableDirectMessage implements Parcelable, Comparable<ParcelableDirectMessage> {
@ParcelableThisPlease
@CursorField(value = DirectMessages._ID, type = TwidereDataStore.TYPE_PRIMARY_KEY, excludeWrite = true)
public long _id;
@ParcelableThisPlease
@JsonField(name = "account_id", typeConverter = UserKeyConverter.class)
@CursorField(value = DirectMessages.ACCOUNT_KEY, converter = UserKeyCursorFieldConverter.class)
@ -70,17 +74,18 @@ public class ParcelableDirectMessage implements Parcelable, Comparable<Parcelabl
public boolean is_outgoing;
@ParcelableThisPlease
@JsonField(name = "text_html")
@CursorField(DirectMessages.TEXT_HTML)
public String text_html;
@JsonField(name = "text_unescaped")
@CursorField(DirectMessages.TEXT_UNESCAPED)
public String text_unescaped;
@ParcelableThisPlease
@JsonField(name = "text_plain")
@CursorField(DirectMessages.TEXT_PLAIN)
public String text_plain;
@ParcelableThisPlease
@JsonField(name = "text_unescaped")
@CursorField(DirectMessages.TEXT_UNESCAPED)
public String text_unescaped;
@JsonField(name = "spans")
@CursorField(value = DirectMessages.SPANS, converter = LoganSquareCursorFieldConverter.class)
public SpanItem[] spans;
@ParcelableThisPlease
@JsonField(name = "sender_name")
@ -108,6 +113,11 @@ public class ParcelableDirectMessage implements Parcelable, Comparable<Parcelabl
@CursorField(DirectMessages.RECIPIENT_PROFILE_IMAGE_URL)
public String recipient_profile_image_url;
@ParcelableThisPlease
@JsonField(name = "conversation_id")
@CursorField(DirectMessages.CONVERSATION_ID)
public String conversation_id;
@ParcelableThisPlease
@JsonField(name = "media")
@CursorField(value = DirectMessages.MEDIA_JSON, converter = LoganSquareCursorFieldConverter.class)
@ -147,14 +157,14 @@ public class ParcelableDirectMessage implements Parcelable, Comparable<Parcelabl
public String toString() {
return "ParcelableDirectMessage{" +
"account_key=" + account_key +
", id=" + id +
", id='" + id + '\'' +
", timestamp=" + timestamp +
", sender_id=" + sender_id +
", recipient_id=" + recipient_id +
", sender_id='" + sender_id + '\'' +
", recipient_id='" + recipient_id + '\'' +
", is_outgoing=" + is_outgoing +
", text_html='" + text_html + '\'' +
", text_plain='" + text_plain + '\'' +
", text_unescaped='" + text_unescaped + '\'' +
", text_plain='" + text_plain + '\'' +
", spans=" + Arrays.toString(spans) +
", sender_name='" + sender_name + '\'' +
", recipient_name='" + recipient_name + '\'' +
", sender_screen_name='" + sender_screen_name + '\'' +

View File

@ -32,10 +32,14 @@ import org.mariotaku.commons.objectcursor.LoganSquareCursorFieldConverter;
import org.mariotaku.library.objectcursor.annotation.AfterCursorObjectCreated;
import org.mariotaku.library.objectcursor.annotation.CursorField;
import org.mariotaku.library.objectcursor.annotation.CursorObject;
import org.mariotaku.microblog.library.twitter.model.DirectMessage;
import org.mariotaku.twidere.model.util.UserKeyConverter;
import org.mariotaku.twidere.model.util.UserKeyCursorFieldConverter;
import org.mariotaku.twidere.provider.TwidereDataStore;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
import java.util.Arrays;
@ParcelablePlease(allFields = false)
@JsonObject
@ -114,17 +118,14 @@ public class ParcelableUser implements Parcelable, Comparable<ParcelableUser> {
@CursorField(CachedUsers.URL_EXPANDED)
public String url_expanded;
@ParcelableThisPlease
@JsonField(name = "description_html")
@CursorField(CachedUsers.DESCRIPTION_HTML)
public String description_html;
@ParcelableThisPlease
@JsonField(name = "description_unescaped")
@CursorField(CachedUsers.DESCRIPTION_UNESCAPED)
public String description_unescaped;
@ParcelableThisPlease
@JsonField(name = "description_expanded")
@CursorField(CachedUsers.DESCRIPTION_EXPANDED)
public String description_expanded;
@JsonField(name = "description_spans")
@CursorField(value = CachedUsers.DESCRIPTION_SPANS, converter = LoganSquareCursorFieldConverter.class)
public SpanItem[] description_spans;
@ParcelableThisPlease
@JsonField(name = "followers_count")
@ -272,9 +273,8 @@ public class ParcelableUser implements Parcelable, Comparable<ParcelableUser> {
", profile_background_url='" + profile_background_url + '\'' +
", url='" + url + '\'' +
", url_expanded='" + url_expanded + '\'' +
", description_html='" + description_html + '\'' +
", description_unescaped='" + description_unescaped + '\'' +
", description_expanded='" + description_expanded + '\'' +
", description_spans=" + Arrays.toString(description_spans) +
", followers_count=" + followers_count +
", friends_count=" + friends_count +
", statuses_count=" + statuses_count +

View File

@ -24,6 +24,7 @@ import android.net.Uri;
import android.provider.BaseColumns;
import org.mariotaku.twidere.model.ParcelableActivityTableInfo;
import org.mariotaku.twidere.model.ParcelableDirectMessageTableInfo;
import org.mariotaku.twidere.model.ParcelableStatusTableInfo;
@SuppressWarnings("unused")
@ -252,11 +253,9 @@ public interface TwidereDataStore {
String DESCRIPTION_PLAIN = "description_plain";
String DESCRIPTION_HTML = "description_html";
String DESCRIPTION_UNESCAPED = "description_unescaped";
String DESCRIPTION_EXPANDED = "description_expanded";
String DESCRIPTION_SPANS = "description_spans";
String LOCATION = "location";
@ -306,7 +305,7 @@ public interface TwidereDataStore {
String[] COLUMNS = {_ID, USER_KEY, CREATED_AT, NAME, SCREEN_NAME, DESCRIPTION_PLAIN, LOCATION,
URL, PROFILE_IMAGE_URL, PROFILE_BANNER_URL, PROFILE_BACKGROUND_URL, IS_PROTECTED,
IS_VERIFIED, IS_FOLLOWING, FOLLOWERS_COUNT, FRIENDS_COUNT, STATUSES_COUNT,
FAVORITES_COUNT, LISTED_COUNT, MEDIA_COUNT, DESCRIPTION_HTML, DESCRIPTION_EXPANDED,
FAVORITES_COUNT, LISTED_COUNT, MEDIA_COUNT, DESCRIPTION_SPANS,
URL_EXPANDED, BACKGROUND_COLOR, LINK_COLOR, TEXT_COLOR, LAST_SEEN,
DESCRIPTION_UNESCAPED, EXTRAS};
@ -315,7 +314,7 @@ public interface TwidereDataStore {
String[] TYPES = {TYPE_PRIMARY_KEY, TYPE_TEXT_NOT_NULL, TYPE_INT, TYPE_TEXT, TYPE_TEXT,
TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_BOOLEAN,
TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT,
TYPE_INT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT,
TYPE_INT, TYPE_TEXT, TYPE_TEXT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT,
TYPE_TEXT, TYPE_TEXT};
}
@ -404,9 +403,9 @@ public interface TwidereDataStore {
String IS_OUTGOING = "is_outgoing";
String TEXT_HTML = "text_html";
String TEXT_PLAIN = "text_plain";
String TEXT_UNESCAPED = "text_unescaped";
String SPANS = "spans";
String SENDER_NAME = "sender_name";
String RECIPIENT_NAME = "recipient_name";
String SENDER_SCREEN_NAME = "sender_screen_name";
@ -416,13 +415,8 @@ public interface TwidereDataStore {
String MEDIA_JSON = "media_json";
String[] COLUMNS = {_ID, ACCOUNT_KEY, MESSAGE_ID, MESSAGE_TIMESTAMP,
SENDER_ID, RECIPIENT_ID, CONVERSATION_ID, IS_OUTGOING, TEXT_HTML, TEXT_PLAIN, TEXT_UNESCAPED,
SENDER_NAME, RECIPIENT_NAME, SENDER_SCREEN_NAME, RECIPIENT_SCREEN_NAME, SENDER_PROFILE_IMAGE_URL,
RECIPIENT_PROFILE_IMAGE_URL, MEDIA_JSON, INSERTED_DATE};
String[] TYPES = {TYPE_PRIMARY_KEY, TYPE_TEXT_NOT_NULL, TYPE_TEXT_NOT_NULL, TYPE_INT,
TYPE_TEXT_NOT_NULL, TYPE_TEXT_NOT_NULL, TYPE_INT, TYPE_BOOLEAN, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT,
TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, INSERTED_DATE_TYPE};
String[] COLUMNS = ParcelableDirectMessageTableInfo.COLUMNS;
String[] TYPES = ParcelableDirectMessageTableInfo.TYPES;
String DEFAULT_SORT_ORDER = MESSAGE_ID + " DESC";
@ -460,7 +454,7 @@ public interface TwidereDataStore {
String NAME = "name";
String SCREEN_NAME = "screen_name";
String PROFILE_IMAGE_URL = "profile_image_url";
String TEXT_HTML = DirectMessages.TEXT_HTML;
String TEXT_UNESCAPED = "text_unescaped";
String CONVERSATION_ID = "conversation_id";
int IDX__ID = 0;
@ -471,7 +465,7 @@ public interface TwidereDataStore {
int IDX_NAME = 5;
int IDX_SCREEN_NAME = 6;
int IDX_PROFILE_IMAGE_URL = 7;
int IDX_TEXT = 8;
int IDX_TEXT_UNESCAPED = 8;
int IDX_CONVERSATION_ID = 9;
}

View File

@ -34,7 +34,7 @@ import static org.mariotaku.twidere.annotation.PreferenceType.STRING;
public interface Constants extends TwidereConstants {
String DATABASES_NAME = "twidere.sqlite";
int DATABASES_VERSION = 145;
int DATABASES_VERSION = 150;
int MENU_GROUP_STATUS_EXTENSION = 10;
int MENU_GROUP_COMPOSE_EXTENSION = 11;

View File

@ -58,7 +58,7 @@ import android.support.v4.view.WindowCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.util.Linkify;
import android.util.Log;
@ -125,6 +125,7 @@ import org.mariotaku.twidere.model.message.TaskStateChangedEvent;
import org.mariotaku.twidere.model.util.ParcelableAccountUtils;
import org.mariotaku.twidere.model.util.ParcelableCredentialsUtils;
import org.mariotaku.twidere.model.util.ParcelableMediaUtils;
import org.mariotaku.twidere.model.util.ParcelableStatusUtils;
import org.mariotaku.twidere.model.util.ParcelableUserUtils;
import org.mariotaku.twidere.model.util.UserKeyUtils;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedRelationships;
@ -133,7 +134,6 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Filters;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ContentValuesCreator;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.HtmlSpanBuilder;
import org.mariotaku.twidere.util.IntentUtils;
import org.mariotaku.twidere.util.InternalTwitterContentUtils;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
@ -553,16 +553,17 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
mProfileTypeView.setVisibility(View.GONE);
}
mScreenNameView.setText(String.format("@%s", user.screen_name));
mDescriptionContainer.setVisibility(TextUtils.isEmpty(user.description_html) ? View.GONE : View.VISIBLE);
final TwidereLinkify linkify = new TwidereLinkify(this);
if (user.description_html != null) {
final Spannable text = HtmlSpanBuilder.fromHtml(user.description_html);
if (user.description_unescaped != null) {
final SpannableStringBuilder text = SpannableStringBuilder.valueOf(user.description_unescaped);
ParcelableStatusUtils.applySpans(text, user.description_spans);
linkify.applyAllLinks(text, user.account_key, false, false);
mDescriptionView.setText(text);
} else {
mDescriptionView.setText(user.description_plain);
Linkify.addLinks(mDescriptionView, Linkify.WEB_URLS);
}
mDescriptionContainer.setVisibility(mDescriptionView.length() > 0 ? View.VISIBLE : View.GONE);
mLocationContainer.setVisibility(TextUtils.isEmpty(user.location) ? View.GONE : View.VISIBLE);
mLocationView.setText(user.location);

View File

@ -258,7 +258,7 @@ public class UserProfileEditorFragment extends BaseSupportFragment implements On
displayUser(user);
mEditName.setText(savedInstanceState.getString(EXTRA_NAME, user.name));
mEditLocation.setText(savedInstanceState.getString(EXTRA_LOCATION, user.location));
mEditDescription.setText(savedInstanceState.getString(EXTRA_DESCRIPTION, user.description_expanded));
mEditDescription.setText(savedInstanceState.getString(EXTRA_DESCRIPTION, ParcelableUserUtils.getExpandedDescription(user)));
mEditUrl.setText(savedInstanceState.getString(EXTRA_URL, user.url_expanded));
} else {
getUserInfo();
@ -359,7 +359,7 @@ public class UserProfileEditorFragment extends BaseSupportFragment implements On
mProgressContainer.setVisibility(View.GONE);
mEditProfileContent.setVisibility(View.VISIBLE);
mEditName.setText(user.name);
mEditDescription.setText(user.description_expanded);
mEditDescription.setText(ParcelableUserUtils.getExpandedDescription(user));
mEditLocation.setText(user.location);
mEditUrl.setText(isEmpty(user.url_expanded) ? user.url : user.url_expanded);
mMediaLoader.displayProfileImage(mProfileImageView, user);
@ -515,7 +515,7 @@ public class UserProfileEditorFragment extends BaseSupportFragment implements On
if (mLinkColor != orig.link_color) return true;
if (mBackgroundColor != orig.background_color) return true;
if (!stringEquals(mName, orig.name)) return true;
if (!stringEquals(mDescription, isEmpty(orig.description_expanded) ? orig.description_plain : orig.description_expanded))
if (!stringEquals(mDescription, ParcelableUserUtils.getExpandedDescription(orig)))
return true;
if (!stringEquals(mLocation, orig.location)) return true;
if (!stringEquals(mUrl, isEmpty(orig.url_expanded) ? orig.url : orig.url_expanded))

View File

@ -1,16 +1,17 @@
package org.mariotaku.twidere.model.util;
import android.support.v4.util.Pair;
import org.mariotaku.microblog.library.twitter.model.DirectMessage;
import org.mariotaku.microblog.library.twitter.model.User;
import org.mariotaku.twidere.model.ParcelableDirectMessage;
import org.mariotaku.twidere.model.SpanItem;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.util.InternalTwitterContentUtils;
import org.mariotaku.twidere.util.TwitterContentUtils;
import java.util.Date;
import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
/**
* Created by mariotaku on 16/2/13.
*/
@ -31,7 +32,9 @@ public class ParcelableDirectMessageUtils {
result.timestamp = getTime(message.getCreatedAt());
result.sender_id = sender.getId();
result.recipient_id = recipient.getId();
result.text_html = InternalTwitterContentUtils.formatDirectMessageText(message);
final Pair<String, SpanItem[]> pair = InternalTwitterContentUtils.formatDirectMessageText(message);
result.text_unescaped = pair.first;
result.spans = pair.second;
result.text_plain = message.getText();
result.sender_name = sender.getName();
result.recipient_name = recipient.getName();
@ -39,8 +42,12 @@ public class ParcelableDirectMessageUtils {
result.recipient_screen_name = recipient.getScreenName();
result.sender_profile_image_url = sender_profile_image_url;
result.recipient_profile_image_url = recipient_profile_image_url;
result.text_unescaped = toPlainText(result.text_html);
result.media = ParcelableMediaUtils.fromEntities(message);
if (isOutgoing) {
result.conversation_id = result.recipient_id;
} else {
result.conversation_id = result.sender_id;
}
return result;
}

View File

@ -3,7 +3,7 @@ package org.mariotaku.twidere.model.util;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.Pair;
import android.text.SpannableStringBuilder;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.URLSpan;
@ -25,7 +25,6 @@ import org.mariotaku.twidere.util.TwitterContentUtils;
import org.mariotaku.twidere.util.UserColorNameManager;
import java.util.Date;
import java.util.List;
import static org.mariotaku.twidere.TwidereConstants.USER_TYPE_FANFOU_COM;
@ -91,10 +90,10 @@ public class ParcelableStatusUtils {
result.quoted_text_plain = result.quoted_text_unescaped;
result.quoted_spans = getSpanItems(html);
} else {
final Pair<String, List<SpanItem>> textWithIndices = InternalTwitterContentUtils.formatStatusTextWithIndices(quoted);
final Pair<String, SpanItem[]> textWithIndices = InternalTwitterContentUtils.formatStatusTextWithIndices(quoted);
result.quoted_text_plain = InternalTwitterContentUtils.unescapeTwitterStatusText(quotedText);
result.quoted_text_unescaped = textWithIndices.first;
result.quoted_spans = textWithIndices.second.toArray(new SpanItem[textWithIndices.second.size()]);
result.quoted_spans = textWithIndices.second;
}
result.quoted_timestamp = quoted.getCreatedAt().getTime();
@ -158,10 +157,10 @@ public class ParcelableStatusUtils {
result.text_plain = result.text_unescaped;
result.spans = getSpanItems(html);
} else {
final Pair<String, List<SpanItem>> textWithIndices = InternalTwitterContentUtils.formatStatusTextWithIndices(status);
final Pair<String, SpanItem[]> textWithIndices = InternalTwitterContentUtils.formatStatusTextWithIndices(status);
result.text_unescaped = textWithIndices.first;
result.text_plain = InternalTwitterContentUtils.unescapeTwitterStatusText(text);
result.spans = textWithIndices.second.toArray(new SpanItem[textWithIndices.second.size()]);
result.spans = textWithIndices.second;
}
result.media = ParcelableMediaUtils.fromStatus(status);
result.source = status.getSource();
@ -284,7 +283,7 @@ public class ParcelableStatusUtils {
return status.getInReplyToScreenName();
}
public static void applySpans(@NonNull SpannableStringBuilder text, @Nullable SpanItem[] spans) {
public static void applySpans(@NonNull Spannable text, @Nullable SpanItem[] spans) {
if (spans == null) return;
for (SpanItem span : spans) {
text.setSpan(new URLSpan(span.link), span.start, span.end,

View File

@ -3,6 +3,7 @@ package org.mariotaku.twidere.model.util;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.Pair;
import android.text.TextUtils;
import org.mariotaku.microblog.library.twitter.model.UrlEntity;
@ -10,9 +11,9 @@ import org.mariotaku.microblog.library.twitter.model.User;
import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.ParcelableUser;
import org.mariotaku.twidere.model.SpanItem;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
import org.mariotaku.twidere.util.HtmlEscapeHelper;
import org.mariotaku.twidere.util.InternalTwitterContentUtils;
import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.TwitterContentUtils;
@ -43,9 +44,9 @@ public class ParcelableUserUtils implements TwidereConstants {
obj.name = user.getName();
obj.screen_name = user.getScreenName();
obj.description_plain = user.getDescription();
obj.description_html = InternalTwitterContentUtils.formatUserDescription(user);
obj.description_expanded = InternalTwitterContentUtils.formatExpandedUserDescription(user);
obj.description_unescaped = HtmlEscapeHelper.toPlainText(obj.description_html);
final Pair<String, SpanItem[]> userDescription = InternalTwitterContentUtils.formatUserDescription(user);
obj.description_unescaped = userDescription.first;
obj.description_spans = userDescription.second;
obj.location = user.getLocation();
obj.profile_image_url = TwitterContentUtils.getProfileImageUrl(user);
obj.profile_banner_url = user.getProfileBannerImageUrl();
@ -129,4 +130,14 @@ public class ParcelableUserUtils implements TwidereConstants {
user.color = manager.getUserColor(user.key);
user.nickname = manager.getUserNickname(user.key);
}
public static String getExpandedDescription(ParcelableUser user) {
if (TextUtils.isEmpty(user.description_unescaped)) {
return user.description_plain;
}
if (user.description_spans != null) {
// TODO expand description
}
return user.description_unescaped;
}
}

View File

@ -38,7 +38,6 @@ import org.mariotaku.twidere.model.ParcelableActivityValuesCreator;
import org.mariotaku.twidere.model.ParcelableCredentials;
import org.mariotaku.twidere.model.ParcelableDirectMessage;
import org.mariotaku.twidere.model.ParcelableDirectMessageValuesCreator;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableMediaUpdate;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.ParcelableStatusValuesCreator;
@ -48,11 +47,10 @@ import org.mariotaku.twidere.model.ParcelableUserValuesCreator;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtra;
import org.mariotaku.twidere.model.util.ParcelableActivityUtils;
import org.mariotaku.twidere.model.util.ParcelableMediaUtils;
import org.mariotaku.twidere.model.util.ParcelableDirectMessageUtils;
import org.mariotaku.twidere.model.util.ParcelableStatusUtils;
import org.mariotaku.twidere.model.util.ParcelableUserUtils;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedTrends;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
import org.mariotaku.twidere.provider.TwidereDataStore.Filters;
import org.mariotaku.twidere.provider.TwidereDataStore.SavedSearches;
@ -82,37 +80,8 @@ public final class ContentValuesCreator implements TwidereConstants {
public static ContentValues createDirectMessage(final DirectMessage message,
final UserKey accountKey,
final boolean isOutgoing) {
if (message == null) return null;
final ContentValues values = new ContentValues();
final User sender = message.getSender(), recipient = message.getRecipient();
if (sender == null || recipient == null) return null;
final String sender_profile_image_url = TwitterContentUtils.getProfileImageUrl(sender);
final String recipient_profile_image_url = TwitterContentUtils.getProfileImageUrl(recipient);
values.put(DirectMessages.ACCOUNT_KEY, accountKey.toString());
values.put(DirectMessages.MESSAGE_ID, message.getId());
values.put(DirectMessages.MESSAGE_TIMESTAMP, message.getCreatedAt().getTime());
values.put(DirectMessages.SENDER_ID, sender.getId());
values.put(DirectMessages.RECIPIENT_ID, recipient.getId());
if (isOutgoing) {
values.put(DirectMessages.CONVERSATION_ID, recipient.getId());
} else {
values.put(DirectMessages.CONVERSATION_ID, sender.getId());
}
final String text_html = InternalTwitterContentUtils.formatDirectMessageText(message);
values.put(DirectMessages.TEXT_HTML, text_html);
values.put(DirectMessages.TEXT_PLAIN, message.getText());
values.put(DirectMessages.TEXT_UNESCAPED, HtmlEscapeHelper.toPlainText(text_html));
values.put(DirectMessages.IS_OUTGOING, isOutgoing);
values.put(DirectMessages.SENDER_NAME, sender.getName());
values.put(DirectMessages.SENDER_SCREEN_NAME, sender.getScreenName());
values.put(DirectMessages.RECIPIENT_NAME, recipient.getName());
values.put(DirectMessages.RECIPIENT_SCREEN_NAME, recipient.getScreenName());
values.put(DirectMessages.SENDER_PROFILE_IMAGE_URL, sender_profile_image_url);
values.put(DirectMessages.RECIPIENT_PROFILE_IMAGE_URL, recipient_profile_image_url);
final ParcelableMedia[] mediaArray = ParcelableMediaUtils.fromEntities(message);
values.put(DirectMessages.MEDIA_JSON, JsonSerializer.serialize(Arrays.asList(mediaArray),
ParcelableMedia.class));
return values;
return ParcelableDirectMessageValuesCreator.create(ParcelableDirectMessageUtils.fromDirectMessage(message,
accountKey, isOutgoing));
}
public static ContentValues createDirectMessage(final ParcelableDirectMessage message) {

View File

@ -26,7 +26,6 @@ import org.mariotaku.twidere.model.SpanItem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import static android.text.TextUtils.isEmpty;
@ -39,7 +38,7 @@ public class HtmlBuilder {
private final int sourceLength;
private final boolean throwExceptions, sourceIsEscaped, shouldReEscape;
private final ArrayList<LinkSpec> links = new ArrayList<>();
private final ArrayList<SpanSpec> spanSpecs = new ArrayList<>();
public HtmlBuilder(final String source, final boolean strict, final boolean sourceIsEscaped,
final boolean shouldReEscape) {
@ -61,7 +60,7 @@ public class HtmlBuilder {
}
public boolean addLink(final String link, final String display, final int start, final int end,
final boolean display_is_html) {
final boolean displayIsHtml) {
if (start < 0 || end < 0 || start > end || end > sourceLength) {
final String message = String.format(Locale.US, "text:%s, length:%d, start:%d, end:%d", source,
sourceLength, start, end);
@ -75,83 +74,39 @@ public class HtmlBuilder {
if (throwExceptions) throw new IllegalArgumentException(message);
return false;
}
return links.add(new LinkSpec(link, display, start, end, display_is_html));
return spanSpecs.add(new LinkSpec(link, display, start, end, displayIsHtml));
}
public String build() {
if (links.isEmpty()) return escapeSource();
Collections.sort(links);
public Pair<String, SpanItem[]> buildWithIndices() {
if (spanSpecs.isEmpty()) return Pair.create(escapeSource(), new SpanItem[0]);
Collections.sort(spanSpecs);
final StringBuilder sb = new StringBuilder();
final int linksSize = links.size();
final int linksSize = spanSpecs.size();
SpanItem[] items = new SpanItem[linksSize];
for (int i = 0; i < linksSize; i++) {
final LinkSpec spec = links.get(i);
if (spec == null) {
continue;
}
final int start = spec.start, end = spec.end;
if (i == 0) {
if (start >= 0 && start <= sourceLength) {
appendSource(sb, 0, start, shouldReEscape, sourceIsEscaped);
}
} else if (i > 0) {
final int lastEnd = links.get(i - 1).end;
if (lastEnd >= 0 && lastEnd <= start && start <= sourceLength) {
appendSource(sb, lastEnd, start, shouldReEscape, sourceIsEscaped);
}
}
sb.append("<a href=\"");
sb.append(spec.link);
sb.append("\">");
if (start >= 0 && start <= end && end <= sourceLength) {
if (isEmpty(spec.display)) {
append(sb, spec.link, shouldReEscape, false);
} else {
append(sb, spec.display, shouldReEscape, spec.displayIsHtml);
}
}
sb.append("</a>");
if (i == linksSize - 1 && end >= 0 && end <= sourceLength) {
appendSource(sb, end, sourceLength, shouldReEscape, sourceIsEscaped);
}
}
return sb.toString();
}
public Pair<String, List<SpanItem>> buildWithIndices() {
List<SpanItem> items = new ArrayList<>();
if (links.isEmpty()) return Pair.create(escapeSource(), items);
Collections.sort(links);
final StringBuilder sb = new StringBuilder();
final int linksSize = links.size();
for (int i = 0; i < linksSize; i++) {
final LinkSpec spec = links.get(i);
if (spec == null) {
continue;
}
final int start = spec.start, end = spec.end;
final SpanSpec spec = spanSpecs.get(i);
final int start = spec.getStart(), end = spec.getEnd();
if (i == 0) {
if (start >= 0 && start <= sourceLength) {
appendSource(sb, 0, start, false, sourceIsEscaped);
}
} else if (i > 0) {
final int lastEnd = links.get(i - 1).end;
final int lastEnd = spanSpecs.get(i - 1).end;
if (lastEnd >= 0 && lastEnd <= start && start <= sourceLength) {
appendSource(sb, lastEnd, start, false, sourceIsEscaped);
}
}
int spanStart = sb.length();
if (start >= 0 && start <= end && end <= sourceLength) {
if (isEmpty(spec.display)) {
append(sb, spec.link, false, false);
} else {
append(sb, spec.display, false, spec.displayIsHtml);
}
spec.appendTo(sb);
}
final SpanItem item = new SpanItem();
item.start = spanStart;
item.end = sb.length();
item.link = spec.link;
items.add(item);
if (spec instanceof LinkSpec) {
item.link = ((LinkSpec) spec).link;
}
items[i] = item;
if (i == linksSize - 1 && end >= 0 && end <= sourceLength) {
appendSource(sb, end, sourceLength, false, sourceIsEscaped);
}
@ -160,9 +115,11 @@ public class HtmlBuilder {
}
public boolean hasLink(final int start, final int end) {
for (final LinkSpec spec : links) {
if (start >= spec.start && start <= spec.end || end >= spec.start && end <= spec.end)
for (final SpanSpec spec : spanSpecs) {
final int specStart = spec.getStart(), specEnd = spec.getEnd();
if (start >= specStart && start <= specEnd || end >= specStart && end <= specEnd) {
return true;
}
}
return false;
}
@ -175,7 +132,7 @@ public class HtmlBuilder {
", throwExceptions=" + throwExceptions +
", sourceIsEscaped=" + sourceIsEscaped +
", shouldReEscape=" + shouldReEscape +
", links=" + links +
", links=" + spanSpecs +
'}';
}
@ -189,7 +146,7 @@ public class HtmlBuilder {
}
}
private void append(final StringBuilder builder, final String text, boolean escapeText, boolean textEscaped) {
private static void append(final StringBuilder builder, final String text, boolean escapeText, boolean textEscaped) {
if (textEscaped == escapeText) {
builder.append(text);
} else if (escapeText) {
@ -205,52 +162,101 @@ public class HtmlBuilder {
return shouldReEscape ? escape(source) : unescape(source);
}
static final class LinkSpec implements Comparable<LinkSpec> {
static abstract class SpanSpec implements Comparable<SpanSpec> {
final String link, display;
final int start, end;
final boolean displayIsHtml;
LinkSpec(final String link, final String display, final int start, final int end, final boolean displayIsHtml) {
this.link = link;
this.display = display;
public final int getStart() {
return start;
}
public final int getEnd() {
return end;
}
public SpanSpec(int start, int end) {
this.start = start;
this.end = end;
this.displayIsHtml = displayIsHtml;
}
@Override
public int compareTo(@NonNull final LinkSpec that) {
public int compareTo(@NonNull final SpanSpec that) {
return start - that.start;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof LinkSpec)) return false;
final LinkSpec other = (LinkSpec) obj;
if (display == null) {
if (other.display != null) return false;
} else if (!display.equals(other.display)) return false;
if (displayIsHtml != other.displayIsHtml) return false;
if (end != other.end) return false;
if (link == null) {
if (other.link != null) return false;
} else if (!link.equals(other.link)) return false;
if (start != other.start) return false;
return true;
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SpanSpec spanSpec = (SpanSpec) o;
if (start != spanSpec.start) return false;
return end == spanSpec.end;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (display == null ? 0 : display.hashCode());
result = prime * result + (displayIsHtml ? 1231 : 1237);
result = prime * result + end;
result = prime * result + (link == null ? 0 : link.hashCode());
result = prime * result + start;
int result = start;
result = 31 * result + end;
return result;
}
@Override
public String toString() {
return "SpanSpec{" +
"start=" + start +
", end=" + end +
'}';
}
public abstract void appendTo(StringBuilder sb);
}
static final class LinkSpec extends SpanSpec {
final String link, display;
final boolean displayIsHtml;
LinkSpec(final String link, final String display, final int start, final int end, final boolean displayIsHtml) {
super(start, end);
this.link = link;
this.display = display;
this.displayIsHtml = displayIsHtml;
}
@Override
public void appendTo(StringBuilder sb) {
if (isEmpty(display)) {
append(sb, link, false, false);
} else {
append(sb, display, false, displayIsHtml);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
LinkSpec linkSpec = (LinkSpec) o;
if (displayIsHtml != linkSpec.displayIsHtml) return false;
if (link != null ? !link.equals(linkSpec.link) : linkSpec.link != null) return false;
return display != null ? display.equals(linkSpec.display) : linkSpec.display == null;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (link != null ? link.hashCode() : 0);
result = 31 * result + (display != null ? display.hashCode() : 0);
result = 31 * result + (displayIsHtml ? 1 : 0);
return result;
}
@ -259,10 +265,8 @@ public class HtmlBuilder {
return "LinkSpec{" +
"link='" + link + '\'' +
", display='" + display + '\'' +
", start=" + start +
", end=" + end +
", displayIsHtml=" + displayIsHtml +
'}';
"} " + super.toString();
}
}

View File

@ -250,10 +250,10 @@ public class InternalTwitterContentUtils {
}
}
}
return HtmlEscapeHelper.toPlainText(builder.build());
return builder.buildWithIndices().first;
}
public static String formatUserDescription(final User user) {
public static Pair<String, SpanItem[]> formatUserDescription(final User user) {
if (user == null) return null;
final String text = user.getDescription();
if (text == null) return null;
@ -267,7 +267,7 @@ public class InternalTwitterContentUtils {
}
}
}
return builder.build();
return builder.buildWithIndices();
}
public static String unescapeTwitterStatusText(final CharSequence text) {
@ -275,14 +275,14 @@ public class InternalTwitterContentUtils {
return UNESCAPE_TWITTER_RAW_TEXT.translate(text);
}
public static String formatDirectMessageText(final DirectMessage message) {
public static Pair<String, SpanItem[]> formatDirectMessageText(final DirectMessage message) {
if (message == null) return null;
final HtmlBuilder builder = new HtmlBuilder(message.getText(), false, true, true);
parseEntities(builder, message, null);
return builder.build();
return builder.buildWithIndices();
}
public static Pair<String, List<SpanItem>> formatStatusTextWithIndices(final Status status) {
public static Pair<String, SpanItem[]> formatStatusTextWithIndices(final Status status) {
if (status == null) return null;
//TODO handle twitter video url

View File

@ -222,7 +222,7 @@ public class TwidereQueryBuilder {
new Column(ConversationEntries.NAME),
new Column(ConversationEntries.SCREEN_NAME),
new Column(ConversationEntries.PROFILE_IMAGE_URL),
new Column(ConversationEntries.TEXT_HTML),
new Column(ConversationEntries.TEXT_UNESCAPED),
new Column(ConversationEntries.CONVERSATION_ID)));
final SQLSelectQuery.Builder entryIds = new SQLSelectQuery.Builder();
entryIds.select(new Columns(new Column(DirectMessages._ID),
@ -233,7 +233,7 @@ public class TwidereQueryBuilder {
new Column(DirectMessages.SENDER_NAME, ConversationEntries.NAME),
new Column(DirectMessages.SENDER_SCREEN_NAME, ConversationEntries.SCREEN_NAME),
new Column(DirectMessages.SENDER_PROFILE_IMAGE_URL, ConversationEntries.PROFILE_IMAGE_URL),
new Column(DirectMessages.TEXT_HTML),
new Column(DirectMessages.TEXT_UNESCAPED),
new Column(DirectMessages.SENDER_ID, ConversationEntries.CONVERSATION_ID)));
entryIds.from(new Tables(Inbox.TABLE_NAME));
entryIds.union();
@ -245,7 +245,7 @@ public class TwidereQueryBuilder {
new Column(DirectMessages.RECIPIENT_NAME, ConversationEntries.NAME),
new Column(DirectMessages.RECIPIENT_SCREEN_NAME, ConversationEntries.SCREEN_NAME),
new Column(DirectMessages.RECIPIENT_PROFILE_IMAGE_URL, ConversationEntries.PROFILE_IMAGE_URL),
new Column(DirectMessages.TEXT_HTML),
new Column(DirectMessages.TEXT_UNESCAPED),
new Column(DirectMessages.RECIPIENT_ID, ConversationEntries.CONVERSATION_ID)));
entryIds.from(new Tables(Outbox.TABLE_NAME));
qb.from(entryIds.build());

View File

@ -122,6 +122,9 @@ public final class TwidereSQLiteOpenHelper extends SQLiteOpenHelper implements C
}
private void createViews(SQLiteDatabase db) {
db.execSQL(SQLQueryBuilder.dropView(true, DirectMessages.TABLE_NAME).getSQL());
db.execSQL(SQLQueryBuilder.dropView(true, DirectMessages.ConversationEntries.TABLE_NAME).getSQL());
db.execSQL(SQLQueryBuilder.createView(true, DirectMessages.TABLE_NAME)
.as(DirectMessagesQueryBuilder.build()).buildSQL());
db.execSQL(SQLQueryBuilder.createView(true, DirectMessages.ConversationEntries.TABLE_NAME)

View File

@ -81,7 +81,7 @@ public class MessageEntryViewHolder extends ViewHolder implements OnClickListene
nameView.setName(manager.getUserNickname(conversationId, name));
nameView.setScreenName("@" + screenName);
nameView.updateText(adapter.getBidiFormatter());
textView.setText(toPlainText(cursor.getString(ConversationEntries.IDX_TEXT)));
textView.setText(toPlainText(cursor.getString(ConversationEntries.IDX_TEXT_UNESCAPED)));
timeView.setTime(timestamp);
if (isOutgoing) {
timeView.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_indicator_sent, 0);

View File

@ -24,7 +24,7 @@ import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.view.View;
import android.widget.TextView;
@ -33,8 +33,9 @@ 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.util.HtmlSpanBuilder;
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;
@ -93,7 +94,10 @@ public class MessageViewHolder extends ViewHolder {
final long timestamp = cursor.getLong(indices.timestamp);
final ParcelableMedia[] media = JsonSerializer.parseArray(cursor.getString(indices.media),
ParcelableMedia.class);
final Spannable text = HtmlSpanBuilder.fromHtml(cursor.getString(indices.text_html));
final SpanItem[] spans = JsonSerializer.parseArray(cursor.getString(indices.spans),
SpanItem.class);
final SpannableStringBuilder text = SpannableStringBuilder.valueOf(cursor.getString(indices.text_unescaped));
ParcelableStatusUtils.applySpans(text, spans);
// Detect entity support
linkify.applyAllLinks(text, accountKey, false, true);
textView.setText(text);