using https to load media if possible

This commit is contained in:
Mariotaku Lee 2015-07-02 11:56:21 +08:00
parent 9c657fc13c
commit 127d338386
25 changed files with 2573 additions and 285 deletions

View File

@ -28,15 +28,14 @@ import android.support.annotation.NonNull;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.api.twitter.model.DirectMessage;
import org.mariotaku.twidere.api.twitter.model.User;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
import org.mariotaku.twidere.util.TwitterContentUtils;
import java.util.Comparator;
import java.util.Date;
import org.mariotaku.twidere.api.twitter.model.DirectMessage;
import org.mariotaku.twidere.api.twitter.model.User;
import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
import static org.mariotaku.twidere.util.content.ContentValuesUtils.getAsBoolean;
import static org.mariotaku.twidere.util.content.ContentValuesUtils.getAsLong;
@ -153,18 +152,19 @@ public class ParcelableDirectMessage implements Parcelable, Comparable<Parcelabl
this.account_id = account_id;
this.is_outgoing = is_outgoing;
final User sender = message.getSender(), recipient = message.getRecipient();
final String sender_profile_image_url = sender != null ? sender.getProfileImageUrlHttps() : null;
final String recipient_profile_image_url = recipient != null ? recipient.getProfileImageUrlHttps() : null;
assert sender != null && recipient != null;
final String sender_profile_image_url = TwitterContentUtils.getProfileImageUrl(sender);
final String recipient_profile_image_url = TwitterContentUtils.getProfileImageUrl(recipient);
id = message.getId();
timestamp = getTime(message.getCreatedAt());
sender_id = sender != null ? sender.getId() : -1;
recipient_id = recipient != null ? recipient.getId() : -1;
sender_id = sender.getId();
recipient_id = recipient.getId();
text_html = TwitterContentUtils.formatDirectMessageText(message);
text_plain = message.getText();
sender_name = sender != null ? sender.getName() : null;
recipient_name = recipient != null ? recipient.getName() : null;
sender_screen_name = sender != null ? sender.getScreenName() : null;
recipient_screen_name = recipient != null ? recipient.getScreenName() : null;
sender_name = sender.getName();
recipient_name = recipient.getName();
sender_screen_name = sender.getScreenName();
recipient_screen_name = recipient.getScreenName();
this.sender_profile_image_url = sender_profile_image_url;
this.recipient_profile_image_url = recipient_profile_image_url;
text_unescaped = toPlainText(text_html);

View File

@ -11,17 +11,6 @@ import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.util.MediaPreviewUtils;
import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.TwidereArrayUtils;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.mariotaku.twidere.api.twitter.model.CardEntity;
import org.mariotaku.twidere.api.twitter.model.CardEntity.BindingValue;
import org.mariotaku.twidere.api.twitter.model.CardEntity.ImageValue;
@ -33,6 +22,17 @@ import org.mariotaku.twidere.api.twitter.model.MediaEntity.Size;
import org.mariotaku.twidere.api.twitter.model.MediaEntity.Type;
import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.model.UrlEntity;
import org.mariotaku.twidere.util.MediaPreviewUtils;
import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.TwidereArrayUtils;
import org.mariotaku.twidere.util.TwitterContentUtils;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@SuppressWarnings("unused")
@JsonObject
@ -87,9 +87,9 @@ public class ParcelableMedia implements Parcelable {
}
public ParcelableMedia(final MediaEntity entity) {
page_url = entity.getMediaUrl();
media_url = entity.getMediaUrl();
preview_url = entity.getMediaUrl();
page_url = TwitterContentUtils.getMediaUrl(entity);
media_url = TwitterContentUtils.getMediaUrl(entity);
preview_url = TwitterContentUtils.getMediaUrl(entity);
start = entity.getStart();
end = entity.getEnd();
type = getTypeInt(entity.getType());
@ -190,7 +190,7 @@ public class ParcelableMedia implements Parcelable {
}
if (mediaEntities != null) {
for (final MediaEntity media : mediaEntities) {
final String mediaURL = media.getMediaUrl();
final String mediaURL = TwitterContentUtils.getMediaUrl(media);
if (mediaURL != null) {
list.add(new ParcelableMedia(media));
}

View File

@ -397,7 +397,7 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
retweeted_by_user_id = retweet_user != null ? retweet_user.getId() : -1;
retweeted_by_user_name = retweet_user != null ? retweet_user.getName() : null;
retweeted_by_user_screen_name = retweet_user != null ? retweet_user.getScreenName() : null;
retweeted_by_user_profile_image = retweet_user != null ? retweet_user.getProfileImageUrlHttps() : null;
retweeted_by_user_profile_image = TwitterContentUtils.getProfileImageUrl(retweet_user);
final Status quoted = orig.getQuotedStatus();
final User quote_user = quoted != null ? orig.getUser() : null;
@ -415,7 +415,7 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
quoted_by_user_id = quote_user != null ? quote_user.getId() : -1;
quoted_by_user_name = quote_user != null ? quote_user.getName() : null;
quoted_by_user_screen_name = quote_user != null ? quote_user.getScreenName() : null;
quoted_by_user_profile_image = quote_user != null ? quote_user.getProfileImageUrlHttps() : null;
quoted_by_user_profile_image = TwitterContentUtils.getProfileImageUrl(quote_user);
quoted_by_user_is_protected = quote_user != null && quote_user.isProtected();
quoted_by_user_is_verified = quote_user != null && quote_user.isVerified();
@ -431,7 +431,7 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
user_id = user.getId();
user_name = user.getName();
user_screen_name = user.getScreenName();
user_profile_image_url = user.getProfileImageUrlHttps();
user_profile_image_url = TwitterContentUtils.getProfileImageUrl(user);
user_is_protected = user.isProtected();
user_is_verified = user.isVerified();
user_is_following = user.isFollowing();

View File

@ -29,15 +29,14 @@ import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.api.twitter.model.UrlEntity;
import org.mariotaku.twidere.api.twitter.model.User;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries;
import org.mariotaku.twidere.util.HtmlEscapeHelper;
import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.TwitterContentUtils;
import org.mariotaku.twidere.api.twitter.model.UrlEntity;
import org.mariotaku.twidere.api.twitter.model.User;
@JsonObject
public class ParcelableUser implements Parcelable, Comparable<ParcelableUser> {
@ -243,7 +242,7 @@ public class ParcelableUser implements Parcelable, Comparable<ParcelableUser> {
description_expanded = TwitterContentUtils.formatExpandedUserDescription(user);
description_unescaped = HtmlEscapeHelper.toPlainText(description_html);
location = user.getLocation();
profile_image_url = user.getProfileImageUrlHttps();
profile_image_url = TwitterContentUtils.getProfileImageUrl(user);
profile_banner_url = user.getProfileBannerImageUrl();
url = user.getUrl();
url_expanded = url != null && urls_url_entities != null && urls_url_entities.length > 0 ? urls_url_entities[0].getExpandedUrl() : null;

View File

@ -28,6 +28,7 @@ import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.api.twitter.model.User;
import org.mariotaku.twidere.api.twitter.model.UserList;
import org.mariotaku.twidere.util.TwitterContentUtils;
@JsonObject
public class ParcelableUserList implements Parcelable, Comparable<ParcelableUserList> {
@ -114,7 +115,7 @@ public class ParcelableUserList implements Parcelable, Comparable<ParcelableUser
user_id = user.getId();
user_name = user.getName();
user_screen_name = user.getScreenName();
user_profile_image_url = user.getProfileImageUrlHttps();
user_profile_image_url = TwitterContentUtils.getProfileImageUrl(user);
members_count = list.getMemberCount();
subscribers_count = list.getSubscriberCount();
}

View File

@ -81,7 +81,7 @@ public final class ContentValuesCreator implements TwidereConstants {
values.put(Accounts.ACCOUNT_ID, user.getId());
values.put(Accounts.SCREEN_NAME, user.getScreenName());
values.put(Accounts.NAME, user.getName());
values.put(Accounts.PROFILE_IMAGE_URL, user.getProfileImageUrlHttps());
values.put(Accounts.PROFILE_IMAGE_URL, TwitterContentUtils.getProfileImageUrl(user));
values.put(Accounts.PROFILE_BANNER_URL, user.getProfileBannerImageUrl());
values.put(Accounts.COLOR, color);
values.put(Accounts.IS_ACTIVATED, 1);
@ -105,7 +105,7 @@ public final class ContentValuesCreator implements TwidereConstants {
values.put(Accounts.ACCOUNT_ID, user.getId());
values.put(Accounts.SCREEN_NAME, user.getScreenName());
values.put(Accounts.NAME, user.getName());
values.put(Accounts.PROFILE_IMAGE_URL, user.getProfileImageUrlHttps());
values.put(Accounts.PROFILE_IMAGE_URL, TwitterContentUtils.getProfileImageUrl(user));
values.put(Accounts.PROFILE_BANNER_URL, user.getProfileBannerImageUrl());
values.put(Accounts.COLOR, color);
values.put(Accounts.IS_ACTIVATED, 1);
@ -123,8 +123,8 @@ public final class ContentValuesCreator implements TwidereConstants {
values.put(Accounts.ACCOUNT_ID, user.getId());
values.put(Accounts.SCREEN_NAME, user.getScreenName());
values.put(Accounts.NAME, user.getName());
values.put(Accounts.PROFILE_IMAGE_URL, (user.getProfileImageUrlHttps()));
values.put(Accounts.PROFILE_BANNER_URL, (user.getProfileBannerImageUrl()));
values.put(Accounts.PROFILE_IMAGE_URL, TwitterContentUtils.getProfileImageUrl(user));
values.put(Accounts.PROFILE_BANNER_URL, user.getProfileBannerImageUrl());
values.put(Accounts.COLOR, color);
values.put(Accounts.IS_ACTIVATED, 1);
values.put(Accounts.API_URL_FORMAT, apiUrlFormat);
@ -147,7 +147,7 @@ public final class ContentValuesCreator implements TwidereConstants {
public static ContentValues createCachedUser(final User user) {
if (user == null || user.getId() <= 0) return null;
final String profile_image_url = user.getProfileImageUrlHttps();
final String profile_image_url = TwitterContentUtils.getProfileImageUrl(user);
final String url = user.getUrl();
final UrlEntity[] urls = user.getUrlEntities();
final ContentValues values = new ContentValues();
@ -186,8 +186,8 @@ public final class ContentValuesCreator implements TwidereConstants {
final ContentValues values = new ContentValues();
final User sender = message.getSender(), recipient = message.getRecipient();
if (sender == null || recipient == null) return null;
final String sender_profile_image_url = sender.getProfileImageUrlHttps();
final String recipient_profile_image_url = recipient.getProfileImageUrlHttps();
final String sender_profile_image_url = TwitterContentUtils.getProfileImageUrl(sender);
final String recipient_profile_image_url = TwitterContentUtils.getProfileImageUrl(recipient);
values.put(DirectMessages.ACCOUNT_ID, accountId);
values.put(DirectMessages.MESSAGE_ID, message.getId());
values.put(DirectMessages.MESSAGE_TIMESTAMP, message.getCreatedAt().getTime());
@ -332,7 +332,7 @@ public final class ContentValuesCreator implements TwidereConstants {
values.put(Statuses.RETWEETED_BY_USER_ID, retweetedById);
values.put(Statuses.RETWEETED_BY_USER_NAME, retweetUser.getName());
values.put(Statuses.RETWEETED_BY_USER_SCREEN_NAME, retweetUser.getScreenName());
values.put(Statuses.RETWEETED_BY_USER_PROFILE_IMAGE, (retweetUser.getProfileImageUrlHttps()));
values.put(Statuses.RETWEETED_BY_USER_PROFILE_IMAGE, TwitterContentUtils.getProfileImageUrl(retweetUser));
values.put(Statuses.IS_RETWEET, true);
if (retweetedById == accountId) {
values.put(Statuses.MY_RETWEET_ID, orig.getId());
@ -364,7 +364,7 @@ public final class ContentValuesCreator implements TwidereConstants {
values.put(Statuses.QUOTED_BY_USER_ID, quotedById);
values.put(Statuses.QUOTED_BY_USER_NAME, quoteUser.getName());
values.put(Statuses.QUOTED_BY_USER_SCREEN_NAME, quoteUser.getScreenName());
values.put(Statuses.QUOTED_BY_USER_PROFILE_IMAGE, quoteUser.getProfileImageUrlHttps());
values.put(Statuses.QUOTED_BY_USER_PROFILE_IMAGE, TwitterContentUtils.getProfileImageUrl(quoteUser));
values.put(Statuses.QUOTED_BY_USER_IS_VERIFIED, quoteUser.isVerified());
values.put(Statuses.QUOTED_BY_USER_IS_PROTECTED, quoteUser.isProtected());
values.put(Statuses.IS_QUOTE, true);
@ -386,7 +386,7 @@ public final class ContentValuesCreator implements TwidereConstants {
}
final User user = status.getUser();
final long userId = user.getId();
final String profileImageUrl = (user.getProfileImageUrlHttps());
final String profileImageUrl = TwitterContentUtils.getProfileImageUrl(user);
final String name = user.getName(), screenName = user.getScreenName();
values.put(Statuses.USER_ID, userId);
values.put(Statuses.USER_NAME, name);

View File

@ -247,7 +247,7 @@ public class MediaPreviewUtils {
final MediaEntity[] mediaEntities = status.getMediaEntities();
if (mediaEntities != null) {
for (final MediaEntity mediaEntity : mediaEntities) {
final String expanded = mediaEntity.getMediaUrlHttps();
final String expanded = TwitterContentUtils.getMediaUrl(mediaEntity);
if (getSupportedLink(expanded) != null) return expanded;
}
}

View File

@ -21,6 +21,8 @@ package org.mariotaku.twidere.util;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException;
@ -206,13 +208,22 @@ public class TwitterContentUtils {
return list;
}
public static String getMediaUrl(MediaEntity entity) {
return TextUtils.isEmpty(entity.getMediaUrlHttps()) ? entity.getMediaUrl() : entity.getMediaUrlHttps();
}
public static String getProfileImageUrl(@Nullable User user) {
if (user == null) return null;
return TextUtils.isEmpty(user.getProfileImageUrlHttps()) ? user.getProfileImageUrl() : user.getProfileImageUrlHttps();
}
private static void parseEntities(final HtmlBuilder builder, final EntitySupport entities) {
// Format media.
final MediaEntity[] mediaEntities = entities.getMediaEntities();
if (mediaEntities != null) {
for (final MediaEntity mediaEntity : mediaEntities) {
final int start = mediaEntity.getStart(), end = mediaEntity.getEnd();
final String mediaUrl = mediaEntity.getMediaUrl();
final String mediaUrl = TwitterContentUtils.getMediaUrl(mediaEntity);
if (mediaUrl != null && start >= 0 && end >= 0) {
builder.addLink(mediaUrl, mediaEntity.getDisplayUrl(), start, end);
}

View File

@ -86,7 +86,7 @@ dependencies {
compile 'com.bluelinelabs:logansquare:1.1.0'
compile 'ch.acra:acra:4.6.2'
compile 'org.jraf:android-switch-backport:2.0.1'
compile 'com.fasterxml.jackson.core:jackson-databind:2.4.4'
compile 'com.fasterxml.jackson.jr:jackson-jr-objects:2.3.0'
compile 'com.makeramen:roundedimageview:2.1.0'
compile 'com.soundcloud.android:android-crop:1.0.0@aar'
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.1'

View File

@ -0,0 +1,109 @@
package com.fasterxml.jackson.simple.tree;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.TreeNode;
import java.util.Iterator;
abstract class JsonAbstractValue implements TreeNode
{
@Override
public JsonParser.NumberType numberType() {
return null;
}
@Override
public int size() {
return 0;
}
@Override
public boolean isValueNode() {
return false;
}
@Override
public boolean isContainerNode() {
return false;
}
@Override
public boolean isMissingNode() {
return false;
}
@Override
public boolean isArray() {
return false;
}
@Override
public boolean isObject() {
return false;
}
@Override
public TreeNode get(String s) {
return null;
}
@Override
public TreeNode get(int i) {
return null;
}
@Override
public TreeNode path(String s) {
return MISSING;
}
@Override
public TreeNode path(int i) {
return MISSING;
}
@Override
public Iterator<String> fieldNames() {
return null;
}
@Override
public TreeNode at(JsonPointer jsonPointer) {
return null;
}
@Override
public TreeNode at(String s) throws IllegalArgumentException {
return null;
}
@Override
public JsonParser traverse() {
return null;
}
@Override
public JsonParser traverse(ObjectCodec objectCodec) {
return null;
}
private static class JsonMissing extends JsonAbstractValue
{
@Override
public JsonToken asToken()
{
return null;
}
@Override
public boolean isMissingNode()
{
return true;
}
}
protected static final TreeNode MISSING = new JsonMissing();
}

View File

@ -0,0 +1,60 @@
package com.fasterxml.jackson.simple.tree;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.TreeNode;
import java.util.Collections;
import java.util.List;
import static com.fasterxml.jackson.core.JsonToken.START_ARRAY;
public class JsonArray extends JsonAbstractValue
{
private final List<TreeNode> values;
public JsonArray()
{
this(Collections.<TreeNode>emptyList());
}
public JsonArray(List<TreeNode> values)
{
this.values = Collections.unmodifiableList(values);
}
@Override
public JsonToken asToken()
{
return START_ARRAY;
}
@Override
public int size()
{
return values.size();
}
@Override
public boolean isContainerNode()
{
return true;
}
@Override
public boolean isArray()
{
return true;
}
@Override
public TreeNode get(int i)
{
return 0 <= i && i < values.size() ? values.get(i) : null;
}
@Override
public TreeNode path(int i)
{
return 0 <= i && i < values.size() ? values.get(i) : MISSING;
}
}

View File

@ -0,0 +1,26 @@
package com.fasterxml.jackson.simple.tree;
import com.fasterxml.jackson.core.JsonToken;
public class JsonBoolean extends JsonAbstractValue
{
public static JsonBoolean TRUE = new JsonBoolean();
public static JsonBoolean FALSE = new JsonBoolean();
private JsonBoolean()
{
}
@Override
public JsonToken asToken()
{
return this == TRUE ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE;
}
@Override
public boolean isValueNode()
{
return true;
}
}

View File

@ -0,0 +1,74 @@
package com.fasterxml.jackson.simple.tree;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static com.fasterxml.jackson.core.JsonToken.VALUE_NUMBER_FLOAT;
import static com.fasterxml.jackson.core.JsonToken.VALUE_NUMBER_INT;
public class JsonNumber extends JsonAbstractValue
{
private static final Map<Class<? extends Number>, JsonParser.NumberType> NUMBER_TYPES;
static
{
final Map<Class<? extends Number>, JsonParser.NumberType> numberTypes = new HashMap<Class<? extends Number>, JsonParser.NumberType>();
numberTypes.put(Byte.class, JsonParser.NumberType.INT);
numberTypes.put(Short.class, JsonParser.NumberType.INT);
numberTypes.put(Integer.class, JsonParser.NumberType.INT);
numberTypes.put(Long.class, JsonParser.NumberType.LONG);
numberTypes.put(BigInteger.class, JsonParser.NumberType.BIG_INTEGER);
numberTypes.put(Float.class, JsonParser.NumberType.FLOAT);
numberTypes.put(Double.class, JsonParser.NumberType.DOUBLE);
numberTypes.put(BigDecimal.class, JsonParser.NumberType.BIG_DECIMAL);
NUMBER_TYPES = Collections.unmodifiableMap(numberTypes);
}
private final Number _value;
private final JsonParser.NumberType _numberType;
public JsonNumber(Number value)
{
if (!NUMBER_TYPES.containsKey(value.getClass()))
throw new IllegalArgumentException("Unsupported Number type");
this._value = value;
this._numberType = NUMBER_TYPES.get(value.getClass());
}
public Number getValue()
{
return _value;
}
@Override
public JsonToken asToken() {
switch (numberType())
{
case BIG_DECIMAL:
case DOUBLE:
case FLOAT:
return VALUE_NUMBER_FLOAT;
default:
return VALUE_NUMBER_INT;
}
}
@Override
public boolean isValueNode()
{
return true;
}
@Override
public JsonParser.NumberType numberType()
{
return _numberType;
}
}

View File

@ -0,0 +1,65 @@
package com.fasterxml.jackson.simple.tree;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.TreeNode;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
public class JsonObject extends JsonAbstractValue
{
private final Map<String, TreeNode> _values;
public JsonObject()
{
this(Collections.<String, TreeNode>emptyMap());
}
public JsonObject(Map<String, TreeNode> values)
{
this._values = Collections.unmodifiableMap(values);
}
@Override
public JsonToken asToken()
{
return JsonToken.START_OBJECT;
}
@Override
public int size()
{
return _values.size();
}
@Override
public boolean isContainerNode()
{
return true;
}
@Override
public boolean isObject()
{
return true;
}
@Override
public Iterator<String> fieldNames()
{
return _values.keySet().iterator();
}
@Override
public TreeNode get(String name)
{
return _values.containsKey(name) ? _values.get(name) : null;
}
@Override
public TreeNode path(String name)
{
return _values.containsKey(name) ? _values.get(name) : MISSING;
}
}

View File

@ -0,0 +1,32 @@
package com.fasterxml.jackson.simple.tree;
import com.fasterxml.jackson.core.JsonToken;
import static com.fasterxml.jackson.core.JsonToken.VALUE_STRING;
public class JsonString extends JsonAbstractValue
{
private final String value;
public JsonString(String value)
{
this.value = value;
}
public String getValue()
{
return value;
}
@Override
public JsonToken asToken()
{
return VALUE_STRING;
}
@Override
public boolean isValueNode()
{
return true;
}
}

View File

@ -0,0 +1,184 @@
/*
* 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 com.fasterxml.jackson.simple.tree;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TreeCodec;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.jr.ob.JSON;
import java.io.IOException;
import java.util.AbstractList;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SimpleTreeCodec extends TreeCodec {
@Override
public <T extends TreeNode> T readTree(JsonParser jsonParser) throws IOException {
return nodeFrom(JSON.std.anyFrom(jsonParser));
}
private <T extends TreeNode> T nodeFrom(Object value)
{
TreeNode node = null;
if (value instanceof Boolean)
node = ((Boolean) value ? JsonBoolean.TRUE : JsonBoolean.FALSE);
else if (value instanceof Number)
node = new JsonNumber(((Number) value));
else if (value instanceof String)
node = new JsonString((String) value);
else if (value instanceof List) {
List<TreeNode> values = new ArrayList<>();
for (Object el : (List) value) {
values.add(nodeFrom(el));
}
node = new JsonArray(values);
}
else if (value instanceof Object[]) {
List<TreeNode> values = new ArrayList<>();
for (Object el : (Object[]) value) {
values.add(nodeFrom(el));
}
node = new JsonArray(values);
}
else if (value instanceof Map) {
Map<String,TreeNode> values = new LinkedHashMap<>();
for (Map.Entry entry : ((Map<?,?>) value).entrySet()) {
values.put(entry.getKey().toString(), nodeFrom(entry.getValue()));
}
node = new JsonObject(values);
}
return (T) node;
}
@Override
public void writeTree(JsonGenerator jsonGenerator, TreeNode treeNode) throws IOException {
JSON.std.write(valueOf(treeNode), jsonGenerator);
}
private Object valueOf(final TreeNode treeNode)
{
if (treeNode == null)
return null;
else if (treeNode instanceof JsonBoolean)
return treeNode == JsonBoolean.TRUE;
else if (treeNode instanceof JsonNumber)
return ((JsonNumber) treeNode).getValue();
else if (treeNode instanceof JsonString)
return ((JsonString) treeNode).getValue();
else if (treeNode instanceof JsonArray) {
return new AbstractList<Object>() {
@Override
public Object get(int index) {
return valueOf(treeNode.get(index));
}
@Override
public int size() {
return treeNode.size();
}
};
}
else if (treeNode instanceof JsonObject) {
return new AbstractMap<String, Object>() {
@Override
public Set<Entry<String, Object>> entrySet() {
return new AbstractSet<Entry<String,Object>>() {
@Override
public Iterator<Entry<String, Object>> iterator() {
return new Iterator<Entry<String, Object>>() {
final Iterator<String> delegate = treeNode.fieldNames();
@Override
public boolean hasNext() {
return delegate.hasNext();
}
@Override
public Entry<String, Object> next() {
final String key = delegate.next();
return new Entry<String, Object>() {
@Override
public String getKey() {
return key;
}
@Override
public Object getValue() {
return valueOf(treeNode.get(key));
}
@Override
public Object setValue(Object value) {
throw new UnsupportedOperationException();
}
};
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return treeNode.size();
}
};
}
};
}
else return treeNode;
}
@Override
public TreeNode createArrayNode() {
return new JsonArray();
}
@Override
public TreeNode createObjectNode() {
return new JsonObject();
}
@Override
public JsonParser treeAsTokens(TreeNode treeNode) {
final TokenBuffer buffer = new TokenBuffer(null, false);
try {
writeTree(buffer, treeNode);
} catch (IOException e) {
}
return buffer.asParser();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -22,8 +22,8 @@ package org.mariotaku.twidere.api.twitter;
import android.util.Log;
import com.bluelinelabs.logansquare.LoganSquare;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.simple.tree.SimpleTreeCodec;
import org.mariotaku.restfu.callback.RawCallback;
import org.mariotaku.restfu.http.RestHttpResponse;
@ -56,7 +56,7 @@ public abstract class UserStreamCallback implements RawCallback {
onException(cause);
return;
}
final ObjectMapper mapper = new ObjectMapper(LoganSquare.JSON_FACTORY);
final SimpleTreeCodec mapper = new SimpleTreeCodec();
final CRLFLineReader reader = new CRLFLineReader(new InputStreamReader(response.getBody().stream(), "UTF-8"));
try {
for (String line; (line = reader.readLine()) != null && !disconnected; ) {
@ -65,7 +65,7 @@ public abstract class UserStreamCallback implements RawCallback {
connected = true;
}
if (line.isEmpty()) continue;
JsonNode rootNode = mapper.readTree(line);
TreeNode rootNode = mapper.readTree(LoganSquare.JSON_FACTORY.createParser(line));
switch (JSONObjectType.determine(rootNode)) {
case SENDER: {
break;
@ -145,7 +145,7 @@ public abstract class UserStreamCallback implements RawCallback {
}
private static <T> T parse(final Class<T> cls, final JsonNode json) throws IOException {
private static <T> T parse(final Class<T> cls, final TreeNode json) throws IOException {
return LoganSquare.mapperFor(cls).parse(json.traverse());
}

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.util;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.core.TreeNode;
/**
* @author Dan Checkoway - dcheckoway at gmail.com
@ -66,29 +66,29 @@ public final class JSONObjectType {
* @param json the JSONObject whose type should be determined
* @return the determined JSONObjectType, or null if not recognized
*/
public static Type determine(JsonNode json) {
public static Type determine(TreeNode json) {
// This code originally lived in AbstractStreamImplementation.
// I've moved it in here to expose it as a public encapsulation of
// the object type determination logic.
if (json.hasNonNull("sender")) {
if (json.get("sender") != null) {
return Type.SENDER;
} else if (json.hasNonNull("text")) {
} else if (json.get("text") != null) {
return Type.STATUS;
} else if (json.hasNonNull("direct_message")) {
} else if (json.get("direct_message") != null) {
return Type.DIRECT_MESSAGE;
} else if (json.hasNonNull("delete")) {
} else if (json.get("delete") != null) {
return Type.DELETE;
} else if (json.hasNonNull("limit")) {
} else if (json.get("limit") != null) {
return Type.LIMIT;
} else if (json.hasNonNull("warning")) {
} else if (json.get("warning") != null) {
return Type.STALL_WARNING;
} else if (json.hasNonNull("scrub_geo")) {
} else if (json.get("scrub_geo") != null) {
return Type.SCRUB_GEO;
} else if (json.hasNonNull("friends")) {
} else if (json.get("friends") != null) {
return Type.FRIENDS;
} else if (json.hasNonNull("event")) {
} else if (json.get("event") != null) {
String event;
event = json.get("event").asText("event");
event = json.get("event").asToken().asString();
if ("favorite".equals(event)) {
return Type.FAVORITE;
} else if ("unfavorite".equals(event)) {
@ -124,7 +124,7 @@ public final class JSONObjectType {
} else if ("unblock".equals(event)) {
return Type.UNBLOCK;
}
} else if (json.hasNonNull("disconnect")) {
} else if (json.get("disconnect") != null) {
return Type.DISCONNECTION;
}
return Type.UNKNOWN;

View File

@ -95,7 +95,11 @@ public class UserProfileEditorFragment extends BaseSupportFragment implements On
private AsyncTask<Object, Object, ?> mTask;
private ImageView mProfileImageView;
private ImageView mProfileBannerView;
private MaterialEditText mEditName, mEditDescription, mEditLocation, mEditUrl;
private MaterialEditText mEditScreenName;
private MaterialEditText mEditName;
private MaterialEditText mEditDescription;
private MaterialEditText mEditLocation;
private MaterialEditText mEditUrl;
private View mProgressContainer, mEditProfileContent;
private View mEditProfileImage;
private View mEditProfileBanner;
@ -282,6 +286,7 @@ public class UserProfileEditorFragment extends BaseSupportFragment implements On
mEditProfileContent = view.findViewById(R.id.edit_profile_content);
mProfileBannerView = (ImageView) view.findViewById(R.id.profile_banner);
mProfileImageView = (ImageView) view.findViewById(R.id.profile_image);
mEditScreenName = (MaterialEditText) view.findViewById(R.id.screen_name);
mEditName = (MaterialEditText) view.findViewById(R.id.name);
mEditDescription = (MaterialEditText) view.findViewById(R.id.description);
mEditLocation = (MaterialEditText) view.findViewById(R.id.location);

View File

@ -276,10 +276,10 @@ public class StreamingService extends Service implements Constants {
if (response != null) {
try {
final TypedData body = response.getBody();
final ByteArrayOutputStream os = new ByteArrayOutputStream();
body.writeTo(os);
final String charsetName;
if (body != null) {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
body.writeTo(os);
final String charsetName;
final ContentType contentType = body.contentType();
if (contentType != null) {
final Charset charset = contentType.getCharset();
@ -291,10 +291,8 @@ public class StreamingService extends Service implements Constants {
} else {
charsetName = Charset.defaultCharset().name();
}
} else {
charsetName = Charset.defaultCharset().name();
Log.w(LOGTAG, os.toString(charsetName));
}
Log.w(LOGTAG, os.toString(charsetName));
} catch (IOException e) {
e.printStackTrace();
}

View File

@ -17,11 +17,10 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<FrameLayout
android:id="@+id/main_content"
xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
@ -39,222 +38,200 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="?cardItemBackgroundColor"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:cardPreventCornerOverlap="false"
app:cardUseCompatPadding="false">
android:background="?cardItemBackgroundColor"
android:orientation="vertical">
<LinearLayout
android:id="@+id/edit_profile_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/edit_profile_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.ForegroundImageView
android:id="@+id/profile_image"
android:layout_width="@dimen/element_size_mlarge"
android:layout_height="@dimen/element_size_mlarge"
android:layout_gravity="center"
android:foreground="?selectableItemBackground"
android:scaleType="centerCrop"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginStart="@dimen/element_spacing_normal"
android:text="@string/profile_image"
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
</LinearLayout>
<LinearLayout
android:id="@+id/edit_profile_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.ForegroundImageView
android:id="@+id/profile_banner"
android:layout_width="@dimen/element_size_mlarge"
android:layout_height="@dimen/element_size_mlarge"
android:layout_gravity="center"
android:foreground="?selectableItemBackground"
android:scaleType="centerCrop"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginStart="@dimen/element_spacing_normal"
android:text="@string/profile_banner"
android:textAllCaps="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/element_spacing_normal"
app:cardBackgroundColor="?cardItemBackgroundColor"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:cardPreventCornerOverlap="false"
app:cardUseCompatPadding="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.themed.BackgroundTintMaterialEditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:singleLine="true"
app:met_baseColor="?android:textColorPrimary"
app:met_floatingLabel="normal"
app:met_floatingLabelText="@string/name"
app:met_maxCharacters="20"/>
<org.mariotaku.twidere.view.ForegroundImageView
android:id="@+id/profile_image"
android:layout_width="@dimen/element_size_mlarge"
android:layout_height="@dimen/element_size_mlarge"
android:layout_gravity="center"
android:foreground="?selectableItemBackground"
android:scaleType="centerCrop" />
<org.mariotaku.twidere.view.themed.BackgroundTintMaterialEditText
android:id="@+id/description"
android:layout_width="match_parent"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:minLines="4"
android:singleLine="false"
app:met_baseColor="?android:textColorPrimary"
app:met_floatingLabel="normal"
app:met_floatingLabelText="@string/description"
app:met_maxCharacters="160"/>
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginStart="@dimen/element_spacing_normal"
android:text="@string/profile_image"
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold" />
<org.mariotaku.twidere.view.themed.BackgroundTintMaterialEditText
android:id="@+id/location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPostalAddress"
android:singleLine="true"
app:met_baseColor="?android:textColorPrimary"
app:met_floatingLabel="normal"
app:met_floatingLabelText="@string/location"
app:met_maxCharacters="30"/>
<org.mariotaku.twidere.view.themed.BackgroundTintMaterialEditText
android:id="@+id/url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textUri"
android:singleLine="true"
app:met_baseColor="?android:textColorPrimary"
app:met_floatingLabel="normal"
app:met_floatingLabelText="@string/url"
app:met_maxCharacters="100"/>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/element_spacing_normal"
app:cardBackgroundColor="?cardItemBackgroundColor"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:cardPreventCornerOverlap="false"
app:cardUseCompatPadding="false">
<LinearLayout
android:id="@+id/edit_profile_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/element_spacing_normal">
<LinearLayout
android:id="@+id/set_link_color"
<org.mariotaku.twidere.view.ForegroundImageView
android:id="@+id/profile_banner"
android:layout_width="@dimen/element_size_mlarge"
android:layout_height="@dimen/element_size_mlarge"
android:layout_gravity="center"
android:foreground="?selectableItemBackground"
android:scaleType="centerCrop" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginStart="@dimen/element_spacing_normal"
android:text="@string/profile_banner"
android:textAllCaps="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="@dimen/element_spacing_normal" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?cardItemBackgroundColor"
android:orientation="vertical"
android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.themed.BackgroundTintMaterialEditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:singleLine="true"
app:met_baseColor="?android:textColorPrimary"
app:met_floatingLabel="normal"
app:met_floatingLabelText="@string/name"
app:met_maxCharacters="20" />
<org.mariotaku.twidere.view.themed.BackgroundTintMaterialEditText
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:minLines="4"
android:singleLine="false"
app:met_baseColor="?android:textColorPrimary"
app:met_floatingLabel="normal"
app:met_floatingLabelText="@string/description"
app:met_maxCharacters="160" />
<org.mariotaku.twidere.view.themed.BackgroundTintMaterialEditText
android:id="@+id/location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPostalAddress"
android:singleLine="true"
app:met_baseColor="?android:textColorPrimary"
app:met_floatingLabel="normal"
app:met_floatingLabelText="@string/location"
app:met_maxCharacters="30" />
<org.mariotaku.twidere.view.themed.BackgroundTintMaterialEditText
android:id="@+id/url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textUri"
android:singleLine="true"
app:met_baseColor="?android:textColorPrimary"
app:met_floatingLabel="normal"
app:met_floatingLabelText="@string/url"
app:met_maxCharacters="100" />
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="@dimen/element_spacing_normal" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?cardItemBackgroundColor"
android:orientation="vertical">
<LinearLayout
android:id="@+id/set_link_color"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.ForegroundColorView
android:id="@+id/link_color"
android:layout_width="@dimen/element_size_normal"
android:layout_height="@dimen/element_size_normal"
android:layout_weight="0" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.ForegroundColorView
android:id="@+id/link_color"
android:layout_width="@dimen/element_size_normal"
android:layout_height="@dimen/element_size_normal"
android:layout_weight="0"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginStart="@dimen/element_spacing_normal"
android:layout_weight="1"
android:text="@string/profile_link_color_main_color"
android:textAllCaps="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
</LinearLayout>
<LinearLayout
android:id="@+id/set_background_color"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.ForegroundColorView
android:id="@+id/background_color"
android:layout_width="@dimen/element_size_normal"
android:layout_height="@dimen/element_size_normal"
android:layout_weight="0"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginStart="@dimen/element_spacing_normal"
android:layout_weight="1"
android:text="@string/profile_background_color"
android:textAllCaps="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"/>
</LinearLayout>
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginStart="@dimen/element_spacing_normal"
android:layout_weight="1"
android:text="@string/profile_link_color_main_color"
android:textAllCaps="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold" />
</LinearLayout>
</android.support.v7.widget.CardView>
<LinearLayout
android:id="@+id/set_background_color"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.ForegroundColorView
android:id="@+id/background_color"
android:layout_width="@dimen/element_size_normal"
android:layout_height="@dimen/element_size_normal"
android:layout_weight="0" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginStart="@dimen/element_spacing_normal"
android:layout_weight="1"
android:text="@string/profile_background_color"
android:textAllCaps="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
@ -266,7 +243,7 @@
android:visibility="visible"
tools:visibility="gone">
<include layout="@layout/layout_progress_wheel_medium"/>
<include layout="@layout/layout_progress_wheel_medium" />
</FrameLayout>
</FrameLayout>

View File

@ -44,7 +44,6 @@
android:layout_below="@id/profile_banner_space"
android:layout_toEndOf="@id/profile_image"
android:layout_toRightOf="@id/profile_image"
android:minHeight="@dimen/element_size_normal"
android:minWidth="48dp"
android:orientation="vertical"
android:paddingBottom="@dimen/element_spacing_xsmall"
@ -59,7 +58,8 @@
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerInParent="true">
android:layout_centerInParent="true"
android:minHeight="@dimen/element_size_normal">
<Button
android:id="@+id/follow"
@ -86,6 +86,7 @@
android:layout_toLeftOf="@id/follow_container"
android:layout_toStartOf="@id/follow_container"
android:gravity="center_vertical"
android:minHeight="@dimen/element_size_normal"
android:orientation="vertical">
<TextView

View File

@ -28,6 +28,18 @@
android:id="@id/add_to_filter"
android:icon="@drawable/ic_action_speaker_muted"
android:title="@string/add_to_filter" />
<item
android:id="@+id/incoming_friendships"
android:icon="@drawable/ic_action_profile"
android:title="@string/incoming_friendships" />
<item
android:id="@+id/user_mentions"
android:icon="@drawable/ic_action_at"
android:title="@string/user_mentions" />
<item
android:id="@+id/saved_searches"
android:icon="@drawable/ic_action_search"
android:title="@string/saved_searches" />
<item
android:id="@id/enable_retweets"
android:checkable="true"
@ -53,18 +65,6 @@
android:id="@+id/blocked_users"
android:icon="@drawable/ic_action_block"
android:title="@string/blocked_users" />
<item
android:id="@+id/incoming_friendships"
android:icon="@drawable/ic_action_profile"
android:title="@string/incoming_friendships" />
<item
android:id="@+id/user_mentions"
android:icon="@drawable/ic_action_at"
android:title="@string/user_mentions" />
<item
android:id="@+id/saved_searches"
android:icon="@drawable/ic_action_search"
android:title="@string/saved_searches" />
<item
android:id="@id/open_with_account"
android:icon="@drawable/ic_action_accounts"

View File

@ -380,7 +380,7 @@
<string name="preview_images">Preview images</string>
<string name="preload_wifi_only">Preload using Wi-Fi only</string>
<string name="sign_in_method_introduction_title">How does it work?</string>
<string name="sign_in_method_introduction">Most clients like twicca open a browser asking you to type username and password, and then take you back to the clients. Sometimes this can become very inconvenient.\n\nTwidere uses simpler steps to sign in, without opening the browser. Don\'t worry about your password, Twidere will never store it, it\'s totally safe!</string>
<string name="sign_in_method_introduction">Most clients opens a webpage to authorize to Twitter, this could be inconvenient when using custom API, or on slow network. Twidere simulates a normal browser to help sign in to Twitter. Don\'t worry, your password will never be stored nor leaked.</string>
<string name="quote_protected_status_notice">It\'s not recommended to quote protected tweets.</string>
<string name="edit_draft">Edit draft</string>
<string name="profile_image">Profile image</string>