mirror of
https://github.com/nuclearfog/Shitter.git
synced 2025-01-30 19:05:02 +01:00
bug fix
This commit is contained in:
parent
31456a9331
commit
0282ed2376
@ -2,6 +2,7 @@ package org.nuclearfog.twidda.backend.api;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.nuclearfog.twidda.model.Relation;
|
import org.nuclearfog.twidda.model.Relation;
|
||||||
|
|
||||||
@ -20,13 +21,19 @@ class RelationV1 implements Relation {
|
|||||||
private boolean canDm;
|
private boolean canDm;
|
||||||
|
|
||||||
|
|
||||||
RelationV1(JSONObject json, long currentId) {
|
RelationV1(JSONObject json) throws JSONException {
|
||||||
isHome = json.optLong("target_id") == currentId;
|
JSONObject relationship = json.getJSONObject("relationship");
|
||||||
isFollowing = json.optBoolean("following");
|
JSONObject source = relationship.getJSONObject("source");
|
||||||
isFollower = json.optBoolean("followed_by");
|
JSONObject target = relationship.getJSONObject("target");
|
||||||
isBlocked = json.optBoolean("blocking");
|
|
||||||
isMuted = json.optBoolean("muting");
|
long sourceId = Long.parseLong(source.getString("id_str"));
|
||||||
canDm = json.optBoolean("can_dm");
|
long targetId = Long.parseLong(target.getString("id_str"));
|
||||||
|
isHome = sourceId == targetId;
|
||||||
|
isFollowing = source.optBoolean("following");
|
||||||
|
isFollower = source.optBoolean("followed_by");
|
||||||
|
isBlocked = source.optBoolean("blocking");
|
||||||
|
isMuted = source.optBoolean("muting");
|
||||||
|
canDm = source.optBoolean("can_dm");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,8 +64,8 @@ class TweetV1 implements Tweet {
|
|||||||
|
|
||||||
|
|
||||||
TweetV1(JSONObject json, long twitterId) throws JSONException {
|
TweetV1(JSONObject json, long twitterId) throws JSONException {
|
||||||
id = json.optLong("id");
|
author = new UserV1(json.getJSONObject("user"), twitterId);
|
||||||
text = json.optString("full_text");
|
id = Long.parseLong(json.optString("id_str", "-1"));
|
||||||
replyId = json.optLong("in_reply_to_status_id", -1);
|
replyId = json.optLong("in_reply_to_status_id", -1);
|
||||||
replyUserId = json.optLong("in_reply_to_status_id", -1);
|
replyUserId = json.optLong("in_reply_to_status_id", -1);
|
||||||
retweetCount = json.optInt("retweet_count");
|
retweetCount = json.optInt("retweet_count");
|
||||||
@ -73,23 +73,19 @@ class TweetV1 implements Tweet {
|
|||||||
isFavorited = json.optBoolean("favorited");
|
isFavorited = json.optBoolean("favorited");
|
||||||
isRetweeted = json.optBoolean("retweeted");
|
isRetweeted = json.optBoolean("retweeted");
|
||||||
isSensitive = json.optBoolean("possibly_sensitive");
|
isSensitive = json.optBoolean("possibly_sensitive");
|
||||||
timestamp = StringTools.getTime(json.optString("created_at"));
|
timestamp = StringTools.getTime1(json.optString("created_at"));
|
||||||
source = StringTools.getSource(json.optString("source"));
|
source = StringTools.getSource(json.optString("source"));
|
||||||
|
text = createText(json);
|
||||||
|
|
||||||
String replyName = json.optString("in_reply_to_screen_name");
|
String replyName = json.optString("in_reply_to_screen_name");
|
||||||
String userMentions = StringTools.getUserMentions(text);
|
String userMentions = StringTools.getUserMentions(text);
|
||||||
|
|
||||||
JSONObject locationJson = json.optJSONObject("place");
|
JSONObject locationJson = json.optJSONObject("place");
|
||||||
JSONObject coordinateJson = json.optJSONObject("coordinates");
|
JSONObject coordinateJson = json.optJSONObject("coordinates");
|
||||||
JSONObject user = json.getJSONObject("user");
|
|
||||||
JSONObject quoted_tweet = json.optJSONObject("retweeted_status");
|
JSONObject quoted_tweet = json.optJSONObject("retweeted_status");
|
||||||
JSONObject user_retweet = json.optJSONObject("current_user_retweet");
|
JSONObject user_retweet = json.optJSONObject("current_user_retweet");
|
||||||
JSONObject entities = json.optJSONObject("entities");
|
|
||||||
JSONObject extEntities = json.optJSONObject("extended_entities");
|
JSONObject extEntities = json.optJSONObject("extended_entities");
|
||||||
|
|
||||||
author = new UserV1(user, twitterId);
|
|
||||||
if (locationJson != null) {
|
|
||||||
location = locationJson.optString("full_name");
|
|
||||||
}
|
|
||||||
if (coordinateJson != null) {
|
if (coordinateJson != null) {
|
||||||
if (coordinateJson.optString("type").equals("Point")) {
|
if (coordinateJson.optString("type").equals("Point")) {
|
||||||
JSONArray coordinateArray = coordinateJson.optJSONArray("coordinates");
|
JSONArray coordinateArray = coordinateJson.optJSONArray("coordinates");
|
||||||
@ -100,6 +96,9 @@ class TweetV1 implements Tweet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (locationJson != null) {
|
||||||
|
location = locationJson.optString("full_name");
|
||||||
|
}
|
||||||
if (!replyName.equals("null")) {
|
if (!replyName.equals("null")) {
|
||||||
this.replyName = '@' + replyName;
|
this.replyName = '@' + replyName;
|
||||||
}
|
}
|
||||||
@ -115,8 +114,6 @@ class TweetV1 implements Tweet {
|
|||||||
isRetweeted = embeddedTweet.isRetweeted();
|
isRetweeted = embeddedTweet.isRetweeted();
|
||||||
isFavorited = embeddedTweet.isFavorited();
|
isFavorited = embeddedTweet.isFavorited();
|
||||||
}
|
}
|
||||||
if (entities != null)
|
|
||||||
addURLs(entities);
|
|
||||||
if (extEntities != null) {
|
if (extEntities != null) {
|
||||||
addMedia(extEntities);
|
addMedia(extEntities);
|
||||||
}
|
}
|
||||||
@ -338,15 +335,16 @@ class TweetV1 implements Tweet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* expand URLs int the tweet text
|
* read tweet and expand urls
|
||||||
*
|
|
||||||
* @param entities json object with tweet entities
|
|
||||||
*/
|
*/
|
||||||
private void addURLs(@NonNull JSONObject entities) {
|
private String createText(@NonNull JSONObject json) {
|
||||||
|
String text = json.optString("full_text");
|
||||||
|
StringBuilder builder = new StringBuilder(text);
|
||||||
|
|
||||||
|
// check for shortened urls and replace them with full urls
|
||||||
try {
|
try {
|
||||||
|
JSONObject entities = json.getJSONObject("entities");
|
||||||
JSONArray urls = entities.getJSONArray("urls");
|
JSONArray urls = entities.getJSONArray("urls");
|
||||||
// replace new line symbol with new line character
|
|
||||||
StringBuilder builder = new StringBuilder(text);
|
|
||||||
for (int i = urls.length() - 1; i >= 0; i--) {
|
for (int i = urls.length() - 1; i >= 0; i--) {
|
||||||
JSONObject entry = urls.getJSONObject(i);
|
JSONObject entry = urls.getJSONObject(i);
|
||||||
String link = entry.getString("expanded_url");
|
String link = entry.getString("expanded_url");
|
||||||
@ -356,9 +354,17 @@ class TweetV1 implements Tweet {
|
|||||||
int offset = StringTools.calculateIndexOffset(text, start);
|
int offset = StringTools.calculateIndexOffset(text, start);
|
||||||
builder.replace(start + offset, end + offset, link);
|
builder.replace(start + offset, end + offset, link);
|
||||||
}
|
}
|
||||||
this.text = builder.toString();
|
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
// use default description
|
// use default tweet text
|
||||||
|
builder = new StringBuilder(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// replace "&" string
|
||||||
|
int index = builder.lastIndexOf("&");
|
||||||
|
while (index >= 0) {
|
||||||
|
builder.replace(index, index + 5, "&");
|
||||||
|
index = builder.lastIndexOf("&");
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -338,9 +338,7 @@ public class Twitter {
|
|||||||
if (response.body() != null) {
|
if (response.body() != null) {
|
||||||
JSONObject json = new JSONObject(response.body().string());
|
JSONObject json = new JSONObject(response.body().string());
|
||||||
if (response.code() == 200) {
|
if (response.code() == 200) {
|
||||||
JSONObject source = json.getJSONObject("relationship").getJSONObject("source");
|
return new RelationV1(json);
|
||||||
long currentId = settings.getCurrentUserId();
|
|
||||||
return new RelationV1(source, currentId);
|
|
||||||
}
|
}
|
||||||
throw new TwitterException(json);
|
throw new TwitterException(json);
|
||||||
}
|
}
|
||||||
|
@ -31,14 +31,14 @@ class UserListV1 implements UserList {
|
|||||||
|
|
||||||
|
|
||||||
UserListV1(JSONObject json, long currentId) throws JSONException {
|
UserListV1(JSONObject json, long currentId) throws JSONException {
|
||||||
id = json.optLong("id");
|
id = Long.parseLong(json.optString("id_str", "-1"));
|
||||||
title = json.optString("name");
|
title = json.optString("name");
|
||||||
description = json.optString("description");
|
description = json.optString("description");
|
||||||
memberCount = json.optInt("member_count");
|
memberCount = json.optInt("member_count");
|
||||||
subscriberCount = json.optInt("subscriber_count");
|
subscriberCount = json.optInt("subscriber_count");
|
||||||
isPrivate = json.optString("mode").equals("private");
|
isPrivate = json.optString("mode").equals("private");
|
||||||
following = json.optBoolean("following");
|
following = json.optBoolean("following");
|
||||||
time = StringTools.getTime(json.optString("created_at"));
|
time = StringTools.getTime1(json.optString("created_at"));
|
||||||
owner = new UserV1(json.getJSONObject("user"));
|
owner = new UserV1(json.getJSONObject("user"));
|
||||||
isOwner = currentId == owner.getId();
|
isOwner = currentId == owner.getId();
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class UserV1 implements User {
|
|||||||
private String screenName;
|
private String screenName;
|
||||||
private String description;
|
private String description;
|
||||||
private String location;
|
private String location;
|
||||||
private String profileUrl;
|
private String url;
|
||||||
private String profileImageUrl;
|
private String profileImageUrl;
|
||||||
private String profileBannerUrl;
|
private String profileBannerUrl;
|
||||||
private int following;
|
private int following;
|
||||||
@ -45,50 +45,23 @@ class UserV1 implements User {
|
|||||||
|
|
||||||
|
|
||||||
UserV1(JSONObject json) {
|
UserV1(JSONObject json) {
|
||||||
String profileImage = json.optString("profile_image_url_https");
|
id = Long.parseLong(json.optString("id_str", "-1"));
|
||||||
profileBannerUrl = json.optString("profile_banner_url");
|
|
||||||
description = json.optString("description");
|
|
||||||
username = json.optString("name");
|
username = json.optString("name");
|
||||||
screenName = '@' + json.optString("screen_name");
|
screenName = '@' + json.optString("screen_name");
|
||||||
location = json.optString("location");
|
|
||||||
id = json.optLong("id");
|
|
||||||
isVerified = json.optBoolean("verified");
|
isVerified = json.optBoolean("verified");
|
||||||
isLocked = json.optBoolean("protected");
|
isLocked = json.optBoolean("protected");
|
||||||
|
profileImageUrl = getProfileImage(json);
|
||||||
|
profileBannerUrl = json.optString("profile_banner_url");
|
||||||
|
description = getDescription(json);
|
||||||
|
location = json.optString("location");
|
||||||
following = json.optInt("friends_count");
|
following = json.optInt("friends_count");
|
||||||
follower = json.optInt("followers_count");
|
follower = json.optInt("followers_count");
|
||||||
tweetCount = json.optInt("statuses_count");
|
tweetCount = json.optInt("statuses_count");
|
||||||
favorCount = json.optInt("favourites_count");
|
favorCount = json.optInt("favourites_count");
|
||||||
followReqSent = json.optBoolean("follow_request_sent");
|
followReqSent = json.optBoolean("follow_request_sent");
|
||||||
defaultImage = json.optBoolean("default_profile_image");
|
defaultImage = json.optBoolean("default_profile_image");
|
||||||
profileUrl = json.optString("profile_image_url_https");
|
created = StringTools.getTime1(json.optString("created_at"));
|
||||||
created = StringTools.getTime(json.optString("created_at"));
|
url = getUrl(json);
|
||||||
|
|
||||||
// expand URLs
|
|
||||||
JSONObject entities = json.optJSONObject("entities");
|
|
||||||
if (entities != null) {
|
|
||||||
JSONObject url = entities.optJSONObject("url");
|
|
||||||
if (url != null) {
|
|
||||||
JSONArray urls = url.optJSONArray("urls");
|
|
||||||
if (urls != null && urls.length() > 0) {
|
|
||||||
profileUrl = urls.optJSONObject(0).optString("display_url");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JSONObject descrEntities = entities.optJSONObject("description");
|
|
||||||
if (descrEntities != null) {
|
|
||||||
JSONArray urls = descrEntities.optJSONArray("urls");
|
|
||||||
if (urls != null) {
|
|
||||||
expandDescriptionUrls(urls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// set profile image url
|
|
||||||
int start = profileImage.lastIndexOf('_');
|
|
||||||
int end = profileImage.lastIndexOf('.');
|
|
||||||
if (!defaultImage && start > 0 && end > 0) {
|
|
||||||
profileImageUrl = profileImage.substring(0, start) + profileImage.substring(end);
|
|
||||||
} else {
|
|
||||||
profileImageUrl = profileImage;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -133,7 +106,7 @@ class UserV1 implements User {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getProfileUrl() {
|
public String getProfileUrl() {
|
||||||
return profileUrl;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -195,13 +168,19 @@ class UserV1 implements User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* expand URLs in the user description
|
* expand URLs of the user description
|
||||||
*
|
*
|
||||||
* @param urls json object with url information
|
* @param json root json object of user v1
|
||||||
|
* @return user description
|
||||||
*/
|
*/
|
||||||
private void expandDescriptionUrls(@NonNull JSONArray urls) {
|
private String getDescription(JSONObject json) {
|
||||||
try {
|
try {
|
||||||
// replace new line symbol with new line character
|
JSONObject entities = json.getJSONObject("entities");
|
||||||
|
String description = json.getString("description");
|
||||||
|
JSONObject descrEntities = entities.getJSONObject("description");
|
||||||
|
JSONArray urls = descrEntities.getJSONArray("urls");
|
||||||
|
|
||||||
|
// expand shortened urls
|
||||||
StringBuilder builder = new StringBuilder(description);
|
StringBuilder builder = new StringBuilder(description);
|
||||||
for (int i = urls.length() - 1; i >= 0; i--) {
|
for (int i = urls.length() - 1; i >= 0; i--) {
|
||||||
JSONObject entry = urls.getJSONObject(i);
|
JSONObject entry = urls.getJSONObject(i);
|
||||||
@ -212,9 +191,45 @@ class UserV1 implements User {
|
|||||||
int offset = StringTools.calculateIndexOffset(description, start);
|
int offset = StringTools.calculateIndexOffset(description, start);
|
||||||
builder.replace(start + offset, end + offset, link);
|
builder.replace(start + offset, end + offset, link);
|
||||||
}
|
}
|
||||||
this.description = builder.toString();
|
return builder.toString();
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
// use default description
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get expanded profile url
|
||||||
|
*
|
||||||
|
* @param json root json object of user v1
|
||||||
|
* @return expanded url
|
||||||
|
*/
|
||||||
|
private String getUrl(JSONObject json) {
|
||||||
|
try {
|
||||||
|
JSONObject entities = json.getJSONObject("entities");
|
||||||
|
JSONObject urlJson = entities.getJSONObject("url");
|
||||||
|
JSONArray urls = urlJson.getJSONArray("urls");
|
||||||
|
if ( urls.length() > 0) {
|
||||||
|
return urls.getJSONObject(0).getString("display_url");
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get original sized profile image url
|
||||||
|
*
|
||||||
|
* @param json root json object of user v1
|
||||||
|
* @return profile image url
|
||||||
|
*/
|
||||||
|
private String getProfileImage(JSONObject json) {
|
||||||
|
String profileImage = json.optString("profile_image_url_https");
|
||||||
|
// set profile image url
|
||||||
|
int start = profileImage.lastIndexOf('_');
|
||||||
|
int end = profileImage.lastIndexOf('.');
|
||||||
|
if (!defaultImage && start > 0 && end > 0)
|
||||||
|
return profileImage.substring(0, start) + profileImage.substring(end);
|
||||||
|
return profileImage;
|
||||||
|
}
|
||||||
}
|
}
|
@ -3,13 +3,12 @@ package org.nuclearfog.twidda.backend.api;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
import org.nuclearfog.twidda.backend.utils.StringTools;
|
||||||
import org.nuclearfog.twidda.model.User;
|
import org.nuclearfog.twidda.model.User;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* implementation of User accessed by API 2.0
|
* implementation of User accessed by API 2.0
|
||||||
*
|
*
|
||||||
@ -22,13 +21,8 @@ class UserV2 implements User {
|
|||||||
/**
|
/**
|
||||||
* extra parameters required to fetch additional data
|
* extra parameters required to fetch additional data
|
||||||
*/
|
*/
|
||||||
public static final String PARAMS = "user.fields=profile_image_url%2Cpublic_metrics%2Cverified%2Cprotected";
|
public static final String PARAMS = "user.fields=profile_image_url%2Cpublic_metrics%2Cverified" +
|
||||||
|
"%2Cprotected%2Cdescription%2Ccreated_at%2Curl%2Centities";
|
||||||
/**
|
|
||||||
* date time formatter for ISO 8601
|
|
||||||
*/
|
|
||||||
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
|
|
||||||
|
|
||||||
|
|
||||||
private long id;
|
private long id;
|
||||||
private long created;
|
private long created;
|
||||||
@ -36,7 +30,7 @@ class UserV2 implements User {
|
|||||||
private String screenName;
|
private String screenName;
|
||||||
private String description;
|
private String description;
|
||||||
private String location;
|
private String location;
|
||||||
private String profileUrl;
|
private String url;
|
||||||
private String profileImageUrl;
|
private String profileImageUrl;
|
||||||
private String profileBannerUrl;
|
private String profileBannerUrl;
|
||||||
private int following;
|
private int following;
|
||||||
@ -51,16 +45,18 @@ class UserV2 implements User {
|
|||||||
|
|
||||||
|
|
||||||
UserV2(JSONObject json, long twitterId) {
|
UserV2(JSONObject json, long twitterId) {
|
||||||
id = json.optLong("id");
|
id = Long.parseLong(json.optString("id", "-1"));
|
||||||
username = json.optString("name");
|
username = json.optString("name");
|
||||||
screenName = '@' + json.optString("username"); // username -> screenname
|
screenName = '@' + json.optString("username");
|
||||||
isProtected = json.optBoolean("protected");
|
isProtected = json.optBoolean("protected");
|
||||||
location = json.optString("location");
|
location = json.optString("location");
|
||||||
profileUrl = json.optString("url");
|
|
||||||
description = json.optString("description");
|
|
||||||
isVerified = json.optBoolean("verified");
|
isVerified = json.optBoolean("verified");
|
||||||
profileImageUrl = json.optString("profile_image_url");
|
profileImageUrl = json.optString("profile_image_url");
|
||||||
profileBannerUrl = json.optString("profile_banner_url");
|
profileBannerUrl = json.optString("profile_banner_url");
|
||||||
|
created = StringTools.getTime2(json.optString("created_at"));
|
||||||
|
description = getDescription(json);
|
||||||
|
url = getUrl(json);
|
||||||
|
isCurrentUser = id == twitterId;
|
||||||
|
|
||||||
JSONObject metrics = json.optJSONObject("public_metrics");
|
JSONObject metrics = json.optJSONObject("public_metrics");
|
||||||
if (metrics != null) {
|
if (metrics != null) {
|
||||||
@ -68,9 +64,8 @@ class UserV2 implements User {
|
|||||||
follower = metrics.optInt("followers_count");
|
follower = metrics.optInt("followers_count");
|
||||||
tweetCount = metrics.optInt("tweet_count");
|
tweetCount = metrics.optInt("tweet_count");
|
||||||
}
|
}
|
||||||
isCurrentUser = id == twitterId;
|
|
||||||
setDate(json.optString("created_at"));
|
|
||||||
|
|
||||||
|
// not yet implemented in API 2.0
|
||||||
favorCount = 0;
|
favorCount = 0;
|
||||||
followReqSent = false;
|
followReqSent = false;
|
||||||
defaultImage = false;
|
defaultImage = false;
|
||||||
@ -118,7 +113,7 @@ class UserV2 implements User {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getProfileUrl() {
|
public String getProfileUrl() {
|
||||||
return profileUrl;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -180,17 +175,57 @@ class UserV2 implements User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set time of account creation
|
* expand URLs of the user description
|
||||||
*
|
*
|
||||||
* @param dateStr date string from twitter
|
* @param json root json object of user v1
|
||||||
|
* @return user description
|
||||||
*/
|
*/
|
||||||
private void setDate(String dateStr) {
|
private String getDescription(JSONObject json) {
|
||||||
try {
|
String description = json.optString("description");
|
||||||
Date date = sdf.parse(dateStr);
|
JSONObject entities = json.optJSONObject("entities");
|
||||||
if (date != null)
|
if (entities != null) {
|
||||||
created = date.getTime();
|
try {
|
||||||
} catch (Exception e) {
|
JSONObject descrEntities = entities.getJSONObject("description");
|
||||||
e.printStackTrace();
|
JSONArray urls = descrEntities.getJSONArray("urls");
|
||||||
|
// expand shortened urls
|
||||||
|
StringBuilder builder = new StringBuilder(description);
|
||||||
|
for (int i = urls.length() - 1; i >= 0; i--) {
|
||||||
|
JSONObject entry = urls.getJSONObject(i);
|
||||||
|
String link = entry.getString("expanded_url");
|
||||||
|
int start = entry.getInt("start");
|
||||||
|
int end = entry.getInt("end");
|
||||||
|
int offset = StringTools.calculateIndexOffset(description, start);
|
||||||
|
builder.replace(start + offset, end + offset, link);
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// ignore, use default description
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get expanded profile url
|
||||||
|
*
|
||||||
|
* @param json root json object of user v1
|
||||||
|
* @return expanded url
|
||||||
|
*/
|
||||||
|
private String getUrl(JSONObject json) {
|
||||||
|
JSONObject entities = json.optJSONObject("entities");
|
||||||
|
if (entities != null) {
|
||||||
|
JSONObject urlJson = entities.optJSONObject("url");
|
||||||
|
if (urlJson != null) {
|
||||||
|
try {
|
||||||
|
JSONArray urls = urlJson.getJSONArray("urls");
|
||||||
|
if (urls.length() > 0) {
|
||||||
|
return urls.getJSONObject(0).getString("display_url");
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -48,7 +48,7 @@ public class UserLists extends LinkedList<UserList> {
|
|||||||
* @return true if list is linked
|
* @return true if list is linked
|
||||||
*/
|
*/
|
||||||
public boolean hasPrevious() {
|
public boolean hasPrevious() {
|
||||||
return prevCursor > 0;
|
return prevCursor != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,7 +57,7 @@ public class UserLists extends LinkedList<UserList> {
|
|||||||
* @return true if list has a successor
|
* @return true if list has a successor
|
||||||
*/
|
*/
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return nextCursor > 0;
|
return nextCursor != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,7 +64,7 @@ public class Users extends LinkedList<User> {
|
|||||||
* @return true if list is linked
|
* @return true if list is linked
|
||||||
*/
|
*/
|
||||||
public boolean hasPrevious() {
|
public boolean hasPrevious() {
|
||||||
return prevCursor > 0;
|
return prevCursor != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,7 +73,7 @@ public class Users extends LinkedList<User> {
|
|||||||
* @return true if list has a successor
|
* @return true if list has a successor
|
||||||
*/
|
*/
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return nextCursor > 0;
|
return nextCursor != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,10 +25,31 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
*/
|
*/
|
||||||
public final class StringTools {
|
public final class StringTools {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* regex pattern used to get user mentions
|
||||||
|
*/
|
||||||
private static final Pattern MENTION = Pattern.compile("[@][\\w_]+");
|
private static final Pattern MENTION = Pattern.compile("[@][\\w_]+");
|
||||||
private static final SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.US);
|
|
||||||
|
/**
|
||||||
|
* date format used by API 1.1
|
||||||
|
* e.g. "Mon Jan 17 13:00:12 +0000 2022"
|
||||||
|
*/
|
||||||
|
private static final SimpleDateFormat dateFormat1 = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.US);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* date format used by API 2.0
|
||||||
|
* e.g. "2008-08-15T13:51:34.000Z"
|
||||||
|
*/
|
||||||
|
private static final SimpleDateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fallback date if parsing failed
|
||||||
|
*/
|
||||||
private static final long DEFAULT_TIME = 0x61D99F64;
|
private static final long DEFAULT_TIME = 0x61D99F64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* random generator used to generate random strings
|
||||||
|
*/
|
||||||
private static Random rand = new Random();
|
private static Random rand = new Random();
|
||||||
|
|
||||||
private StringTools() {
|
private StringTools() {
|
||||||
@ -116,26 +137,40 @@ public final class StringTools {
|
|||||||
*/
|
*/
|
||||||
public static int countMentions(String text) {
|
public static int countMentions(String text) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
for (int i = 0; i < text.length() - 1; i++) {
|
Matcher m = MENTION.matcher(text);
|
||||||
if (text.charAt(i) == '@') {
|
while (m.find()) {
|
||||||
char next = text.charAt(i + 1);
|
result++;
|
||||||
if ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') || (next >= '0' && next <= '9') || next == '_') {
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert Twitter ISO 8601 date time to long format
|
* convert Twitter API 1.1 date time to long format
|
||||||
*
|
*
|
||||||
* @param timeStr Twitter time string
|
* @param timeStr Twitter time string
|
||||||
* @return date time
|
* @return date time
|
||||||
*/
|
*/
|
||||||
public static long getTime(String timeStr) {
|
public static long getTime1(String timeStr) {
|
||||||
try {
|
try {
|
||||||
Date date = sdf.parse(timeStr);
|
Date date = dateFormat1.parse(timeStr);
|
||||||
|
if (date != null)
|
||||||
|
return date.getTime();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// make date invalid so it will be not shown
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return DEFAULT_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert Twitter API 2 date time to long format
|
||||||
|
*
|
||||||
|
* @param timeStr Twitter time string
|
||||||
|
* @return date time
|
||||||
|
*/
|
||||||
|
public static long getTime2(String timeStr) {
|
||||||
|
try {
|
||||||
|
Date date = dateFormat2.parse(timeStr);
|
||||||
if (date != null)
|
if (date != null)
|
||||||
return date.getTime();
|
return date.getTime();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -168,7 +203,7 @@ public final class StringTools {
|
|||||||
*/
|
*/
|
||||||
public static int calculateIndexOffset(String text, int limit) {
|
public static int calculateIndexOffset(String text, int limit) {
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (int c = 0; c < limit - 1 && c < text.length(); c++) {
|
for (int c = 0; c < limit - 1 && c < text.length() - 1; c++) {
|
||||||
// determine if a pair of chars represent an emoji
|
// determine if a pair of chars represent an emoji
|
||||||
if (Character.isSurrogatePair(text.charAt(c), text.charAt(c + 1))) {
|
if (Character.isSurrogatePair(text.charAt(c), text.charAt(c + 1))) {
|
||||||
offset++;
|
offset++;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user