From 6f6dec7bb61dc7ff3576db4eff5be02f54a0f3d1 Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Fri, 25 Dec 2015 17:54:13 +0800 Subject: [PATCH] fixing leaks --- .../api/twitter/model/Contributor.java | 42 ++ .../twidere/api/twitter/model/Status.java | 29 +- .../twidere/model/ParcelableStatus.java | 588 ++++++++---------- .../twidere/provider/TwidereDataStore.java | 6 +- .../edu/tsinghua/hotmobi/HotMobiLogger.java | 29 +- .../edu/tsinghua/hotmobi/PreProcessing.java | 29 + .../edu/tsinghua/hotmobi/UploadLogsTask.java | 1 - .../edu/tsinghua/hotmobi/WriteLogTask.java | 20 +- .../java/org/mariotaku/twidere/Constants.java | 2 +- .../activity/support/MediaViewerActivity.java | 7 +- .../activity/support/SignInActivity.java | 22 +- .../twidere/adapter/AbsActivitiesAdapter.java | 8 +- .../fragment/support/StatusFragment.java | 21 +- .../twidere/util/ActivityTracker.java | 12 +- .../twidere/util/EmojiSupportUtils.java | 60 +- .../twidere/util/TwitterAPIFactory.java | 9 +- .../twidere/util/net/NetworkUsageUtils.java | 22 +- .../twidere/util/net/TwidereDns.java | 22 +- .../mariotaku/twidere/view/ShortTimeView.java | 12 +- .../view/holder/MessageViewHolder.java | 8 - .../twidere/view/holder/StatusViewHolder.java | 6 +- .../res/layout/activity_quick_search_bar.xml | 15 +- .../card_item_message_conversation_common.xml | 15 +- .../res/layout/card_item_status_compact.xml | 45 +- .../res/layout/spinner_item_account_icon.xml | 9 +- 25 files changed, 594 insertions(+), 445 deletions(-) create mode 100644 twidere.component.common/src/main/java/org/mariotaku/twidere/api/twitter/model/Contributor.java create mode 100644 twidere/src/main/java/edu/tsinghua/hotmobi/PreProcessing.java diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/api/twitter/model/Contributor.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/api/twitter/model/Contributor.java new file mode 100644 index 000000000..21069991e --- /dev/null +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/api/twitter/model/Contributor.java @@ -0,0 +1,42 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * 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 . + */ + +package org.mariotaku.twidere.api.twitter.model; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +/** + * Created by mariotaku on 15/12/25. + */ +@JsonObject +public class Contributor { + @JsonField(name = "id") + long id; + @JsonField(name = "screen_name") + String screenName; + + public long getId() { + return id; + } + + public String getScreenName() { + return screenName; + } +} diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/api/twitter/model/Status.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/api/twitter/model/Status.java index f5a952f8f..6d2a17f59 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/api/twitter/model/Status.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/api/twitter/model/Status.java @@ -20,12 +20,12 @@ package org.mariotaku.twidere.api.twitter.model; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.bluelinelabs.logansquare.annotation.JsonField; import com.bluelinelabs.logansquare.annotation.JsonObject; import com.bluelinelabs.logansquare.annotation.OnJsonParseComplete; -import org.apache.commons.lang3.builder.CompareToBuilder; import org.mariotaku.twidere.api.twitter.util.TwitterDateConverter; import java.io.IOException; @@ -81,8 +81,9 @@ public class Status extends TwitterResponseObject implements Comparable, @JsonField(name = "current_user_retweet") CurrentUserRetweet currentUserRetweet; + @Nullable @JsonField(name = "contributors") - long[] contributors; + Contributor[] contributors; @JsonField(name = "retweet_count") long retweetCount; @@ -127,25 +128,21 @@ public class Status extends TwitterResponseObject implements Comparable, public String getInReplyToScreenName() { - return inReplyToScreenName; } public long getInReplyToUserId() { - return inReplyToUserId; } public long getInReplyToStatusId() { - return inReplyToStatusId; } public boolean isTruncated() { - return truncated; } @@ -157,11 +154,12 @@ public class Status extends TwitterResponseObject implements Comparable, public String getSource() { - return source; } - + /** + * UTC time when this Tweet was created. + */ public Date getCreatedAt() { return createdAt; } @@ -201,6 +199,10 @@ public class Status extends TwitterResponseObject implements Comparable, return currentUserRetweet != null; } + public boolean wasRetweeted() { + return retweeted; + } + public long getFavoriteCount() { return favoriteCount; @@ -213,6 +215,10 @@ public class Status extends TwitterResponseObject implements Comparable, } + /** + * Perspectival. Only surfaces on methods supporting the include_my_retweet parameter, + * when set to true. Details the Tweet ID of the user’s own retweet (if existent) of this Tweet. + */ public long getCurrentUserRetweet() { if (currentUserRetweet == null) return -1; return currentUserRetweet.id; @@ -279,7 +285,12 @@ public class Status extends TwitterResponseObject implements Comparable, } - public long[] getContributors() { + /** + * An collection of brief user objects (usually only one) indicating users who contributed to + * the authorship of the tweet, on behalf of the official tweet author. + */ + @Nullable + public Contributor[] getContributors() { return contributors; } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableStatus.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableStatus.java index 7718a4124..efb11be80 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableStatus.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableStatus.java @@ -62,6 +62,244 @@ import java.util.Map.Entry; @JsonObject @ParcelablePlease public class ParcelableStatus implements Parcelable, Comparable, Cloneable { + @ParcelableThisPlease + @JsonField(name = "id") + @CursorField(Statuses.STATUS_ID) + public long id; + public static final Comparator REVERSE_ID_COMPARATOR = new Comparator() { + + @Override + public int compare(final ParcelableStatus object1, final ParcelableStatus object2) { + final long diff = object1.id - object2.id; + if (diff > Integer.MAX_VALUE) return Integer.MAX_VALUE; + if (diff < Integer.MIN_VALUE) return Integer.MIN_VALUE; + return (int) diff; + } + }; + @ParcelableThisPlease + @JsonField(name = "account_id") + @CursorField(Statuses.ACCOUNT_ID) + public long account_id; + @ParcelableThisPlease + @JsonField(name = "timestamp") + @CursorField(Statuses.STATUS_TIMESTAMP) + public long timestamp; + public static final Comparator TIMESTAMP_COMPARATOR = new Comparator() { + + @Override + public int compare(final ParcelableStatus object1, final ParcelableStatus object2) { + final long diff = object2.timestamp - object1.timestamp; + if (diff > Integer.MAX_VALUE) return Integer.MAX_VALUE; + if (diff < Integer.MIN_VALUE) return Integer.MIN_VALUE; + return (int) diff; + } + }; + @ParcelableThisPlease + @JsonField(name = "user_id") + @CursorField(Statuses.USER_ID) + public long user_id; + @ParcelableThisPlease + @JsonField(name = "retweet_id") + @CursorField(Statuses.RETWEET_ID) + public long retweet_id; + @ParcelableThisPlease + @JsonField(name = "retweeted_by_user_id") + @CursorField(Statuses.RETWEETED_BY_USER_ID) + public long retweeted_by_user_id; + @ParcelableThisPlease + @JsonField(name = "retweet_timestamp") + @CursorField(Statuses.RETWEET_TIMESTAMP) + public long retweet_timestamp; + @ParcelableThisPlease + @JsonField(name = "retweet_count") + @CursorField(Statuses.RETWEET_COUNT) + public long retweet_count; + @ParcelableThisPlease + @JsonField(name = "favorite_count") + @CursorField(Statuses.FAVORITE_COUNT) + public long favorite_count; + @ParcelableThisPlease + @JsonField(name = "reply_count") + @CursorField(Statuses.REPLY_COUNT) + public long reply_count; + @ParcelableThisPlease + @JsonField(name = "in_reply_to_status_id") + @CursorField(Statuses.IN_REPLY_TO_STATUS_ID) + public long in_reply_to_status_id; + @ParcelableThisPlease + @JsonField(name = "in_reply_to_user_id") + @CursorField(Statuses.IN_REPLY_TO_USER_ID) + public long in_reply_to_user_id; + @ParcelableThisPlease + @JsonField(name = "my_retweet_id") + @CursorField(Statuses.MY_RETWEET_ID) + public long my_retweet_id; + @ParcelableThisPlease + @JsonField(name = "quoted_id") + @CursorField(Statuses.QUOTED_ID) + public long quoted_id; + @ParcelableThisPlease + @JsonField(name = "quoted_timestamp") + @CursorField(Statuses.QUOTED_TIMESTAMP) + public long quoted_timestamp; + @ParcelableThisPlease + @JsonField(name = "quoted_user_id") + @CursorField(Statuses.QUOTED_USER_ID) + public long quoted_user_id; + @ParcelableThisPlease + @JsonField(name = "is_gap") + @CursorField(Statuses.IS_GAP) + public boolean is_gap; + @ParcelableThisPlease + @JsonField(name = "is_retweet") + @CursorField(Statuses.IS_RETWEET) + public boolean is_retweet; + @ParcelableThisPlease + @JsonField(name = "retweeted") + @CursorField(Statuses.RETWEETED) + public boolean retweeted; + @ParcelableThisPlease + @JsonField(name = "is_favorite") + @CursorField(Statuses.IS_FAVORITE) + public boolean is_favorite; + @ParcelableThisPlease + @JsonField(name = "is_possibly_sensitive") + @CursorField(Statuses.IS_POSSIBLY_SENSITIVE) + public boolean is_possibly_sensitive; + @ParcelableThisPlease + @JsonField(name = "user_is_following") + @CursorField(Statuses.IS_FOLLOWING) + public boolean user_is_following; + @ParcelableThisPlease + @JsonField(name = "user_is_protected") + @CursorField(Statuses.IS_PROTECTED) + public boolean user_is_protected; + @ParcelableThisPlease + @JsonField(name = "user_is_verified") + @CursorField(Statuses.IS_VERIFIED) + public boolean user_is_verified; + @ParcelableThisPlease + @JsonField(name = "is_quote") + @CursorField(Statuses.IS_QUOTE) + public boolean is_quote; + @ParcelableThisPlease + @JsonField(name = "quoted_user_is_protected") + @CursorField(Statuses.QUOTED_USER_IS_PROTECTED) + public boolean quoted_user_is_protected; + @ParcelableThisPlease + @JsonField(name = "quoted_user_is_verified") + @CursorField(Statuses.QUOTED_USER_IS_VERIFIED) + public boolean quoted_user_is_verified; + @ParcelableThisPlease + @JsonField(name = "retweeted_by_user_name") + @CursorField(Statuses.RETWEETED_BY_USER_NAME) + public String retweeted_by_user_name; + @ParcelableThisPlease + @JsonField(name = "retweeted_by_user_screen_name") + @CursorField(Statuses.RETWEETED_BY_USER_SCREEN_NAME) + public String retweeted_by_user_screen_name; + @ParcelableThisPlease + @JsonField(name = "retweeted_by_user_profile_image") + @CursorField(Statuses.RETWEETED_BY_USER_PROFILE_IMAGE) + public String retweeted_by_user_profile_image; + @ParcelableThisPlease + @JsonField(name = "text_html") + @CursorField(Statuses.TEXT_HTML) + public String text_html; + @ParcelableThisPlease + @JsonField(name = "text_plain") + @CursorField(Statuses.TEXT_PLAIN) + public String text_plain; + @ParcelableThisPlease + @JsonField(name = "lang") + @CursorField(Statuses.LANG) + public String lang; + @ParcelableThisPlease + @JsonField(name = "user_name") + @CursorField(Statuses.USER_NAME) + public String user_name; + @ParcelableThisPlease + @JsonField(name = "user_screen_name") + @CursorField(Statuses.USER_SCREEN_NAME) + public String user_screen_name; + @ParcelableThisPlease + @JsonField(name = "in_reply_to_name") + @CursorField(Statuses.IN_REPLY_TO_USER_NAME) + public String in_reply_to_name; + @ParcelableThisPlease + @JsonField(name = "in_reply_to_screen_name") + @CursorField(Statuses.IN_REPLY_TO_USER_SCREEN_NAME) + public String in_reply_to_screen_name; + @ParcelableThisPlease + @JsonField(name = "source") + @CursorField(Statuses.SOURCE) + public String source; + @ParcelableThisPlease + @JsonField(name = "user_profile_image_url") + @CursorField(Statuses.USER_PROFILE_IMAGE_URL) + public String user_profile_image_url; + @ParcelableThisPlease + @JsonField(name = "text_unescaped") + @CursorField(Statuses.TEXT_UNESCAPED) + public String text_unescaped; + @ParcelableThisPlease + @JsonField(name = "card_name") + @CursorField(Statuses.CARD_NAME) + public String card_name; + @ParcelableThisPlease + @JsonField(name = "quoted_text_html") + @CursorField(Statuses.QUOTED_TEXT_HTML) + public String quoted_text_html; + @ParcelableThisPlease + @JsonField(name = "quoted_text_plain") + @CursorField(Statuses.QUOTED_TEXT_PLAIN) + public String quoted_text_plain; + @ParcelableThisPlease + @JsonField(name = "quoted_text_unescaped") + @CursorField(Statuses.QUOTED_TEXT_UNESCAPED) + public String quoted_text_unescaped; + @ParcelableThisPlease + @JsonField(name = "quoted_source") + @CursorField(Statuses.QUOTED_SOURCE) + public String quoted_source; + @ParcelableThisPlease + @JsonField(name = "quoted_user_name") + @CursorField(Statuses.QUOTED_USER_NAME) + public String quoted_user_name; + @ParcelableThisPlease + @JsonField(name = "quoted_user_screen_name") + @CursorField(Statuses.QUOTED_USER_SCREEN_NAME) + public String quoted_user_screen_name; + @ParcelableThisPlease + @JsonField(name = "quoted_user_profile_image") + @CursorField(Statuses.QUOTED_USER_PROFILE_IMAGE) + public String quoted_user_profile_image; + @ParcelableThisPlease + @JsonField(name = "location") + @CursorField(value = Statuses.LOCATION, converter = ParcelableLocation.Converter.class) + public ParcelableLocation location; + @ParcelableThisPlease + @JsonField(name = "place_full_name") + @CursorField(value = Statuses.PLACE_FULL_NAME, converter = LoganSquareCursorFieldConverter.class) + public String place_full_name; + @ParcelableThisPlease + @JsonField(name = "mentions") + @CursorField(value = Statuses.MENTIONS_JSON, converter = LoganSquareCursorFieldConverter.class) + public ParcelableUserMention[] mentions; + @ParcelableThisPlease + @JsonField(name = "media") + @CursorField(value = Statuses.MEDIA_JSON, converter = LoganSquareCursorFieldConverter.class) + public ParcelableMedia[] media; + @ParcelableThisPlease + @JsonField(name = "quoted_media") + @CursorField(value = Statuses.QUOTED_MEDIA_JSON, converter = LoganSquareCursorFieldConverter.class) + public ParcelableMedia[] quoted_media; + @ParcelableThisPlease + @JsonField(name = "card") + @CursorField(value = Statuses.CARD, converter = LoganSquareCursorFieldConverter.class) + public ParcelableCardEntity card; + @CursorField(value = Statuses._ID, excludeWrite = true) + long _id; public static final Creator CREATOR = new Creator() { public ParcelableStatus createFromParcel(Parcel source) { ParcelableStatus target = new ParcelableStatus(); @@ -74,296 +312,6 @@ public class ParcelableStatus implements Parcelable, Comparable REVERSE_ID_COMPARATOR = new Comparator() { - - @Override - public int compare(final ParcelableStatus object1, final ParcelableStatus object2) { - final long diff = object1.id - object2.id; - if (diff > Integer.MAX_VALUE) return Integer.MAX_VALUE; - if (diff < Integer.MIN_VALUE) return Integer.MIN_VALUE; - return (int) diff; - } - }; - - public static final Comparator TIMESTAMP_COMPARATOR = new Comparator() { - - @Override - public int compare(final ParcelableStatus object1, final ParcelableStatus object2) { - final long diff = object2.timestamp - object1.timestamp; - if (diff > Integer.MAX_VALUE) return Integer.MAX_VALUE; - if (diff < Integer.MIN_VALUE) return Integer.MIN_VALUE; - return (int) diff; - } - }; - - @CursorField(value = Statuses._ID, excludeWrite = true) - long _id; - - @ParcelableThisPlease - @JsonField(name = "id") - @CursorField(Statuses.STATUS_ID) - public long id; - - @ParcelableThisPlease - @JsonField(name = "account_id") - @CursorField(Statuses.ACCOUNT_ID) - public long account_id; - - @ParcelableThisPlease - @JsonField(name = "timestamp") - @CursorField(Statuses.STATUS_TIMESTAMP) - public long timestamp; - - @ParcelableThisPlease - @JsonField(name = "user_id") - @CursorField(Statuses.USER_ID) - public long user_id; - - @ParcelableThisPlease - @JsonField(name = "retweet_id") - @CursorField(Statuses.RETWEET_ID) - public long retweet_id; - - @ParcelableThisPlease - @JsonField(name = "retweeted_by_user_id") - @CursorField(Statuses.RETWEETED_BY_USER_ID) - public long retweeted_by_user_id; - - @ParcelableThisPlease - @JsonField(name = "retweet_timestamp") - @CursorField(Statuses.RETWEET_TIMESTAMP) - public long retweet_timestamp; - - @ParcelableThisPlease - @JsonField(name = "retweet_count") - @CursorField(Statuses.RETWEET_COUNT) - public long retweet_count; - - @ParcelableThisPlease - @JsonField(name = "favorite_count") - @CursorField(Statuses.FAVORITE_COUNT) - public long favorite_count; - - @ParcelableThisPlease - @JsonField(name = "reply_count") - @CursorField(Statuses.REPLY_COUNT) - public long reply_count; - - @ParcelableThisPlease - @JsonField(name = "in_reply_to_status_id") - @CursorField(Statuses.IN_REPLY_TO_STATUS_ID) - public long in_reply_to_status_id; - - @ParcelableThisPlease - @JsonField(name = "in_reply_to_user_id") - @CursorField(Statuses.IN_REPLY_TO_USER_ID) - public long in_reply_to_user_id; - - @ParcelableThisPlease - @JsonField(name = "my_retweet_id") - @CursorField(Statuses.MY_RETWEET_ID) - public long my_retweet_id; - - @ParcelableThisPlease - @JsonField(name = "quoted_id") - @CursorField(Statuses.QUOTED_ID) - public long quoted_id; - - @ParcelableThisPlease - @JsonField(name = "quoted_timestamp") - @CursorField(Statuses.QUOTED_TIMESTAMP) - public long quoted_timestamp; - - @ParcelableThisPlease - @JsonField(name = "quoted_user_id") - @CursorField(Statuses.QUOTED_USER_ID) - public long quoted_user_id; - - @ParcelableThisPlease - @JsonField(name = "is_gap") - @CursorField(Statuses.IS_GAP) - public boolean is_gap; - - @ParcelableThisPlease - @JsonField(name = "is_retweet") - @CursorField(Statuses.IS_RETWEET) - public boolean is_retweet; - - @ParcelableThisPlease - @JsonField(name = "is_favorite") - @CursorField(Statuses.IS_FAVORITE) - public boolean is_favorite; - - @ParcelableThisPlease - @JsonField(name = "is_possibly_sensitive") - @CursorField(Statuses.IS_POSSIBLY_SENSITIVE) - public boolean is_possibly_sensitive; - - @ParcelableThisPlease - @JsonField(name = "user_is_following") - @CursorField(Statuses.IS_FOLLOWING) - public boolean user_is_following; - - @ParcelableThisPlease - @JsonField(name = "user_is_protected") - @CursorField(Statuses.IS_PROTECTED) - public boolean user_is_protected; - - @ParcelableThisPlease - @JsonField(name = "user_is_verified") - @CursorField(Statuses.IS_VERIFIED) - public boolean user_is_verified; - - @ParcelableThisPlease - @JsonField(name = "is_quote") - @CursorField(Statuses.IS_QUOTE) - public boolean is_quote; - - @ParcelableThisPlease - @JsonField(name = "quoted_user_is_protected") - @CursorField(Statuses.QUOTED_USER_IS_PROTECTED) - public boolean quoted_user_is_protected; - - @ParcelableThisPlease - @JsonField(name = "quoted_user_is_verified") - @CursorField(Statuses.QUOTED_USER_IS_VERIFIED) - public boolean quoted_user_is_verified; - - @ParcelableThisPlease - @JsonField(name = "retweeted_by_user_name") - @CursorField(Statuses.RETWEETED_BY_USER_NAME) - public String retweeted_by_user_name; - - @ParcelableThisPlease - @JsonField(name = "retweeted_by_user_screen_name") - @CursorField(Statuses.RETWEETED_BY_USER_SCREEN_NAME) - public String retweeted_by_user_screen_name; - - @ParcelableThisPlease - @JsonField(name = "retweeted_by_user_profile_image") - @CursorField(Statuses.RETWEETED_BY_USER_PROFILE_IMAGE) - public String retweeted_by_user_profile_image; - - @ParcelableThisPlease - @JsonField(name = "text_html") - @CursorField(Statuses.TEXT_HTML) - public String text_html; - - @ParcelableThisPlease - @JsonField(name = "text_plain") - @CursorField(Statuses.TEXT_PLAIN) - public String text_plain; - - @ParcelableThisPlease - @JsonField(name = "lang") - @CursorField(Statuses.LANG) - public String lang; - - @ParcelableThisPlease - @JsonField(name = "user_name") - @CursorField(Statuses.USER_NAME) - public String user_name; - - @ParcelableThisPlease - @JsonField(name = "user_screen_name") - @CursorField(Statuses.USER_SCREEN_NAME) - public String user_screen_name; - - @ParcelableThisPlease - @JsonField(name = "in_reply_to_name") - @CursorField(Statuses.IN_REPLY_TO_USER_NAME) - public String in_reply_to_name; - - @ParcelableThisPlease - @JsonField(name = "in_reply_to_screen_name") - @CursorField(Statuses.IN_REPLY_TO_USER_SCREEN_NAME) - public String in_reply_to_screen_name; - - @ParcelableThisPlease - @JsonField(name = "source") - @CursorField(Statuses.SOURCE) - public String source; - - @ParcelableThisPlease - @JsonField(name = "user_profile_image_url") - @CursorField(Statuses.USER_PROFILE_IMAGE_URL) - public String user_profile_image_url; - - @ParcelableThisPlease - @JsonField(name = "text_unescaped") - @CursorField(Statuses.TEXT_UNESCAPED) - public String text_unescaped; - - @ParcelableThisPlease - @JsonField(name = "card_name") - @CursorField(Statuses.CARD_NAME) - public String card_name; - - @ParcelableThisPlease - @JsonField(name = "quoted_text_html") - @CursorField(Statuses.QUOTED_TEXT_HTML) - public String quoted_text_html; - - @ParcelableThisPlease - @JsonField(name = "quoted_text_plain") - @CursorField(Statuses.QUOTED_TEXT_PLAIN) - public String quoted_text_plain; - - @ParcelableThisPlease - @JsonField(name = "quoted_text_unescaped") - @CursorField(Statuses.QUOTED_TEXT_UNESCAPED) - public String quoted_text_unescaped; - - @ParcelableThisPlease - @JsonField(name = "quoted_source") - @CursorField(Statuses.QUOTED_SOURCE) - public String quoted_source; - - @ParcelableThisPlease - @JsonField(name = "quoted_user_name") - @CursorField(Statuses.QUOTED_USER_NAME) - public String quoted_user_name; - - @ParcelableThisPlease - @JsonField(name = "quoted_user_screen_name") - @CursorField(Statuses.QUOTED_USER_SCREEN_NAME) - public String quoted_user_screen_name; - - @ParcelableThisPlease - @JsonField(name = "quoted_user_profile_image") - @CursorField(Statuses.QUOTED_USER_PROFILE_IMAGE) - public String quoted_user_profile_image; - - @ParcelableThisPlease - @JsonField(name = "location") - @CursorField(value = Statuses.LOCATION, converter = ParcelableLocation.Converter.class) - public ParcelableLocation location; - - @ParcelableThisPlease - @JsonField(name = "place_full_name") - @CursorField(value = Statuses.PLACE_FULL_NAME, converter = LoganSquareCursorFieldConverter.class) - public String place_full_name; - - @ParcelableThisPlease - @JsonField(name = "mentions") - @CursorField(value = Statuses.MENTIONS_JSON, converter = LoganSquareCursorFieldConverter.class) - public ParcelableUserMention[] mentions; - - @ParcelableThisPlease - @JsonField(name = "media") - @CursorField(value = Statuses.MEDIA_JSON, converter = LoganSquareCursorFieldConverter.class) - public ParcelableMedia[] media; - - @ParcelableThisPlease - @JsonField(name = "quoted_media") - @CursorField(value = Statuses.QUOTED_MEDIA_JSON, converter = LoganSquareCursorFieldConverter.class) - public ParcelableMedia[] quoted_media; - - @ParcelableThisPlease - @JsonField(name = "card") - @CursorField(value = Statuses.CARD, converter = LoganSquareCursorFieldConverter.class) - public ParcelableCardEntity card; - ParcelableStatus() { } @@ -374,15 +322,18 @@ public class ParcelableStatus implements Parcelable, Comparable void log(long accountId, final T event, final PreProcessing preProcessing) { + mExecutor.execute(new WriteLogTask<>(mApplication, accountId, event, preProcessing)); } - public void log(Object event) { - log(ACCOUNT_ID_NOT_NEEDED, event); + public void log(long accountId, final T event) { + log(accountId, event, null); } - public void logList(List events, long accountId, String type) { - mExecutor.execute(new WriteLogTask(mApplication, accountId, type, events)); + public void log(final T event) { + log(event, null); + } + + public void log(final Object event, final PreProcessing preProcessing) { + log(ACCOUNT_ID_NOT_NEEDED, event, preProcessing); + } + + public void logList(List events, long accountId, String type) { + logList(events, accountId, type, null); + } + + public void logList(List events, long accountId, String type, final PreProcessing preProcessing) { + mExecutor.execute(new WriteLogTask<>(mApplication, accountId, type, events, preProcessing)); } public static void logScreenEvent(Context context, ScreenEvent.Action action, long presentDuration) { - getInstance(context).log(ScreenEvent.create(context, action, presentDuration)); + getInstance(context).log(ScreenEvent.create(context, action, presentDuration), null); } + } diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/PreProcessing.java b/twidere/src/main/java/edu/tsinghua/hotmobi/PreProcessing.java new file mode 100644 index 000000000..a12f0371d --- /dev/null +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/PreProcessing.java @@ -0,0 +1,29 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * 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 . + */ + +package edu.tsinghua.hotmobi; + +import android.content.Context; + +/** + * Created by mariotaku on 15/12/24. + */ +public interface PreProcessing { + void process(T event, Context appContext); +} diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/UploadLogsTask.java b/twidere/src/main/java/edu/tsinghua/hotmobi/UploadLogsTask.java index 17b5154a9..b478797ef 100644 --- a/twidere/src/main/java/edu/tsinghua/hotmobi/UploadLogsTask.java +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/UploadLogsTask.java @@ -110,7 +110,6 @@ public class UploadLogsTask implements Runnable { if (response.isSuccessful()) { succeeded &= logFile.delete(); } - response.close(); } catch (IOException e) { Log.w(HotMobiLogger.LOGTAG, e); succeeded = false; diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/WriteLogTask.java b/twidere/src/main/java/edu/tsinghua/hotmobi/WriteLogTask.java index ac72ea406..f9942abca9 100644 --- a/twidere/src/main/java/edu/tsinghua/hotmobi/WriteLogTask.java +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/WriteLogTask.java @@ -21,6 +21,7 @@ package edu.tsinghua.hotmobi; import android.content.Context; import android.content.SharedPreferences; +import android.support.annotation.Nullable; import android.util.Log; import com.bluelinelabs.logansquare.LoganSquare; @@ -40,24 +41,28 @@ import java.util.List; /** * Created by mariotaku on 15/8/23. */ -public class WriteLogTask implements Runnable, Constants { +public class WriteLogTask implements Runnable, Constants { private static final byte[] LF = {'\n'}; private final Context context; private final long accountId; private final String type; - private final List events; + private final List events; + @Nullable + private final PreProcessing preProcessing; - public WriteLogTask(Context context, long accountId, Object event) { - this(context, accountId, HotMobiLogger.getLogFilename(event), Collections.singletonList(event)); + public WriteLogTask(Context context, long accountId, T event, @Nullable PreProcessing preProcessing) { + this(context, accountId, HotMobiLogger.getLogFilename(event), Collections.singletonList(event), preProcessing); } - public WriteLogTask(Context context, long accountId, String type, List events) { + public WriteLogTask(Context context, long accountId, String type, List events, + @Nullable PreProcessing preProcessing) { this.context = context; this.accountId = accountId; this.type = type; this.events = events; + this.preProcessing = preProcessing; } @Override @@ -71,7 +76,10 @@ public class WriteLogTask implements Runnable, Constants { raf = new RandomAccessFile(HotMobiLogger.getLogFile(context, accountId, type), "rw"); fc = raf.getChannel(); final FileLock lock = fc.lock(); - for (Object event : events) { + for (T event : events) { + if (preProcessing != null) { + preProcessing.process(event, context); + } if (BuildConfig.DEBUG) { if (accountId > 0) { Log.v(HotMobiLogger.LOGTAG, "Log " + type + " for account " + accountId + ": " + event); diff --git a/twidere/src/main/java/org/mariotaku/twidere/Constants.java b/twidere/src/main/java/org/mariotaku/twidere/Constants.java index ee3ae5fbc..fb06a285c 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/Constants.java +++ b/twidere/src/main/java/org/mariotaku/twidere/Constants.java @@ -33,7 +33,7 @@ import static org.mariotaku.twidere.annotation.Preference.Type.STRING; public interface Constants extends TwidereConstants { String DATABASES_NAME = "twidere.sqlite"; - int DATABASES_VERSION = 114; + int DATABASES_VERSION = 115; int MENU_GROUP_STATUS_EXTENSION = 10; int MENU_GROUP_COMPOSE_EXTENSION = 11; diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/MediaViewerActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/MediaViewerActivity.java index 115b5cf8a..355aaaaaa 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/MediaViewerActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/MediaViewerActivity.java @@ -85,6 +85,7 @@ import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.util.VideoLoader.VideoLoadingListener; import java.io.File; +import java.io.IOException; import java.util.concurrent.TimeUnit; import pl.droidsonroids.gif.GifSupportChecker; @@ -402,7 +403,8 @@ public final class MediaViewerActivity extends BaseAppCompatActivity implements public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); final boolean isLoading = getLoaderManager().hasRunningLoaders(); - final TaskRunnable, Pair> checkState = new TaskRunnable, Pair>() { + final TaskRunnable, Pair> checkState + = new TaskRunnable, Pair>() { @Override public Pair doLongOperation(File file) throws InterruptedException { final boolean hasImage = file != null && file.exists(); @@ -411,7 +413,8 @@ public final class MediaViewerActivity extends BaseAppCompatActivity implements } final Intent intent = new Intent(Intent.ACTION_SEND); final Uri fileUri = Uri.fromFile(file); - intent.setDataAndType(fileUri, Utils.getImageMimeType(file)); + final String imageMimeType = Utils.getImageMimeType(file); + intent.setDataAndType(fileUri, imageMimeType); intent.putExtra(Intent.EXTRA_STREAM, fileUri); final MediaViewerActivity activity = (MediaViewerActivity) getActivity(); if (activity.hasStatus()) { diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java index 06e10173a..667001e83 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java @@ -102,6 +102,8 @@ import org.mariotaku.twidere.util.view.ConsumerKeySecretValidator; import org.mariotaku.twidere.view.TintedStatusNativeActionModeAwareLayout; import org.mariotaku.twidere.view.iface.TintedStatusLayout; +import java.lang.ref.WeakReference; + import static android.text.TextUtils.isEmpty; import static org.mariotaku.twidere.util.ContentValuesCreator.createAccount; import static org.mariotaku.twidere.util.DataStoreUtils.getActivatedAccountIds; @@ -577,14 +579,15 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList public static abstract class AbstractSignInTask extends AsyncTask { - protected final SignInActivity activity; + protected final WeakReference activityRef; public AbstractSignInTask(final SignInActivity activity) { - this.activity = activity; + this.activityRef = new WeakReference<>(activity); } @Override protected void onPostExecute(final SignInResponse result) { + final SignInActivity activity = activityRef.get(); if (activity != null) { activity.onSignInResult(result); } @@ -592,6 +595,7 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList @Override protected void onPreExecute() { + final SignInActivity activity = activityRef.get(); if (activity != null) { activity.onSignInStart(); } @@ -730,6 +734,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList } private SignInResponse authBasic() throws TwitterException { + final SignInActivity activity = activityRef.get(); + if (activity == null) return new SignInResponse(false, false, null); final String versionSuffix = noVersionSuffix ? null : "1.1"; final Endpoint endpoint = new Endpoint(TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", versionSuffix)); final Authorization auth = new BasicAuthorization(username, password); @@ -744,6 +750,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList } private SignInResponse authOAuth() throws AuthenticationException, TwitterException { + final SignInActivity activity = activityRef.get(); + if (activity == null) return new SignInResponse(false, false, null); Endpoint endpoint = TwitterAPIFactory.getOAuthEndpoint(apiUrlFormat, "api", null, sameOAuthSigningUrl); OAuthAuthorization auth = new OAuthAuthorization(consumerKey.getOauthToken(), consumerKey.getOauthTokenSecret()); final TwitterOAuth oauth = TwitterAPIFactory.getInstance(activity, endpoint, auth, TwitterOAuth.class); @@ -762,6 +770,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList } private SignInResponse authTwipOMode() throws TwitterException { + final SignInActivity activity = activityRef.get(); + if (activity == null) return new SignInResponse(false, false, null); final String versionSuffix = noVersionSuffix ? null : "1.1"; final Endpoint endpoint = new Endpoint(TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", versionSuffix)); final Authorization auth = new EmptyAuthorization(); @@ -774,6 +784,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList } private SignInResponse authxAuth() throws TwitterException { + final SignInActivity activity = activityRef.get(); + if (activity == null) return new SignInResponse(false, false, null); Endpoint endpoint = TwitterAPIFactory.getOAuthEndpoint(apiUrlFormat, "api", null, sameOAuthSigningUrl); OAuthAuthorization auth = new OAuthAuthorization(consumerKey.getOauthToken(), consumerKey.getOauthTokenSecret()); final TwitterOAuth oauth = TwitterAPIFactory.getInstance(activity, endpoint, auth, TwitterOAuth.class); @@ -800,6 +812,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList publishProgress(new Runnable() { @Override public void run() { + final SignInActivity activity = activityRef.get(); + if (activity == null) return; activity.dismissDialogFragment(FRAGMENT_TAG_SIGN_IN_PROGRESS); } }); @@ -807,6 +821,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList publishProgress(new Runnable() { @Override public void run() { + final SignInActivity activity = activityRef.get(); + if (activity == null) return; activity.postAfterFragmentResumed(new Runnable() { @Override public void run() { @@ -830,6 +846,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList publishProgress(new Runnable() { @Override public void run() { + final SignInActivity activity = activityRef.get(); + if (activity == null) return; activity.showSignInProgressDialog(); } }); diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java index 2b0b94590..fae4ac05c 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java @@ -72,7 +72,6 @@ public abstract class AbsActivitiesAdapter extends LoadMoreSupportAdapter< private final MediaLoadingHandler mLoadingHandler; private final int mCardBackgroundColor; private final boolean mCompactCards; - private final TwidereLinkify mLinkify; private final DummyStatusHolderAdapter mStatusAdapterDelegate; private ActivityAdapterListener mActivityAdapterListener; @@ -80,14 +79,13 @@ public abstract class AbsActivitiesAdapter extends LoadMoreSupportAdapter< protected AbsActivitiesAdapter(final Context context, boolean compact) { super(context); - mStatusAdapterDelegate = new DummyStatusHolderAdapter(context); + mStatusAdapterDelegate = new DummyStatusHolderAdapter(context, new TwidereLinkify(new OnLinkClickHandler(context, null))); mCardBackgroundColor = ThemeUtils.getCardBackgroundColor(context, ThemeUtils.getThemeBackgroundOption(context), ThemeUtils.getUserThemeBackgroundAlpha(context)); mInflater = LayoutInflater.from(context); mLoadingHandler = new MediaLoadingHandler(R.id.media_preview_progress); mCompactCards = compact; - mLinkify = new TwidereLinkify(new OnLinkClickHandler(context, null)); mStatusAdapterDelegate.updateOptions(); } @@ -133,10 +131,6 @@ public abstract class AbsActivitiesAdapter extends LoadMoreSupportAdapter< return mStatusAdapterDelegate.getLinkHighlightingStyle(); } - public TwidereLinkify getLinkify() { - return mLinkify; - } - public boolean isNameFirst() { return mStatusAdapterDelegate.isNameFirst(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java index 6f58310de..12cbece42 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java @@ -1263,7 +1263,8 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac private static final int VIEW_TYPE_CONVERSATION_LOAD_INDICATOR = 2; private static final int VIEW_TYPE_REPLIES_LOAD_INDICATOR = 3; private static final int VIEW_TYPE_REPLY_ERROR = 4; - private static final int VIEW_TYPE_SPACE = 5; + private static final int VIEW_TYPE_CONVERSATION_ERROR = 5; + private static final int VIEW_TYPE_SPACE = 6; private static final int ITEM_IDX_CONVERSATION_ERROR = 0; private static final int ITEM_IDX_CONVERSATION_LOAD_MORE = 1; private static final int ITEM_IDX_CONVERSATION = 2; @@ -1300,7 +1301,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac private List mConversation, mReplies; private StatusAdapterListener mStatusAdapterListener; private RecyclerView mRecyclerView; - private CharSequence mReplyError; + private CharSequence mReplyError, mConversationError; private boolean mRepliesLoading, mConversationsLoading; private TranslationResult mTranslationResult; @@ -1615,6 +1616,11 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac errorHolder.showError(mReplyError); break; } + case VIEW_TYPE_CONVERSATION_ERROR: { + final StatusErrorItemViewHolder errorHolder = (StatusErrorItemViewHolder) holder; + errorHolder.showError(mReplyError); + break; + } case VIEW_TYPE_CONVERSATION_LOAD_INDICATOR: { LoadIndicatorViewHolder indicatorHolder = ((LoadIndicatorViewHolder) holder); indicatorHolder.setLoadProgressVisible(mConversationsLoading); @@ -1660,6 +1666,8 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac return VIEW_TYPE_SPACE; case ITEM_IDX_REPLY_ERROR: return VIEW_TYPE_REPLY_ERROR; + case ITEM_IDX_CONVERSATION_ERROR: + return VIEW_TYPE_CONVERSATION_ERROR; } throw new IllegalStateException(); } @@ -1781,6 +1789,12 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac updateItemDecoration(); } + public void setConversationError(CharSequence error) { + mConversationError = error; + setCount(ITEM_IDX_CONVERSATION_ERROR, error != null ? 1 : 0); + updateItemDecoration(); + } + public void setReplies(List replies) { mReplies = replies; setCount(ITEM_IDX_REPLY, replies != null ? replies.size() : 0); @@ -1857,7 +1871,6 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac @Override public int getDecoratedMeasuredHeight(View child) { - final int height = super.getDecoratedMeasuredHeight(child); int heightBeforeSpace = 0; if (getItemViewType(child) == StatusAdapter.VIEW_TYPE_SPACE) { for (int i = 0, j = getChildCount(); i < j; i++) { @@ -1877,7 +1890,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac return Math.max(0, spaceHeight); } } - return height; + return super.getDecoratedMeasuredHeight(child); } @Override diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/ActivityTracker.java b/twidere/src/main/java/org/mariotaku/twidere/util/ActivityTracker.java index 4f388b536..7a28ae959 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/ActivityTracker.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/ActivityTracker.java @@ -22,12 +22,14 @@ package org.mariotaku.twidere.util; import android.app.Activity; import android.app.Application; +import android.content.Context; import android.os.Bundle; import org.apache.commons.collections.primitives.ArrayIntList; import org.apache.commons.collections.primitives.IntList; import edu.tsinghua.hotmobi.HotMobiLogger; +import edu.tsinghua.hotmobi.PreProcessing; import edu.tsinghua.hotmobi.model.SessionEvent; /** @@ -56,7 +58,7 @@ public class ActivityTracker implements Application.ActivityLifecycleCallbacks { } @Override - public void onActivityStarted(Activity activity) { + public void onActivityStarted(final Activity activity) { mInternalStack.add(System.identityHashCode(activity)); // BEGIN HotMobi if (mSessionEvent == null) { @@ -82,9 +84,13 @@ public class ActivityTracker implements Application.ActivityLifecycleCallbacks { // BEGIN HotMobi final SessionEvent event = mSessionEvent; if (event != null && !isSwitchingInSameTask(hashCode)) { - event.dumpPreferences(activity); event.markEnd(); - HotMobiLogger.getInstance(activity).log(event); + HotMobiLogger.getInstance(activity).log(event, new PreProcessing() { + @Override + public void process(SessionEvent event, Context appContext) { + event.dumpPreferences(appContext); + } + }); mSessionEvent = null; } // END HotMobi diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/EmojiSupportUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/EmojiSupportUtils.java index e36c23e25..9cf134499 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/EmojiSupportUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/EmojiSupportUtils.java @@ -42,8 +42,9 @@ public class EmojiSupportUtils { for (int i = array.length() - 1; i >= 0; i--) { final int codePoint = array.get(i); if (isEmoji(codePoint)) { - int arrayIdx = i, arrayEnd = i + 1; - int textIdx = array.indexOfText(codePoint, i); + int arrayIdx = i, arrayEnd = i + 1, arrayIdxOffset = 0; + int textIdx = array.indexOfText(codePoint, i), textIdxOffset = 0; + int indexOffset = 0; if (textIdx == -1 || textIdx < textStart) { continue; } @@ -52,31 +53,60 @@ public class EmojiSupportUtils { if (i > 0) { int prev = array.get(i - 1); if (isRegionalIndicatorSymbol(prev)) { - textIdx -= Character.charCount(prev); - arrayIdx--; - i--; + arrayIdxOffset = -1; + textIdxOffset = -Character.charCount(prev); + indexOffset = -1; } } } else if (isModifier(codePoint)) { if (i > 0) { int prev = array.get(i - 1); if (isEmoji(prev)) { - textIdx -= Character.charCount(prev); - arrayIdx--; - i--; + arrayIdxOffset = -1; + textIdxOffset = -Character.charCount(prev); + indexOffset = -1; + } + } + } else if (isKeyCap(codePoint)) { + if (i > 0) { + int prev = array.get(i - 1); + if (isPhoneNumberSymbol(prev)) { + arrayIdxOffset = -1; + textIdxOffset = -Character.charCount(prev); + indexOffset = -1; } } } if (textEnd > textStart + textLength) continue; - final EmojiSpan[] spans = text.getSpans(textIdx, textEnd, EmojiSpan.class); - if (spans.length > 0) continue; - final Drawable drawable = emoji.getEmojiDrawableFor(array.subarray(arrayIdx, arrayEnd)); - if (drawable == null) continue; - text.setSpan(new EmojiSpan(drawable), textIdx, textEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + EmojiSpan[] spans = text.getSpans(textIdx + textIdxOffset, textEnd, EmojiSpan.class); + if (spans.length == 0) { + Drawable drawable = emoji.getEmojiDrawableFor(array.subarray(arrayIdx + arrayIdxOffset, + arrayEnd)); + if (drawable == null) { + // Not emoji combination, just use fallback + textIdxOffset = 0; + arrayIdxOffset = 0; + indexOffset = 0; + spans = text.getSpans(textIdx + textIdxOffset, textEnd, EmojiSpan.class); + if (spans.length == 0) { + drawable = emoji.getEmojiDrawableFor(array.subarray(arrayIdx + arrayIdxOffset, + arrayEnd)); + } + } + if (drawable != null) { + text.setSpan(new EmojiSpan(drawable), textIdx + textIdxOffset, textEnd, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + i += indexOffset; } } } + private static boolean isPhoneNumberSymbol(int codePoint) { + return codePoint == 0x0023 || codePoint == 0x002a || inRange(codePoint, 0x0030, 0x0039); + } + private static boolean isModifier(int codePoint) { return inRange(codePoint, 0x1f3fb, 0x1f3ff); } @@ -93,4 +123,8 @@ public class EmojiSupportUtils { return inRange(codePoint, 0x1f1e6, 0x1f1ff); } + private static boolean isKeyCap(int codePoint) { + return codePoint == 0x20e3; + } + } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java index 985a293e6..58301ba2a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java @@ -11,8 +11,11 @@ import android.support.annotation.Nullable; import android.text.TextUtils; import android.webkit.URLUtil; +import com.squareup.okhttp.Authenticator; import com.squareup.okhttp.Dns; import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; import org.apache.commons.lang3.math.NumberUtils; import org.mariotaku.restfu.ExceptionFactory; @@ -51,6 +54,7 @@ import org.mariotaku.twidere.model.RequestType; import org.mariotaku.twidere.util.dagger.ApplicationModule; import org.mariotaku.twidere.util.net.NetworkUsageUtils; +import java.io.IOException; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.SocketAddress; @@ -118,6 +122,7 @@ public class TwitterAPIFactory implements TwidereConstants { final OkHttpClient client = new OkHttpClient(); updateHttpClientConfiguration(prefs, client); client.setDns(dns); + DebugModeUtils.initForHttpClient(client); NetworkUsageUtils.initForHttpClient(context, client); return new OkHttpRestClient(client); } @@ -386,8 +391,8 @@ public class TwitterAPIFactory implements TwidereConstants { sExtraParams.put("include_cards", "true"); sExtraParams.put("cards_platform", "Android-12"); sExtraParams.put("include_entities", "true"); - sExtraParams.put("include_my_retweet", "1"); - sExtraParams.put("include_rts", "1"); + sExtraParams.put("include_my_retweet", "true"); + sExtraParams.put("include_rts", "true"); sExtraParams.put("include_reply_count", "true"); sExtraParams.put("include_descendent_reply_count", "true"); sExtraParams.put("full_text", "true"); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/NetworkUsageUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/NetworkUsageUtils.java index 19dc133c4..11a6dcdcd 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/NetworkUsageUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/NetworkUsageUtils.java @@ -38,11 +38,16 @@ import org.mariotaku.twidere.util.ConnectivityUtils; import java.io.IOException; import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * Created by mariotaku on 15/6/24. */ public class NetworkUsageUtils implements Constants { + + private static final Executor sNetworkUsageExecutor = Executors.newSingleThreadExecutor(); + public static void initForHttpClient(Context context, OkHttpClient client) { final List interceptors = client.networkInterceptors(); interceptors.add(new NetworkUsageInterceptor(context)); @@ -74,12 +79,17 @@ public class NetworkUsageUtils implements Constants { values.put(NetworkUsages.REQUEST_NETWORK, sNetworkType); final Response response = chain.proceed(request); values.put(NetworkUsages.KILOBYTES_RECEIVED, getBodyLength(response.body()) / 1024.0); - final ContentResolver cr = context.getContentResolver(); - try { - cr.insert(NetworkUsages.CONTENT_URI, values); - } catch (IllegalStateException e) { - Log.e(LOGTAG, "Unable to log network usage", e); - } + sNetworkUsageExecutor.execute(new Runnable() { + @Override + public void run() { + final ContentResolver cr = context.getContentResolver(); + try { + cr.insert(NetworkUsages.CONTENT_URI, values); + } catch (IllegalStateException e) { + Log.e(LOGTAG, "Unable to log network usage", e); + } + } + }); return response; } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereDns.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereDns.java index c059f95d2..52b2bba8a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereDns.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereDns.java @@ -87,8 +87,8 @@ public class TwidereDns implements Constants, Dns { // First, I'll try to load address cached. final InetAddress[] cachedHostAddr = mHostCache.get(host); if (cachedHostAddr != null) { - if (BuildConfig.DEBUG) { - Log.d(RESOLVER_LOGTAG, "Got cached " + Arrays.toString(cachedHostAddr)); + if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) { + Log.v(RESOLVER_LOGTAG, "Got cached " + Arrays.toString(cachedHostAddr)); return cachedHostAddr; } } @@ -100,7 +100,7 @@ public class TwidereDns implements Constants, Dns { final InetAddress[] hostAddr = fromAddressString(originalHost, mappedAddr); putCache(originalHost, hostAddr); if (BuildConfig.DEBUG) { - Log.d(RESOLVER_LOGTAG, "Got mapped " + Arrays.toString(hostAddr)); + Log.v(RESOLVER_LOGTAG, "Got mapped " + Arrays.toString(hostAddr)); } if (hostAddr != null) { return hostAddr; @@ -110,8 +110,8 @@ public class TwidereDns implements Constants, Dns { try { final InetAddress[] hostAddr = mResolver.resolve(host); putCache(originalHost, hostAddr); - if (BuildConfig.DEBUG) { - Log.d(RESOLVER_LOGTAG, "Got hosts " + Arrays.toString(hostAddr)); + if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) { + Log.v(RESOLVER_LOGTAG, "Got hosts " + Arrays.toString(hostAddr)); } return hostAddr; } catch (UnknownHostException e) { @@ -121,8 +121,8 @@ public class TwidereDns implements Constants, Dns { if (customMappedHost != null) { final InetAddress[] hostAddr = fromAddressString(originalHost, customMappedHost); putCache(originalHost, hostAddr); - if (BuildConfig.DEBUG) { - Log.d(RESOLVER_LOGTAG, "Got mapped address " + customMappedHost + " for host " + host); + if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) { + Log.v(RESOLVER_LOGTAG, "Got mapped address " + customMappedHost + " for host " + host); } if (hostAddr != null) { return hostAddr; @@ -154,8 +154,8 @@ public class TwidereDns implements Constants, Dns { if (!resolvedAddresses.isEmpty()) { final InetAddress[] hostAddr = resolvedAddresses.toArray(new InetAddress[resolvedAddresses.size()]); putCache(originalHost, hostAddr); - if (BuildConfig.DEBUG) { - Log.d(RESOLVER_LOGTAG, "Resolved " + Arrays.toString(hostAddr)); + if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) { + Log.v(RESOLVER_LOGTAG, "Resolved " + Arrays.toString(hostAddr)); } return hostAddr; } @@ -166,8 +166,8 @@ public class TwidereDns implements Constants, Dns { return resolveInternal(originalHost, ((CNAMERecord) record).getTarget().toString()); } } - if (BuildConfig.DEBUG) { - Log.w(RESOLVER_LOGTAG, "Resolve address " + host + " failed, using original host"); + if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) { + Log.v(RESOLVER_LOGTAG, "Resolve address " + host + " failed, using original host"); } final InetAddress[] defaultAddresses = InetAddress.getAllByName(host); putCache(host, defaultAddresses); diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/ShortTimeView.java b/twidere/src/main/java/org/mariotaku/twidere/view/ShortTimeView.java index 4a9415719..5237227a5 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/ShortTimeView.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/ShortTimeView.java @@ -31,6 +31,8 @@ import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; import org.mariotaku.twidere.view.themed.ThemedTextView; +import java.lang.ref.WeakReference; + import static android.text.format.DateUtils.getRelativeTimeSpanString; import static org.mariotaku.twidere.util.Utils.formatSameDayTime; @@ -111,17 +113,19 @@ public class ShortTimeView extends ThemedTextView implements Constants, OnShared private static class TickerRunnable implements Runnable { - private final ShortTimeView mTextView; + private final WeakReference mViewRef; private TickerRunnable(final ShortTimeView view) { - mTextView = view; + mViewRef = new WeakReference<>(view); } @Override public void run() { - final Handler handler = mTextView.getHandler(); + final ShortTimeView view = mViewRef.get(); + if (view == null) return; + final Handler handler = view.getHandler(); if (handler == null) return; - mTextView.invalidateTime(); + view.invalidateTime(); final long now = SystemClock.uptimeMillis(); final long next = now + TICKER_DURATION - now % TICKER_DURATION; handler.postAtTime(this, next); diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/MessageViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/MessageViewHolder.java index 54e269e31..3acc22a02 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/MessageViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/MessageViewHolder.java @@ -26,7 +26,6 @@ import android.database.Cursor; import android.os.Bundle; import android.support.v7.widget.RecyclerView.ViewHolder; import android.text.Spanned; -import android.text.method.ArrowKeyMovementMethod; import android.view.View; import android.widget.TextView; @@ -38,7 +37,6 @@ import org.mariotaku.twidere.model.ParcelableMedia; import org.mariotaku.twidere.util.HtmlSpanBuilder; import org.mariotaku.twidere.util.JsonSerializer; import org.mariotaku.twidere.util.MediaLoaderWrapper; -import org.mariotaku.twidere.util.StatusActionModeCallback; import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.TwidereColorUtils; import org.mariotaku.twidere.util.TwidereLinkify; @@ -55,7 +53,6 @@ public class MessageViewHolder extends ViewHolder implements OnMediaClickListene protected final MessageConversationAdapter adapter; private final int textColorPrimary, textColorPrimaryInverse, textColorSecondary, textColorSecondaryInverse; - private final StatusActionModeCallback callback; public MessageViewHolder(final MessageConversationAdapter adapter, final View itemView) { @@ -75,7 +72,6 @@ public class MessageViewHolder extends ViewHolder implements OnMediaClickListene time = (TextView) itemView.findViewById(R.id.time); mediaContainer = (CardMediaContainer) itemView.findViewById(R.id.media_preview_container); mediaContainer.setStyle(adapter.getMediaPreviewStyle()); - callback = new StatusActionModeCallback(textView, adapter.getContext()); } public void displayMessage(Cursor cursor, ParcelableDirectMessageCursorIndices indices) { @@ -92,10 +88,6 @@ public class MessageViewHolder extends ViewHolder implements OnMediaClickListene time.setText(Utils.formatToLongTimeString(context, timestamp)); mediaContainer.setVisibility(media != null && media.length > 0 ? View.VISIBLE : View.GONE); mediaContainer.displayMedia(media, loader, accountId, true, this, adapter.getMediaLoadingHandler()); - - textView.setTextIsSelectable(true); - textView.setMovementMethod(ArrowKeyMovementMethod.getInstance()); - textView.setCustomSelectionActionModeCallback(callback); } @Override diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java index 0799df2ac..a9c61bf45 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java @@ -471,11 +471,15 @@ public class StatusViewHolder extends ViewHolder implements Constants, OnClickLi private boolean useStarsForLikes; public DummyStatusHolderAdapter(Context context) { + this(context, new TwidereLinkify(null)); + } + + public DummyStatusHolderAdapter(Context context, TwidereLinkify linkify) { DaggerGeneralComponent.builder().applicationModule(ApplicationModule.get(context)).build().inject(this); this.context = context; preferences = SharedPreferencesWrapper.getInstance(context, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); handler = new MediaLoadingHandler(R.id.media_preview_progress); - linkify = new TwidereLinkify(null); + this.linkify = linkify; updateOptions(); } diff --git a/twidere/src/main/res/layout/activity_quick_search_bar.xml b/twidere/src/main/res/layout/activity_quick_search_bar.xml index d431923dc..e57965abd 100644 --- a/twidere/src/main/res/layout/activity_quick_search_bar.xml +++ b/twidere/src/main/res/layout/activity_quick_search_bar.xml @@ -17,10 +17,11 @@ ~ along with this program. If not, see . --> - @@ -53,7 +54,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="0" - tools:listitem="@layout/spinner_item_account_icon" /> + tools:listitem="@layout/spinner_item_account_icon"/> - + + android:src="@drawable/ic_action_search"/> + android:background="#20808080"/> + tools:listitem="@layout/list_item_user"/> diff --git a/twidere/src/main/res/layout/card_item_message_conversation_common.xml b/twidere/src/main/res/layout/card_item_message_conversation_common.xml index 1230140c7..c1a9b1b28 100644 --- a/twidere/src/main/res/layout/card_item_message_conversation_common.xml +++ b/twidere/src/main/res/layout/card_item_message_conversation_common.xml @@ -18,17 +18,17 @@ --> + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + tools:showIn="@layout/card_item_message_conversation_incoming"> - + @@ -45,8 +45,7 @@ android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="?android:attr/textColorSecondary" - android:textIsSelectable="true" - tools:text="@string/sample_status_text" /> + tools:text="@string/sample_status_text"/> + tools:text="12:00"/> \ No newline at end of file diff --git a/twidere/src/main/res/layout/card_item_status_compact.xml b/twidere/src/main/res/layout/card_item_status_compact.xml index 0c1768e61..697b98b85 100644 --- a/twidere/src/main/res/layout/card_item_status_compact.xml +++ b/twidere/src/main/res/layout/card_item_status_compact.xml @@ -16,13 +16,13 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - + + tools:visibility="visible"/> + tools:visibility="visible"/> + tools:visibility="visible"/> + tools:visibility="gone"/> + app:nv_secondaryTextColor="?android:textColorSecondary"/> + tools:textSize="@dimen/text_size_extra_small"/> + tools:src="@drawable/ic_action_gallery"/> @@ -156,9 +156,8 @@ android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="?android:attr/textColorPrimary" android:visibility="visible" - app:nv_primaryTextStyle="bold" tools:text="@string/sample_status_text" - tools:visibility="visible" /> + tools:visibility="visible"/> + tools:visibility="visible"/> + tools:visibility="visible"/> + tools:visibility="visible"/> + app:iabColor="?android:textColorTertiary"/> + app:iabColor="?android:textColorTertiary"/> + app:iabColor="?android:textColorTertiary"/> @@ -296,7 +295,7 @@ android:color="?android:textColorTertiary" android:focusable="false" android:src="@drawable/ic_action_more_horizontal" - tools:visibility="visible" /> + tools:visibility="visible"/> \ No newline at end of file diff --git a/twidere/src/main/res/layout/spinner_item_account_icon.xml b/twidere/src/main/res/layout/spinner_item_account_icon.xml index 5146ef8b5..fca3f7ca5 100644 --- a/twidere/src/main/res/layout/spinner_item_account_icon.xml +++ b/twidere/src/main/res/layout/spinner_item_account_icon.xml @@ -17,10 +17,13 @@ ~ along with this program. If not, see . --> - + android:padding="@dimen/element_spacing_small" + tools:layout_width="48dp"> + android:scaleType="centerCrop"/> \ No newline at end of file