fixing leaks

This commit is contained in:
Mariotaku Lee 2015-12-25 17:54:13 +08:00
parent 1c5dfb86a9
commit 6f6dec7bb6
25 changed files with 594 additions and 445 deletions

View File

@ -0,0 +1,42 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.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;
}
}

View File

@ -20,12 +20,12 @@
package org.mariotaku.twidere.api.twitter.model; package org.mariotaku.twidere.api.twitter.model;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.annotation.JsonField; import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject; import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.bluelinelabs.logansquare.annotation.OnJsonParseComplete; import com.bluelinelabs.logansquare.annotation.OnJsonParseComplete;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.mariotaku.twidere.api.twitter.util.TwitterDateConverter; import org.mariotaku.twidere.api.twitter.util.TwitterDateConverter;
import java.io.IOException; import java.io.IOException;
@ -81,8 +81,9 @@ public class Status extends TwitterResponseObject implements Comparable<Status>,
@JsonField(name = "current_user_retweet") @JsonField(name = "current_user_retweet")
CurrentUserRetweet currentUserRetweet; CurrentUserRetweet currentUserRetweet;
@Nullable
@JsonField(name = "contributors") @JsonField(name = "contributors")
long[] contributors; Contributor[] contributors;
@JsonField(name = "retweet_count") @JsonField(name = "retweet_count")
long retweetCount; long retweetCount;
@ -127,25 +128,21 @@ public class Status extends TwitterResponseObject implements Comparable<Status>,
public String getInReplyToScreenName() { public String getInReplyToScreenName() {
return inReplyToScreenName; return inReplyToScreenName;
} }
public long getInReplyToUserId() { public long getInReplyToUserId() {
return inReplyToUserId; return inReplyToUserId;
} }
public long getInReplyToStatusId() { public long getInReplyToStatusId() {
return inReplyToStatusId; return inReplyToStatusId;
} }
public boolean isTruncated() { public boolean isTruncated() {
return truncated; return truncated;
} }
@ -157,11 +154,12 @@ public class Status extends TwitterResponseObject implements Comparable<Status>,
public String getSource() { public String getSource() {
return source; return source;
} }
/**
* UTC time when this Tweet was created.
*/
public Date getCreatedAt() { public Date getCreatedAt() {
return createdAt; return createdAt;
} }
@ -201,6 +199,10 @@ public class Status extends TwitterResponseObject implements Comparable<Status>,
return currentUserRetweet != null; return currentUserRetweet != null;
} }
public boolean wasRetweeted() {
return retweeted;
}
public long getFavoriteCount() { public long getFavoriteCount() {
return favoriteCount; return favoriteCount;
@ -213,6 +215,10 @@ public class Status extends TwitterResponseObject implements Comparable<Status>,
} }
/**
* <i>Perspectival</i>. Only surfaces on methods supporting the <code>include_my_retweet</code> parameter,
* when set to true. Details the Tweet ID of the users own retweet (if existent) of this Tweet.
*/
public long getCurrentUserRetweet() { public long getCurrentUserRetweet() {
if (currentUserRetweet == null) return -1; if (currentUserRetweet == null) return -1;
return currentUserRetweet.id; return currentUserRetweet.id;
@ -279,7 +285,12 @@ public class Status extends TwitterResponseObject implements Comparable<Status>,
} }
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; return contributors;
} }

View File

@ -62,6 +62,244 @@ import java.util.Map.Entry;
@JsonObject @JsonObject
@ParcelablePlease @ParcelablePlease
public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus>, Cloneable { public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus>, Cloneable {
@ParcelableThisPlease
@JsonField(name = "id")
@CursorField(Statuses.STATUS_ID)
public long id;
public static final Comparator<ParcelableStatus> REVERSE_ID_COMPARATOR = new Comparator<ParcelableStatus>() {
@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<ParcelableStatus> TIMESTAMP_COMPARATOR = new Comparator<ParcelableStatus>() {
@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<ParcelableStatus> CREATOR = new Creator<ParcelableStatus>() { public static final Creator<ParcelableStatus> CREATOR = new Creator<ParcelableStatus>() {
public ParcelableStatus createFromParcel(Parcel source) { public ParcelableStatus createFromParcel(Parcel source) {
ParcelableStatus target = new ParcelableStatus(); ParcelableStatus target = new ParcelableStatus();
@ -74,296 +312,6 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
} }
}; };
public static final Comparator<ParcelableStatus> REVERSE_ID_COMPARATOR = new Comparator<ParcelableStatus>() {
@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<ParcelableStatus> TIMESTAMP_COMPARATOR = new Comparator<ParcelableStatus>() {
@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() { ParcelableStatus() {
} }
@ -374,15 +322,18 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
id = orig.getId(); id = orig.getId();
timestamp = getTime(orig.getCreatedAt()); timestamp = getTime(orig.getCreatedAt());
final Status retweeted = orig.getRetweetedStatus(); final Status retweetedStatus = orig.getRetweetedStatus();
final User retweet_user = retweeted != null ? orig.getUser() : null; final User retweetUser = retweetedStatus != null ? orig.getUser() : null;
is_retweet = orig.isRetweet(); is_retweet = orig.isRetweet();
retweet_id = retweeted != null ? retweeted.getId() : -1; retweeted = orig.wasRetweeted();
retweet_timestamp = retweeted != null ? getTime(retweeted.getCreatedAt()) : -1; if (retweetedStatus != null) {
retweeted_by_user_id = retweet_user != null ? retweet_user.getId() : -1; retweet_id = retweetedStatus.getId();
retweeted_by_user_name = retweet_user != null ? retweet_user.getName() : null; retweet_timestamp = getTime(retweetedStatus.getCreatedAt());
retweeted_by_user_screen_name = retweet_user != null ? retweet_user.getScreenName() : null; retweeted_by_user_id = retweetUser.getId();
retweeted_by_user_profile_image = TwitterContentUtils.getProfileImageUrl(retweet_user); retweeted_by_user_name = retweetUser.getName();
retweeted_by_user_screen_name = retweetUser.getScreenName();
retweeted_by_user_profile_image = TwitterContentUtils.getProfileImageUrl(retweetUser);
}
final Status quoted = orig.getQuotedStatus(); final Status quoted = orig.getQuotedStatus();
is_quote = orig.isQuote(); is_quote = orig.isQuote();
@ -405,15 +356,28 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
} }
final Status status; final Status status;
if (retweeted != null) { if (retweetedStatus != null) {
status = retweeted; status = retweetedStatus;
reply_count = retweetedStatus.getReplyCount();
retweet_count = retweetedStatus.getRetweetCount();
favorite_count = retweetedStatus.getFavoriteCount();
in_reply_to_name = TwitterContentUtils.getInReplyToName(retweetedStatus);
in_reply_to_screen_name = retweetedStatus.getInReplyToScreenName();
in_reply_to_status_id = retweetedStatus.getInReplyToStatusId();
in_reply_to_user_id = retweetedStatus.getInReplyToUserId();
} else { } else {
status = orig; status = orig;
} reply_count = orig.getReplyCount();
retweet_count = orig.getRetweetCount();
favorite_count = orig.getFavoriteCount();
retweet_count = (retweeted != null ? retweeted : orig).getRetweetCount(); in_reply_to_name = TwitterContentUtils.getInReplyToName(orig);
favorite_count = (retweeted != null ? retweeted : orig).getFavoriteCount(); in_reply_to_screen_name = orig.getInReplyToScreenName();
reply_count = (retweeted != null ? retweeted : orig).getReplyCount(); in_reply_to_status_id = orig.getInReplyToStatusId();
in_reply_to_user_id = orig.getInReplyToUserId();
}
final User user = status.getUser(); final User user = status.getUser();
user_id = user.getId(); user_id = user.getId();
@ -426,10 +390,6 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
text_html = TwitterContentUtils.formatStatusText(status); text_html = TwitterContentUtils.formatStatusText(status);
media = ParcelableMedia.fromStatus(status); media = ParcelableMedia.fromStatus(status);
text_plain = TwitterContentUtils.unescapeTwitterStatusText(status.getText()); text_plain = TwitterContentUtils.unescapeTwitterStatusText(status.getText());
in_reply_to_name = TwitterContentUtils.getInReplyToName(retweeted != null ? retweeted : orig);
in_reply_to_screen_name = (retweeted != null ? retweeted : orig).getInReplyToScreenName();
in_reply_to_status_id = (retweeted != null ? retweeted : orig).getInReplyToStatusId();
in_reply_to_user_id = (retweeted != null ? retweeted : orig).getInReplyToUserId();
source = status.getSource(); source = status.getSource();
location = ParcelableLocation.fromGeoLocation(status.getGeoLocation()); location = ParcelableLocation.fromGeoLocation(status.getGeoLocation());
is_favorite = status.isFavorited(); is_favorite = status.isFavorited();
@ -632,18 +592,18 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
} }
} }
@Nullable
public ParcelableBindingValue getValue(@Nullable String key) {
if (key == null || values == null) return null;
return values.get(key);
}
@Nullable @Nullable
public static ParcelableBindingValue getValue(@Nullable ParcelableCardEntity entity, @Nullable String key) { public static ParcelableBindingValue getValue(@Nullable ParcelableCardEntity entity, @Nullable String key) {
if (entity == null) return null; if (entity == null) return null;
return entity.getValue(key); return entity.getValue(key);
} }
@Nullable
public ParcelableBindingValue getValue(@Nullable String key) {
if (key == null || values == null) return null;
return values.get(key);
}
@Override @Override
public String toString() { public String toString() {
return "ParcelableCardEntity{" + return "ParcelableCardEntity{" +

View File

@ -855,6 +855,8 @@ public interface TwidereDataStore {
String QUOTED_USER_IS_VERIFIED = "quoted_user_is_verified"; String QUOTED_USER_IS_VERIFIED = "quoted_user_is_verified";
String QUOTED_USER_IS_PROTECTED = "quoted_user_is_protected"; String QUOTED_USER_IS_PROTECTED = "quoted_user_is_protected";
String RETWEETED = "retweeted";
String[] COLUMNS = {_ID, ACCOUNT_ID, STATUS_ID, USER_ID, String[] COLUMNS = {_ID, ACCOUNT_ID, STATUS_ID, USER_ID,
STATUS_TIMESTAMP, TEXT_HTML, TEXT_PLAIN, TEXT_UNESCAPED, USER_NAME, USER_SCREEN_NAME, STATUS_TIMESTAMP, TEXT_HTML, TEXT_PLAIN, TEXT_UNESCAPED, USER_NAME, USER_SCREEN_NAME,
USER_PROFILE_IMAGE_URL, IN_REPLY_TO_STATUS_ID, IN_REPLY_TO_USER_ID, IN_REPLY_TO_USER_NAME, USER_PROFILE_IMAGE_URL, IN_REPLY_TO_STATUS_ID, IN_REPLY_TO_USER_ID, IN_REPLY_TO_USER_NAME,
@ -866,7 +868,7 @@ public interface TwidereDataStore {
QUOTED_USER_IS_VERIFIED, QUOTED_USER_IS_PROTECTED, MY_RETWEET_ID, IS_RETWEET, QUOTED_USER_IS_VERIFIED, QUOTED_USER_IS_PROTECTED, MY_RETWEET_ID, IS_RETWEET,
IS_QUOTE, IS_FAVORITE, IS_PROTECTED, IS_VERIFIED, IS_FOLLOWING, IS_GAP, IS_QUOTE, IS_FAVORITE, IS_PROTECTED, IS_VERIFIED, IS_FOLLOWING, IS_GAP,
IS_POSSIBLY_SENSITIVE, MEDIA_JSON, MENTIONS_JSON, QUOTED_MEDIA_JSON, CARD_NAME, CARD, IS_POSSIBLY_SENSITIVE, MEDIA_JSON, MENTIONS_JSON, QUOTED_MEDIA_JSON, CARD_NAME, CARD,
PLACE_FULL_NAME, LANG}; PLACE_FULL_NAME, LANG, RETWEETED};
String[] TYPES = {TYPE_PRIMARY_KEY, TYPE_INT, TYPE_INT, String[] TYPES = {TYPE_PRIMARY_KEY, TYPE_INT, TYPE_INT,
TYPE_INT, TYPE_INT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_INT, TYPE_INT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT,
@ -876,7 +878,7 @@ public interface TwidereDataStore {
TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_INT, TYPE_BOOLEAN, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_INT, TYPE_BOOLEAN,
TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN,
TYPE_BOOLEAN, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_BOOLEAN, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT,
TYPE_TEXT, TYPE_TEXT, TYPE_TEXT}; TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_BOOLEAN};
} }

View File

@ -181,22 +181,35 @@ public class HotMobiLogger {
record.setState(intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)); record.setState(intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1));
record.setTimestamp(System.currentTimeMillis()); record.setTimestamp(System.currentTimeMillis());
record.setTimeOffset(TimeZone.getDefault().getRawOffset()); record.setTimeOffset(TimeZone.getDefault().getRawOffset());
getInstance(context).log(record); getInstance(context).log(record, null);
} }
public void log(long accountId, final Object event) { public <T> void log(long accountId, final T event, final PreProcessing<T> preProcessing) {
mExecutor.execute(new WriteLogTask(mApplication, accountId, event)); mExecutor.execute(new WriteLogTask<>(mApplication, accountId, event, preProcessing));
} }
public void log(Object event) { public <T> void log(long accountId, final T event) {
log(ACCOUNT_ID_NOT_NEEDED, event); log(accountId, event, null);
} }
public void logList(List<?> events, long accountId, String type) { public <T> void log(final T event) {
mExecutor.execute(new WriteLogTask(mApplication, accountId, type, events)); log(event, null);
}
public void log(final Object event, final PreProcessing preProcessing) {
log(ACCOUNT_ID_NOT_NEEDED, event, preProcessing);
}
public <T> void logList(List<T> events, long accountId, String type) {
logList(events, accountId, type, null);
}
public <T> void logList(List<T> events, long accountId, String type, final PreProcessing<T> preProcessing) {
mExecutor.execute(new WriteLogTask<>(mApplication, accountId, type, events, preProcessing));
} }
public static void logScreenEvent(Context context, ScreenEvent.Action action, long presentDuration) { 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);
} }
} }

View File

@ -0,0 +1,29 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package edu.tsinghua.hotmobi;
import android.content.Context;
/**
* Created by mariotaku on 15/12/24.
*/
public interface PreProcessing<T> {
void process(T event, Context appContext);
}

View File

@ -110,7 +110,6 @@ public class UploadLogsTask implements Runnable {
if (response.isSuccessful()) { if (response.isSuccessful()) {
succeeded &= logFile.delete(); succeeded &= logFile.delete();
} }
response.close();
} catch (IOException e) { } catch (IOException e) {
Log.w(HotMobiLogger.LOGTAG, e); Log.w(HotMobiLogger.LOGTAG, e);
succeeded = false; succeeded = false;

View File

@ -21,6 +21,7 @@ package edu.tsinghua.hotmobi;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import com.bluelinelabs.logansquare.LoganSquare; import com.bluelinelabs.logansquare.LoganSquare;
@ -40,24 +41,28 @@ import java.util.List;
/** /**
* Created by mariotaku on 15/8/23. * Created by mariotaku on 15/8/23.
*/ */
public class WriteLogTask implements Runnable, Constants { public class WriteLogTask<T> implements Runnable, Constants {
private static final byte[] LF = {'\n'}; private static final byte[] LF = {'\n'};
private final Context context; private final Context context;
private final long accountId; private final long accountId;
private final String type; private final String type;
private final List<?> events; private final List<T> events;
@Nullable
private final PreProcessing<T> preProcessing;
public WriteLogTask(Context context, long accountId, Object event) { public WriteLogTask(Context context, long accountId, T event, @Nullable PreProcessing<T> preProcessing) {
this(context, accountId, HotMobiLogger.getLogFilename(event), Collections.singletonList(event)); 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<T> events,
@Nullable PreProcessing<T> preProcessing) {
this.context = context; this.context = context;
this.accountId = accountId; this.accountId = accountId;
this.type = type; this.type = type;
this.events = events; this.events = events;
this.preProcessing = preProcessing;
} }
@Override @Override
@ -71,7 +76,10 @@ public class WriteLogTask implements Runnable, Constants {
raf = new RandomAccessFile(HotMobiLogger.getLogFile(context, accountId, type), "rw"); raf = new RandomAccessFile(HotMobiLogger.getLogFile(context, accountId, type), "rw");
fc = raf.getChannel(); fc = raf.getChannel();
final FileLock lock = fc.lock(); final FileLock lock = fc.lock();
for (Object event : events) { for (T event : events) {
if (preProcessing != null) {
preProcessing.process(event, context);
}
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
if (accountId > 0) { if (accountId > 0) {
Log.v(HotMobiLogger.LOGTAG, "Log " + type + " for account " + accountId + ": " + event); Log.v(HotMobiLogger.LOGTAG, "Log " + type + " for account " + accountId + ": " + event);

View File

@ -33,7 +33,7 @@ import static org.mariotaku.twidere.annotation.Preference.Type.STRING;
public interface Constants extends TwidereConstants { public interface Constants extends TwidereConstants {
String DATABASES_NAME = "twidere.sqlite"; String DATABASES_NAME = "twidere.sqlite";
int DATABASES_VERSION = 114; int DATABASES_VERSION = 115;
int MENU_GROUP_STATUS_EXTENSION = 10; int MENU_GROUP_STATUS_EXTENSION = 10;
int MENU_GROUP_COMPOSE_EXTENSION = 11; int MENU_GROUP_COMPOSE_EXTENSION = 11;

View File

@ -85,6 +85,7 @@ import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.VideoLoader.VideoLoadingListener; import org.mariotaku.twidere.util.VideoLoader.VideoLoadingListener;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import pl.droidsonroids.gif.GifSupportChecker; import pl.droidsonroids.gif.GifSupportChecker;
@ -402,7 +403,8 @@ public final class MediaViewerActivity extends BaseAppCompatActivity implements
public void onPrepareOptionsMenu(Menu menu) { public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu); super.onPrepareOptionsMenu(menu);
final boolean isLoading = getLoaderManager().hasRunningLoaders(); final boolean isLoading = getLoaderManager().hasRunningLoaders();
final TaskRunnable<File, Pair<Boolean, Intent>, Pair<Fragment, Menu>> checkState = new TaskRunnable<File, Pair<Boolean, Intent>, Pair<Fragment, Menu>>() { final TaskRunnable<File, Pair<Boolean, Intent>, Pair<Fragment, Menu>> checkState
= new TaskRunnable<File, Pair<Boolean, Intent>, Pair<Fragment, Menu>>() {
@Override @Override
public Pair<Boolean, Intent> doLongOperation(File file) throws InterruptedException { public Pair<Boolean, Intent> doLongOperation(File file) throws InterruptedException {
final boolean hasImage = file != null && file.exists(); 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 Intent intent = new Intent(Intent.ACTION_SEND);
final Uri fileUri = Uri.fromFile(file); 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); intent.putExtra(Intent.EXTRA_STREAM, fileUri);
final MediaViewerActivity activity = (MediaViewerActivity) getActivity(); final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
if (activity.hasStatus()) { if (activity.hasStatus()) {

View File

@ -102,6 +102,8 @@ import org.mariotaku.twidere.util.view.ConsumerKeySecretValidator;
import org.mariotaku.twidere.view.TintedStatusNativeActionModeAwareLayout; import org.mariotaku.twidere.view.TintedStatusNativeActionModeAwareLayout;
import org.mariotaku.twidere.view.iface.TintedStatusLayout; import org.mariotaku.twidere.view.iface.TintedStatusLayout;
import java.lang.ref.WeakReference;
import static android.text.TextUtils.isEmpty; import static android.text.TextUtils.isEmpty;
import static org.mariotaku.twidere.util.ContentValuesCreator.createAccount; import static org.mariotaku.twidere.util.ContentValuesCreator.createAccount;
import static org.mariotaku.twidere.util.DataStoreUtils.getActivatedAccountIds; 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<Object, Runnable, SignInResponse> { public static abstract class AbstractSignInTask extends AsyncTask<Object, Runnable, SignInResponse> {
protected final SignInActivity activity; protected final WeakReference<SignInActivity> activityRef;
public AbstractSignInTask(final SignInActivity activity) { public AbstractSignInTask(final SignInActivity activity) {
this.activity = activity; this.activityRef = new WeakReference<>(activity);
} }
@Override @Override
protected void onPostExecute(final SignInResponse result) { protected void onPostExecute(final SignInResponse result) {
final SignInActivity activity = activityRef.get();
if (activity != null) { if (activity != null) {
activity.onSignInResult(result); activity.onSignInResult(result);
} }
@ -592,6 +595,7 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
final SignInActivity activity = activityRef.get();
if (activity != null) { if (activity != null) {
activity.onSignInStart(); activity.onSignInStart();
} }
@ -730,6 +734,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
} }
private SignInResponse authBasic() throws TwitterException { 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 String versionSuffix = noVersionSuffix ? null : "1.1";
final Endpoint endpoint = new Endpoint(TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", versionSuffix)); final Endpoint endpoint = new Endpoint(TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", versionSuffix));
final Authorization auth = new BasicAuthorization(username, password); final Authorization auth = new BasicAuthorization(username, password);
@ -744,6 +750,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
} }
private SignInResponse authOAuth() throws AuthenticationException, TwitterException { 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); Endpoint endpoint = TwitterAPIFactory.getOAuthEndpoint(apiUrlFormat, "api", null, sameOAuthSigningUrl);
OAuthAuthorization auth = new OAuthAuthorization(consumerKey.getOauthToken(), consumerKey.getOauthTokenSecret()); OAuthAuthorization auth = new OAuthAuthorization(consumerKey.getOauthToken(), consumerKey.getOauthTokenSecret());
final TwitterOAuth oauth = TwitterAPIFactory.getInstance(activity, endpoint, auth, TwitterOAuth.class); 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 { 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 String versionSuffix = noVersionSuffix ? null : "1.1";
final Endpoint endpoint = new Endpoint(TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", versionSuffix)); final Endpoint endpoint = new Endpoint(TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", versionSuffix));
final Authorization auth = new EmptyAuthorization(); final Authorization auth = new EmptyAuthorization();
@ -774,6 +784,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
} }
private SignInResponse authxAuth() throws TwitterException { 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); Endpoint endpoint = TwitterAPIFactory.getOAuthEndpoint(apiUrlFormat, "api", null, sameOAuthSigningUrl);
OAuthAuthorization auth = new OAuthAuthorization(consumerKey.getOauthToken(), consumerKey.getOauthTokenSecret()); OAuthAuthorization auth = new OAuthAuthorization(consumerKey.getOauthToken(), consumerKey.getOauthTokenSecret());
final TwitterOAuth oauth = TwitterAPIFactory.getInstance(activity, endpoint, auth, TwitterOAuth.class); final TwitterOAuth oauth = TwitterAPIFactory.getInstance(activity, endpoint, auth, TwitterOAuth.class);
@ -800,6 +812,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
publishProgress(new Runnable() { publishProgress(new Runnable() {
@Override @Override
public void run() { public void run() {
final SignInActivity activity = activityRef.get();
if (activity == null) return;
activity.dismissDialogFragment(FRAGMENT_TAG_SIGN_IN_PROGRESS); activity.dismissDialogFragment(FRAGMENT_TAG_SIGN_IN_PROGRESS);
} }
}); });
@ -807,6 +821,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
publishProgress(new Runnable() { publishProgress(new Runnable() {
@Override @Override
public void run() { public void run() {
final SignInActivity activity = activityRef.get();
if (activity == null) return;
activity.postAfterFragmentResumed(new Runnable() { activity.postAfterFragmentResumed(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -830,6 +846,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
publishProgress(new Runnable() { publishProgress(new Runnable() {
@Override @Override
public void run() { public void run() {
final SignInActivity activity = activityRef.get();
if (activity == null) return;
activity.showSignInProgressDialog(); activity.showSignInProgressDialog();
} }
}); });

View File

@ -72,7 +72,6 @@ public abstract class AbsActivitiesAdapter<Data> extends LoadMoreSupportAdapter<
private final MediaLoadingHandler mLoadingHandler; private final MediaLoadingHandler mLoadingHandler;
private final int mCardBackgroundColor; private final int mCardBackgroundColor;
private final boolean mCompactCards; private final boolean mCompactCards;
private final TwidereLinkify mLinkify;
private final DummyStatusHolderAdapter mStatusAdapterDelegate; private final DummyStatusHolderAdapter mStatusAdapterDelegate;
private ActivityAdapterListener mActivityAdapterListener; private ActivityAdapterListener mActivityAdapterListener;
@ -80,14 +79,13 @@ public abstract class AbsActivitiesAdapter<Data> extends LoadMoreSupportAdapter<
protected AbsActivitiesAdapter(final Context context, boolean compact) { protected AbsActivitiesAdapter(final Context context, boolean compact) {
super(context); super(context);
mStatusAdapterDelegate = new DummyStatusHolderAdapter(context); mStatusAdapterDelegate = new DummyStatusHolderAdapter(context, new TwidereLinkify(new OnLinkClickHandler(context, null)));
mCardBackgroundColor = ThemeUtils.getCardBackgroundColor(context, mCardBackgroundColor = ThemeUtils.getCardBackgroundColor(context,
ThemeUtils.getThemeBackgroundOption(context), ThemeUtils.getThemeBackgroundOption(context),
ThemeUtils.getUserThemeBackgroundAlpha(context)); ThemeUtils.getUserThemeBackgroundAlpha(context));
mInflater = LayoutInflater.from(context); mInflater = LayoutInflater.from(context);
mLoadingHandler = new MediaLoadingHandler(R.id.media_preview_progress); mLoadingHandler = new MediaLoadingHandler(R.id.media_preview_progress);
mCompactCards = compact; mCompactCards = compact;
mLinkify = new TwidereLinkify(new OnLinkClickHandler(context, null));
mStatusAdapterDelegate.updateOptions(); mStatusAdapterDelegate.updateOptions();
} }
@ -133,10 +131,6 @@ public abstract class AbsActivitiesAdapter<Data> extends LoadMoreSupportAdapter<
return mStatusAdapterDelegate.getLinkHighlightingStyle(); return mStatusAdapterDelegate.getLinkHighlightingStyle();
} }
public TwidereLinkify getLinkify() {
return mLinkify;
}
public boolean isNameFirst() { public boolean isNameFirst() {
return mStatusAdapterDelegate.isNameFirst(); return mStatusAdapterDelegate.isNameFirst();
} }

View File

@ -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_CONVERSATION_LOAD_INDICATOR = 2;
private static final int VIEW_TYPE_REPLIES_LOAD_INDICATOR = 3; 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_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_ERROR = 0;
private static final int ITEM_IDX_CONVERSATION_LOAD_MORE = 1; private static final int ITEM_IDX_CONVERSATION_LOAD_MORE = 1;
private static final int ITEM_IDX_CONVERSATION = 2; private static final int ITEM_IDX_CONVERSATION = 2;
@ -1300,7 +1301,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
private List<ParcelableStatus> mConversation, mReplies; private List<ParcelableStatus> mConversation, mReplies;
private StatusAdapterListener mStatusAdapterListener; private StatusAdapterListener mStatusAdapterListener;
private RecyclerView mRecyclerView; private RecyclerView mRecyclerView;
private CharSequence mReplyError; private CharSequence mReplyError, mConversationError;
private boolean mRepliesLoading, mConversationsLoading; private boolean mRepliesLoading, mConversationsLoading;
private TranslationResult mTranslationResult; private TranslationResult mTranslationResult;
@ -1615,6 +1616,11 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
errorHolder.showError(mReplyError); errorHolder.showError(mReplyError);
break; break;
} }
case VIEW_TYPE_CONVERSATION_ERROR: {
final StatusErrorItemViewHolder errorHolder = (StatusErrorItemViewHolder) holder;
errorHolder.showError(mReplyError);
break;
}
case VIEW_TYPE_CONVERSATION_LOAD_INDICATOR: { case VIEW_TYPE_CONVERSATION_LOAD_INDICATOR: {
LoadIndicatorViewHolder indicatorHolder = ((LoadIndicatorViewHolder) holder); LoadIndicatorViewHolder indicatorHolder = ((LoadIndicatorViewHolder) holder);
indicatorHolder.setLoadProgressVisible(mConversationsLoading); indicatorHolder.setLoadProgressVisible(mConversationsLoading);
@ -1660,6 +1666,8 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
return VIEW_TYPE_SPACE; return VIEW_TYPE_SPACE;
case ITEM_IDX_REPLY_ERROR: case ITEM_IDX_REPLY_ERROR:
return VIEW_TYPE_REPLY_ERROR; return VIEW_TYPE_REPLY_ERROR;
case ITEM_IDX_CONVERSATION_ERROR:
return VIEW_TYPE_CONVERSATION_ERROR;
} }
throw new IllegalStateException(); throw new IllegalStateException();
} }
@ -1781,6 +1789,12 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
updateItemDecoration(); updateItemDecoration();
} }
public void setConversationError(CharSequence error) {
mConversationError = error;
setCount(ITEM_IDX_CONVERSATION_ERROR, error != null ? 1 : 0);
updateItemDecoration();
}
public void setReplies(List<ParcelableStatus> replies) { public void setReplies(List<ParcelableStatus> replies) {
mReplies = replies; mReplies = replies;
setCount(ITEM_IDX_REPLY, replies != null ? replies.size() : 0); setCount(ITEM_IDX_REPLY, replies != null ? replies.size() : 0);
@ -1857,7 +1871,6 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
@Override @Override
public int getDecoratedMeasuredHeight(View child) { public int getDecoratedMeasuredHeight(View child) {
final int height = super.getDecoratedMeasuredHeight(child);
int heightBeforeSpace = 0; int heightBeforeSpace = 0;
if (getItemViewType(child) == StatusAdapter.VIEW_TYPE_SPACE) { if (getItemViewType(child) == StatusAdapter.VIEW_TYPE_SPACE) {
for (int i = 0, j = getChildCount(); i < j; i++) { 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 Math.max(0, spaceHeight);
} }
} }
return height; return super.getDecoratedMeasuredHeight(child);
} }
@Override @Override

View File

@ -22,12 +22,14 @@ package org.mariotaku.twidere.util;
import android.app.Activity; import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import org.apache.commons.collections.primitives.ArrayIntList; import org.apache.commons.collections.primitives.ArrayIntList;
import org.apache.commons.collections.primitives.IntList; import org.apache.commons.collections.primitives.IntList;
import edu.tsinghua.hotmobi.HotMobiLogger; import edu.tsinghua.hotmobi.HotMobiLogger;
import edu.tsinghua.hotmobi.PreProcessing;
import edu.tsinghua.hotmobi.model.SessionEvent; import edu.tsinghua.hotmobi.model.SessionEvent;
/** /**
@ -56,7 +58,7 @@ public class ActivityTracker implements Application.ActivityLifecycleCallbacks {
} }
@Override @Override
public void onActivityStarted(Activity activity) { public void onActivityStarted(final Activity activity) {
mInternalStack.add(System.identityHashCode(activity)); mInternalStack.add(System.identityHashCode(activity));
// BEGIN HotMobi // BEGIN HotMobi
if (mSessionEvent == null) { if (mSessionEvent == null) {
@ -82,9 +84,13 @@ public class ActivityTracker implements Application.ActivityLifecycleCallbacks {
// BEGIN HotMobi // BEGIN HotMobi
final SessionEvent event = mSessionEvent; final SessionEvent event = mSessionEvent;
if (event != null && !isSwitchingInSameTask(hashCode)) { if (event != null && !isSwitchingInSameTask(hashCode)) {
event.dumpPreferences(activity);
event.markEnd(); event.markEnd();
HotMobiLogger.getInstance(activity).log(event); HotMobiLogger.getInstance(activity).log(event, new PreProcessing<SessionEvent>() {
@Override
public void process(SessionEvent event, Context appContext) {
event.dumpPreferences(appContext);
}
});
mSessionEvent = null; mSessionEvent = null;
} }
// END HotMobi // END HotMobi

View File

@ -42,8 +42,9 @@ public class EmojiSupportUtils {
for (int i = array.length() - 1; i >= 0; i--) { for (int i = array.length() - 1; i >= 0; i--) {
final int codePoint = array.get(i); final int codePoint = array.get(i);
if (isEmoji(codePoint)) { if (isEmoji(codePoint)) {
int arrayIdx = i, arrayEnd = i + 1; int arrayIdx = i, arrayEnd = i + 1, arrayIdxOffset = 0;
int textIdx = array.indexOfText(codePoint, i); int textIdx = array.indexOfText(codePoint, i), textIdxOffset = 0;
int indexOffset = 0;
if (textIdx == -1 || textIdx < textStart) { if (textIdx == -1 || textIdx < textStart) {
continue; continue;
} }
@ -52,29 +53,58 @@ public class EmojiSupportUtils {
if (i > 0) { if (i > 0) {
int prev = array.get(i - 1); int prev = array.get(i - 1);
if (isRegionalIndicatorSymbol(prev)) { if (isRegionalIndicatorSymbol(prev)) {
textIdx -= Character.charCount(prev); arrayIdxOffset = -1;
arrayIdx--; textIdxOffset = -Character.charCount(prev);
i--; indexOffset = -1;
} }
} }
} else if (isModifier(codePoint)) { } else if (isModifier(codePoint)) {
if (i > 0) { if (i > 0) {
int prev = array.get(i - 1); int prev = array.get(i - 1);
if (isEmoji(prev)) { if (isEmoji(prev)) {
textIdx -= Character.charCount(prev); arrayIdxOffset = -1;
arrayIdx--; textIdxOffset = -Character.charCount(prev);
i--; 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; if (textEnd > textStart + textLength) continue;
final EmojiSpan[] spans = text.getSpans(textIdx, textEnd, EmojiSpan.class); EmojiSpan[] spans = text.getSpans(textIdx + textIdxOffset, textEnd, EmojiSpan.class);
if (spans.length > 0) continue; if (spans.length == 0) {
final Drawable drawable = emoji.getEmojiDrawableFor(array.subarray(arrayIdx, arrayEnd)); Drawable drawable = emoji.getEmojiDrawableFor(array.subarray(arrayIdx + arrayIdxOffset,
if (drawable == null) continue; arrayEnd));
text.setSpan(new EmojiSpan(drawable), textIdx, textEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 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) { private static boolean isModifier(int codePoint) {
@ -93,4 +123,8 @@ public class EmojiSupportUtils {
return inRange(codePoint, 0x1f1e6, 0x1f1ff); return inRange(codePoint, 0x1f1e6, 0x1f1ff);
} }
private static boolean isKeyCap(int codePoint) {
return codePoint == 0x20e3;
}
} }

View File

@ -11,8 +11,11 @@ import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.webkit.URLUtil; import android.webkit.URLUtil;
import com.squareup.okhttp.Authenticator;
import com.squareup.okhttp.Dns; import com.squareup.okhttp.Dns;
import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.math.NumberUtils;
import org.mariotaku.restfu.ExceptionFactory; 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.dagger.ApplicationModule;
import org.mariotaku.twidere.util.net.NetworkUsageUtils; import org.mariotaku.twidere.util.net.NetworkUsageUtils;
import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Proxy; import java.net.Proxy;
import java.net.SocketAddress; import java.net.SocketAddress;
@ -118,6 +122,7 @@ public class TwitterAPIFactory implements TwidereConstants {
final OkHttpClient client = new OkHttpClient(); final OkHttpClient client = new OkHttpClient();
updateHttpClientConfiguration(prefs, client); updateHttpClientConfiguration(prefs, client);
client.setDns(dns); client.setDns(dns);
DebugModeUtils.initForHttpClient(client);
NetworkUsageUtils.initForHttpClient(context, client); NetworkUsageUtils.initForHttpClient(context, client);
return new OkHttpRestClient(client); return new OkHttpRestClient(client);
} }
@ -386,8 +391,8 @@ public class TwitterAPIFactory implements TwidereConstants {
sExtraParams.put("include_cards", "true"); sExtraParams.put("include_cards", "true");
sExtraParams.put("cards_platform", "Android-12"); sExtraParams.put("cards_platform", "Android-12");
sExtraParams.put("include_entities", "true"); sExtraParams.put("include_entities", "true");
sExtraParams.put("include_my_retweet", "1"); sExtraParams.put("include_my_retweet", "true");
sExtraParams.put("include_rts", "1"); sExtraParams.put("include_rts", "true");
sExtraParams.put("include_reply_count", "true"); sExtraParams.put("include_reply_count", "true");
sExtraParams.put("include_descendent_reply_count", "true"); sExtraParams.put("include_descendent_reply_count", "true");
sExtraParams.put("full_text", "true"); sExtraParams.put("full_text", "true");

View File

@ -38,11 +38,16 @@ import org.mariotaku.twidere.util.ConnectivityUtils;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/** /**
* Created by mariotaku on 15/6/24. * Created by mariotaku on 15/6/24.
*/ */
public class NetworkUsageUtils implements Constants { public class NetworkUsageUtils implements Constants {
private static final Executor sNetworkUsageExecutor = Executors.newSingleThreadExecutor();
public static void initForHttpClient(Context context, OkHttpClient client) { public static void initForHttpClient(Context context, OkHttpClient client) {
final List<Interceptor> interceptors = client.networkInterceptors(); final List<Interceptor> interceptors = client.networkInterceptors();
interceptors.add(new NetworkUsageInterceptor(context)); interceptors.add(new NetworkUsageInterceptor(context));
@ -74,12 +79,17 @@ public class NetworkUsageUtils implements Constants {
values.put(NetworkUsages.REQUEST_NETWORK, sNetworkType); values.put(NetworkUsages.REQUEST_NETWORK, sNetworkType);
final Response response = chain.proceed(request); final Response response = chain.proceed(request);
values.put(NetworkUsages.KILOBYTES_RECEIVED, getBodyLength(response.body()) / 1024.0); values.put(NetworkUsages.KILOBYTES_RECEIVED, getBodyLength(response.body()) / 1024.0);
sNetworkUsageExecutor.execute(new Runnable() {
@Override
public void run() {
final ContentResolver cr = context.getContentResolver(); final ContentResolver cr = context.getContentResolver();
try { try {
cr.insert(NetworkUsages.CONTENT_URI, values); cr.insert(NetworkUsages.CONTENT_URI, values);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Log.e(LOGTAG, "Unable to log network usage", e); Log.e(LOGTAG, "Unable to log network usage", e);
} }
}
});
return response; return response;
} }

View File

@ -87,8 +87,8 @@ public class TwidereDns implements Constants, Dns {
// First, I'll try to load address cached. // First, I'll try to load address cached.
final InetAddress[] cachedHostAddr = mHostCache.get(host); final InetAddress[] cachedHostAddr = mHostCache.get(host);
if (cachedHostAddr != null) { if (cachedHostAddr != null) {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) {
Log.d(RESOLVER_LOGTAG, "Got cached " + Arrays.toString(cachedHostAddr)); Log.v(RESOLVER_LOGTAG, "Got cached " + Arrays.toString(cachedHostAddr));
return cachedHostAddr; return cachedHostAddr;
} }
} }
@ -100,7 +100,7 @@ public class TwidereDns implements Constants, Dns {
final InetAddress[] hostAddr = fromAddressString(originalHost, mappedAddr); final InetAddress[] hostAddr = fromAddressString(originalHost, mappedAddr);
putCache(originalHost, hostAddr); putCache(originalHost, hostAddr);
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Log.d(RESOLVER_LOGTAG, "Got mapped " + Arrays.toString(hostAddr)); Log.v(RESOLVER_LOGTAG, "Got mapped " + Arrays.toString(hostAddr));
} }
if (hostAddr != null) { if (hostAddr != null) {
return hostAddr; return hostAddr;
@ -110,8 +110,8 @@ public class TwidereDns implements Constants, Dns {
try { try {
final InetAddress[] hostAddr = mResolver.resolve(host); final InetAddress[] hostAddr = mResolver.resolve(host);
putCache(originalHost, hostAddr); putCache(originalHost, hostAddr);
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) {
Log.d(RESOLVER_LOGTAG, "Got hosts " + Arrays.toString(hostAddr)); Log.v(RESOLVER_LOGTAG, "Got hosts " + Arrays.toString(hostAddr));
} }
return hostAddr; return hostAddr;
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
@ -121,8 +121,8 @@ public class TwidereDns implements Constants, Dns {
if (customMappedHost != null) { if (customMappedHost != null) {
final InetAddress[] hostAddr = fromAddressString(originalHost, customMappedHost); final InetAddress[] hostAddr = fromAddressString(originalHost, customMappedHost);
putCache(originalHost, hostAddr); putCache(originalHost, hostAddr);
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) {
Log.d(RESOLVER_LOGTAG, "Got mapped address " + customMappedHost + " for host " + host); Log.v(RESOLVER_LOGTAG, "Got mapped address " + customMappedHost + " for host " + host);
} }
if (hostAddr != null) { if (hostAddr != null) {
return hostAddr; return hostAddr;
@ -154,8 +154,8 @@ public class TwidereDns implements Constants, Dns {
if (!resolvedAddresses.isEmpty()) { if (!resolvedAddresses.isEmpty()) {
final InetAddress[] hostAddr = resolvedAddresses.toArray(new InetAddress[resolvedAddresses.size()]); final InetAddress[] hostAddr = resolvedAddresses.toArray(new InetAddress[resolvedAddresses.size()]);
putCache(originalHost, hostAddr); putCache(originalHost, hostAddr);
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) {
Log.d(RESOLVER_LOGTAG, "Resolved " + Arrays.toString(hostAddr)); Log.v(RESOLVER_LOGTAG, "Resolved " + Arrays.toString(hostAddr));
} }
return hostAddr; return hostAddr;
} }
@ -166,8 +166,8 @@ public class TwidereDns implements Constants, Dns {
return resolveInternal(originalHost, ((CNAMERecord) record).getTarget().toString()); return resolveInternal(originalHost, ((CNAMERecord) record).getTarget().toString());
} }
} }
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) {
Log.w(RESOLVER_LOGTAG, "Resolve address " + host + " failed, using original host"); Log.v(RESOLVER_LOGTAG, "Resolve address " + host + " failed, using original host");
} }
final InetAddress[] defaultAddresses = InetAddress.getAllByName(host); final InetAddress[] defaultAddresses = InetAddress.getAllByName(host);
putCache(host, defaultAddresses); putCache(host, defaultAddresses);

View File

@ -31,6 +31,8 @@ import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.view.themed.ThemedTextView; import org.mariotaku.twidere.view.themed.ThemedTextView;
import java.lang.ref.WeakReference;
import static android.text.format.DateUtils.getRelativeTimeSpanString; import static android.text.format.DateUtils.getRelativeTimeSpanString;
import static org.mariotaku.twidere.util.Utils.formatSameDayTime; 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 static class TickerRunnable implements Runnable {
private final ShortTimeView mTextView; private final WeakReference<ShortTimeView> mViewRef;
private TickerRunnable(final ShortTimeView view) { private TickerRunnable(final ShortTimeView view) {
mTextView = view; mViewRef = new WeakReference<>(view);
} }
@Override @Override
public void run() { 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; if (handler == null) return;
mTextView.invalidateTime(); view.invalidateTime();
final long now = SystemClock.uptimeMillis(); final long now = SystemClock.uptimeMillis();
final long next = now + TICKER_DURATION - now % TICKER_DURATION; final long next = now + TICKER_DURATION - now % TICKER_DURATION;
handler.postAtTime(this, next); handler.postAtTime(this, next);

View File

@ -26,7 +26,6 @@ import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.widget.RecyclerView.ViewHolder; import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.Spanned; import android.text.Spanned;
import android.text.method.ArrowKeyMovementMethod;
import android.view.View; import android.view.View;
import android.widget.TextView; 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.HtmlSpanBuilder;
import org.mariotaku.twidere.util.JsonSerializer; import org.mariotaku.twidere.util.JsonSerializer;
import org.mariotaku.twidere.util.MediaLoaderWrapper; import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.StatusActionModeCallback;
import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereColorUtils; import org.mariotaku.twidere.util.TwidereColorUtils;
import org.mariotaku.twidere.util.TwidereLinkify; import org.mariotaku.twidere.util.TwidereLinkify;
@ -55,7 +53,6 @@ public class MessageViewHolder extends ViewHolder implements OnMediaClickListene
protected final MessageConversationAdapter adapter; protected final MessageConversationAdapter adapter;
private final int textColorPrimary, textColorPrimaryInverse, textColorSecondary, textColorSecondaryInverse; private final int textColorPrimary, textColorPrimaryInverse, textColorSecondary, textColorSecondaryInverse;
private final StatusActionModeCallback callback;
public MessageViewHolder(final MessageConversationAdapter adapter, final View itemView) { 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); time = (TextView) itemView.findViewById(R.id.time);
mediaContainer = (CardMediaContainer) itemView.findViewById(R.id.media_preview_container); mediaContainer = (CardMediaContainer) itemView.findViewById(R.id.media_preview_container);
mediaContainer.setStyle(adapter.getMediaPreviewStyle()); mediaContainer.setStyle(adapter.getMediaPreviewStyle());
callback = new StatusActionModeCallback(textView, adapter.getContext());
} }
public void displayMessage(Cursor cursor, ParcelableDirectMessageCursorIndices indices) { public void displayMessage(Cursor cursor, ParcelableDirectMessageCursorIndices indices) {
@ -92,10 +88,6 @@ public class MessageViewHolder extends ViewHolder implements OnMediaClickListene
time.setText(Utils.formatToLongTimeString(context, timestamp)); time.setText(Utils.formatToLongTimeString(context, timestamp));
mediaContainer.setVisibility(media != null && media.length > 0 ? View.VISIBLE : View.GONE); mediaContainer.setVisibility(media != null && media.length > 0 ? View.VISIBLE : View.GONE);
mediaContainer.displayMedia(media, loader, accountId, true, this, adapter.getMediaLoadingHandler()); mediaContainer.displayMedia(media, loader, accountId, true, this, adapter.getMediaLoadingHandler());
textView.setTextIsSelectable(true);
textView.setMovementMethod(ArrowKeyMovementMethod.getInstance());
textView.setCustomSelectionActionModeCallback(callback);
} }
@Override @Override

View File

@ -471,11 +471,15 @@ public class StatusViewHolder extends ViewHolder implements Constants, OnClickLi
private boolean useStarsForLikes; private boolean useStarsForLikes;
public DummyStatusHolderAdapter(Context context) { public DummyStatusHolderAdapter(Context context) {
this(context, new TwidereLinkify(null));
}
public DummyStatusHolderAdapter(Context context, TwidereLinkify linkify) {
DaggerGeneralComponent.builder().applicationModule(ApplicationModule.get(context)).build().inject(this); DaggerGeneralComponent.builder().applicationModule(ApplicationModule.get(context)).build().inject(this);
this.context = context; this.context = context;
preferences = SharedPreferencesWrapper.getInstance(context, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); preferences = SharedPreferencesWrapper.getInstance(context, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
handler = new MediaLoadingHandler(R.id.media_preview_progress); handler = new MediaLoadingHandler(R.id.media_preview_progress);
linkify = new TwidereLinkify(null); this.linkify = linkify;
updateOptions(); updateOptions();
} }

View File

@ -17,10 +17,11 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>. ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<org.mariotaku.twidere.view.ExtendedRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <org.mariotaku.twidere.view.ExtendedRelativeLayout
android:id="@+id/main_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="@dimen/element_spacing_normal"> android:padding="@dimen/element_spacing_normal">
@ -53,7 +54,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="0" android:layout_weight="0"
tools:listitem="@layout/spinner_item_account_icon" /> tools:listitem="@layout/spinner_item_account_icon"/>
<EditText <EditText
android:id="@+id/search_query" android:id="@+id/search_query"
@ -65,7 +66,7 @@
android:hint="@string/search_hint" android:hint="@string/search_hint"
android:inputType="text|textMultiLine"> android:inputType="text|textMultiLine">
<requestFocus /> <requestFocus/>
</EditText> </EditText>
<org.mariotaku.twidere.view.ActionIconButton <org.mariotaku.twidere.view.ActionIconButton
@ -75,14 +76,14 @@
android:layout_weight="0" android:layout_weight="0"
android:background="?actionBarItemBackground" android:background="?actionBarItemBackground"
android:color="?menuIconColor" android:color="?menuIconColor"
android:src="@drawable/ic_action_search" /> android:src="@drawable/ic_action_search"/>
</LinearLayout> </LinearLayout>
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="2dp" android:layout_height="2dp"
android:background="#20808080" /> android:background="#20808080"/>
<ListView <ListView
android:id="@+id/suggestions_list" android:id="@+id/suggestions_list"
@ -90,7 +91,7 @@
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
android:listSelector="?selectableItemBackground" android:listSelector="?selectableItemBackground"
tools:listitem="@layout/list_item_user" /> tools:listitem="@layout/list_item_user"/>
</LinearLayout> </LinearLayout>
</android.support.v7.widget.CardView> </android.support.v7.widget.CardView>

View File

@ -28,7 +28,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<include layout="@layout/layout_card_media_preview" /> <include layout="@layout/layout_card_media_preview"/>
</org.mariotaku.twidere.view.CardMediaContainer> </org.mariotaku.twidere.view.CardMediaContainer>
@ -45,8 +45,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:textIsSelectable="true" tools:text="@string/sample_status_text"/>
tools:text="@string/sample_status_text" />
<org.mariotaku.twidere.view.themed.ThemedTextView <org.mariotaku.twidere.view.themed.ThemedTextView
android:id="@+id/time" android:id="@+id/time"
@ -54,6 +53,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/element_spacing_small" android:layout_marginTop="@dimen/element_spacing_small"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
tools:text="12:00" /> tools:text="12:00"/>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -16,10 +16,10 @@
~ You should have received a copy of the GNU General Public License ~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>. ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<org.mariotaku.twidere.view.ColorLabelFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <org.mariotaku.twidere.view.ColorLabelFrameLayout android:id="@+id/item_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:background="?cardItemBackgroundColor"> tools:background="?cardItemBackgroundColor">
@ -47,7 +47,7 @@
android:scaleType="centerInside" android:scaleType="centerInside"
android:visibility="gone" android:visibility="gone"
tools:src="@drawable/ic_activity_action_retweet" tools:src="@drawable/ic_activity_action_retweet"
tools:visibility="visible" /> tools:visibility="visible"/>
<org.mariotaku.twidere.view.ActionIconThemedTextView <org.mariotaku.twidere.view.ActionIconThemedTextView
android:id="@+id/reply_retweet_status" android:id="@+id/reply_retweet_status"
@ -66,7 +66,7 @@
android:visibility="gone" android:visibility="gone"
tools:text="Retweeted by Mariotaku" tools:text="Retweeted by Mariotaku"
tools:textSize="@dimen/text_size_extra_small" tools:textSize="@dimen/text_size_extra_small"
tools:visibility="visible" /> tools:visibility="visible"/>
<org.mariotaku.twidere.view.ProfileImageView <org.mariotaku.twidere.view.ProfileImageView
android:id="@+id/profile_image" android:id="@+id/profile_image"
@ -83,7 +83,7 @@
android:layout_marginRight="@dimen/element_spacing_small" android:layout_marginRight="@dimen/element_spacing_small"
android:contentDescription="@string/profile_image" android:contentDescription="@string/profile_image"
android:scaleType="centerCrop" android:scaleType="centerCrop"
tools:visibility="visible" /> tools:visibility="visible"/>
<org.mariotaku.twidere.view.BoundsImageView <org.mariotaku.twidere.view.BoundsImageView
android:id="@+id/profile_type" android:id="@+id/profile_type"
@ -96,7 +96,7 @@
android:layout_marginEnd="@dimen/element_spacing_minus_small" android:layout_marginEnd="@dimen/element_spacing_minus_small"
android:layout_marginRight="@dimen/element_spacing_minus_small" android:layout_marginRight="@dimen/element_spacing_minus_small"
android:scaleType="fitCenter" android:scaleType="fitCenter"
tools:visibility="gone" /> tools:visibility="gone"/>
<RelativeLayout <RelativeLayout
android:id="@+id/status_content" android:id="@+id/status_content"
@ -123,7 +123,7 @@
android:layout_weight="1" android:layout_weight="1"
app:nv_primaryTextColor="?android:textColorPrimary" app:nv_primaryTextColor="?android:textColorPrimary"
app:nv_primaryTextStyle="bold" app:nv_primaryTextStyle="bold"
app:nv_secondaryTextColor="?android:textColorSecondary" /> app:nv_secondaryTextColor="?android:textColorSecondary"/>
<org.mariotaku.twidere.view.ShortTimeView <org.mariotaku.twidere.view.ShortTimeView
android:id="@+id/time" android:id="@+id/time"
@ -132,7 +132,7 @@
android:layout_weight="0" android:layout_weight="0"
android:textAppearance="?android:textAppearanceSmall" android:textAppearance="?android:textAppearanceSmall"
tools:text="42 mins ago" tools:text="42 mins ago"
tools:textSize="@dimen/text_size_extra_small" /> tools:textSize="@dimen/text_size_extra_small"/>
<org.mariotaku.twidere.view.ActionIconView <org.mariotaku.twidere.view.ActionIconView
android:id="@+id/extra_type" android:id="@+id/extra_type"
@ -140,7 +140,7 @@
android:layout_height="@dimen/element_size_small" android:layout_height="@dimen/element_size_small"
android:layout_weight="0" android:layout_weight="0"
android:color="?android:textColorSecondary" android:color="?android:textColorSecondary"
tools:src="@drawable/ic_action_gallery" /> tools:src="@drawable/ic_action_gallery"/>
</LinearLayout> </LinearLayout>
@ -156,9 +156,8 @@
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:visibility="visible" android:visibility="visible"
app:nv_primaryTextStyle="bold"
tools:text="@string/sample_status_text" tools:text="@string/sample_status_text"
tools:visibility="visible" /> tools:visibility="visible"/>
<org.mariotaku.twidere.view.ForegroundColorView <org.mariotaku.twidere.view.ForegroundColorView
android:id="@+id/quote_indicator" android:id="@+id/quote_indicator"
@ -171,7 +170,7 @@
android:layout_marginRight="@dimen/element_spacing_normal" android:layout_marginRight="@dimen/element_spacing_normal"
android:background="?quoteIndicatorBackgroundColor" android:background="?quoteIndicatorBackgroundColor"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible"/>
<org.mariotaku.twidere.view.NameView <org.mariotaku.twidere.view.NameView
android:id="@+id/quoted_name" android:id="@+id/quoted_name"
@ -185,7 +184,7 @@
android:visibility="gone" android:visibility="gone"
app:nv_primaryTextColor="?android:textColorPrimary" app:nv_primaryTextColor="?android:textColorPrimary"
app:nv_secondaryTextColor="?android:textColorSecondary" app:nv_secondaryTextColor="?android:textColorSecondary"
tools:visibility="visible" /> tools:visibility="visible"/>
<org.mariotaku.twidere.view.TimelineContentTextView <org.mariotaku.twidere.view.TimelineContentTextView
android:id="@+id/quoted_text" android:id="@+id/quoted_text"
@ -200,7 +199,7 @@
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:visibility="gone" android:visibility="gone"
tools:text="@string/sample_status_text" tools:text="@string/sample_status_text"
tools:visibility="visible" /> tools:visibility="visible"/>
<org.mariotaku.twidere.view.CardMediaContainer <org.mariotaku.twidere.view.CardMediaContainer
android:id="@+id/media_preview" android:id="@+id/media_preview"
@ -248,7 +247,7 @@
android:paddingRight="@dimen/element_spacing_normal" android:paddingRight="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceSmall" android:textAppearance="?android:textAppearanceSmall"
app:iabActivatedColor="@color/highlight_reply" app:iabActivatedColor="@color/highlight_reply"
app:iabColor="?android:textColorTertiary" /> app:iabColor="?android:textColorTertiary"/>
<org.mariotaku.twidere.view.ActionIconThemedTextView <org.mariotaku.twidere.view.ActionIconThemedTextView
android:id="@+id/retweet_count" android:id="@+id/retweet_count"
@ -264,7 +263,7 @@
android:paddingRight="@dimen/element_spacing_normal" android:paddingRight="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceSmall" android:textAppearance="?android:textAppearanceSmall"
app:iabActivatedColor="@color/highlight_retweet" app:iabActivatedColor="@color/highlight_retweet"
app:iabColor="?android:textColorTertiary" /> app:iabColor="?android:textColorTertiary"/>
<org.mariotaku.twidere.view.ActionIconThemedTextView <org.mariotaku.twidere.view.ActionIconThemedTextView
android:id="@+id/favorite_count" android:id="@+id/favorite_count"
@ -280,7 +279,7 @@
android:paddingRight="@dimen/element_spacing_normal" android:paddingRight="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceSmall" android:textAppearance="?android:textAppearanceSmall"
app:iabActivatedColor="@color/highlight_like" app:iabActivatedColor="@color/highlight_like"
app:iabColor="?android:textColorTertiary" /> app:iabColor="?android:textColorTertiary"/>
</LinearLayout> </LinearLayout>
@ -296,7 +295,7 @@
android:color="?android:textColorTertiary" android:color="?android:textColorTertiary"
android:focusable="false" android:focusable="false"
android:src="@drawable/ic_action_more_horizontal" android:src="@drawable/ic_action_more_horizontal"
tools:visibility="visible" /> tools:visibility="visible"/>
</RelativeLayout> </RelativeLayout>
</org.mariotaku.twidere.view.ColorLabelFrameLayout> </org.mariotaku.twidere.view.ColorLabelFrameLayout>

View File

@ -17,10 +17,13 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>. ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<org.mariotaku.twidere.view.SquareFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <org.mariotaku.twidere.view.SquareFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="@dimen/element_spacing_small"> android:padding="@dimen/element_spacing_small"
tools:layout_width="48dp">
<org.mariotaku.twidere.view.ProfileImageView <org.mariotaku.twidere.view.ProfileImageView
android:id="@android:id/icon" android:id="@android:id/icon"
@ -29,6 +32,6 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center" android:layout_gravity="center"
android:contentDescription="@string/profile_image" android:contentDescription="@string/profile_image"
android:scaleType="centerCrop" /> android:scaleType="centerCrop"/>
</org.mariotaku.twidere.view.SquareFrameLayout> </org.mariotaku.twidere.view.SquareFrameLayout>