made json mapper resolve single threaded

This commit is contained in:
Mariotaku Lee 2017-04-05 13:03:18 +08:00
parent 033d30bf3f
commit 0d79ab4b27
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
44 changed files with 395 additions and 1626 deletions

View File

@ -35,7 +35,7 @@ subprojects {
libVersions = [ libVersions = [
Kotlin : '1.1.1', Kotlin : '1.1.1',
SupportLib : '25.3.1', SupportLib : '25.3.1',
MariotakuCommons : '0.9.11', MariotakuCommons : '0.9.12',
RestFu : '0.9.44', RestFu : '0.9.44',
ObjectCursor : '0.9.16', ObjectCursor : '0.9.16',
PlayServices : '10.2.1', PlayServices : '10.2.1',

View File

@ -23,8 +23,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.microblog.library.MicroBlogException; import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.fanfou.model.FanfouStreamObject; import org.mariotaku.microblog.library.fanfou.model.FanfouStreamObject;
import org.mariotaku.microblog.library.twitter.model.Status; import org.mariotaku.microblog.library.twitter.model.Status;
@ -32,6 +30,7 @@ import org.mariotaku.microblog.library.twitter.model.User;
import org.mariotaku.microblog.library.util.CRLFLineReader; import org.mariotaku.microblog.library.util.CRLFLineReader;
import org.mariotaku.restfu.callback.RawCallback; import org.mariotaku.restfu.callback.RawCallback;
import org.mariotaku.restfu.http.HttpResponse; import org.mariotaku.restfu.http.HttpResponse;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -66,7 +65,7 @@ public abstract class FanfouUserStreamCallback implements RawCallback<MicroBlogE
connected = true; connected = true;
} }
if (TextUtils.isEmpty(line)) continue; if (TextUtils.isEmpty(line)) continue;
FanfouStreamObject object = LoganSquare.parse(line, FanfouStreamObject.class); FanfouStreamObject object = JsonSerializer.parse(line, FanfouStreamObject.class);
if (!handleEvent(object, line)) { if (!handleEvent(object, line)) {
onUnhandledEvent(object.getEvent(), line); onUnhandledEvent(object.getEvent(), line);
} }

View File

@ -23,13 +23,13 @@ package org.mariotaku.microblog.library.fanfou.model;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.bluelinelabs.logansquare.LoganSquare;
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 org.mariotaku.commons.logansquare.JsonStringConverter; import org.mariotaku.commons.logansquare.JsonStringConverter;
import org.mariotaku.microblog.library.fanfou.model.util.StreamDateConverter; import org.mariotaku.microblog.library.fanfou.model.util.StreamDateConverter;
import org.mariotaku.microblog.library.twitter.model.User; import org.mariotaku.microblog.library.twitter.model.User;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.util.Date; import java.util.Date;
@ -71,7 +71,7 @@ public class FanfouStreamObject {
public <T> T getObject(Class<T> cls) throws IOException { public <T> T getObject(Class<T> cls) throws IOException {
if (rawObject == null) return null; if (rawObject == null) return null;
return LoganSquare.parse(rawObject, cls); return JsonSerializer.parse(rawObject, cls);
} }
} }

View File

@ -22,8 +22,6 @@ package org.mariotaku.microblog.library.twitter.callback;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.microblog.library.MicroBlogException; import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.DeletionEvent; import org.mariotaku.microblog.library.twitter.model.DeletionEvent;
import org.mariotaku.microblog.library.twitter.model.DirectMessage; import org.mariotaku.microblog.library.twitter.model.DirectMessage;
@ -39,6 +37,7 @@ import org.mariotaku.microblog.library.twitter.model.Warning;
import org.mariotaku.microblog.library.util.CRLFLineReader; import org.mariotaku.microblog.library.util.CRLFLineReader;
import org.mariotaku.restfu.callback.RawCallback; import org.mariotaku.restfu.callback.RawCallback;
import org.mariotaku.restfu.http.HttpResponse; import org.mariotaku.restfu.http.HttpResponse;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -73,7 +72,7 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
connected = true; connected = true;
} }
if (TextUtils.isEmpty(line)) continue; if (TextUtils.isEmpty(line)) continue;
final TwitterStreamObject object = LoganSquare.parse(line, TwitterStreamObject.class); final TwitterStreamObject object = JsonSerializer.parse(line, TwitterStreamObject.class);
if (!handleEvent(object, line)) { if (!handleEvent(object, line)) {
onUnhandledEvent(object, line); onUnhandledEvent(object, line);
} }
@ -100,7 +99,7 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
return onFriendList(object.getFriends()); return onFriendList(object.getFriends());
} }
case Type.STATUS: { case Type.STATUS: {
return onStatus(LoganSquare.parse(json, Status.class)); return onStatus(JsonSerializer.parse(json, Status.class));
} }
case Type.DIRECT_MESSAGE: { case Type.DIRECT_MESSAGE: {
return onDirectMessage(object.getDirectMessage()); return onDirectMessage(object.getDirectMessage());
@ -125,82 +124,82 @@ public abstract class UserStreamCallback implements RawCallback<MicroBlogExcepti
return onScrubGeo(scrubGeo.getUserId(), scrubGeo.getUpToStatusId()); return onScrubGeo(scrubGeo.getUserId(), scrubGeo.getUpToStatusId());
} }
case Type.FAVORITE: { case Type.FAVORITE: {
StatusTargetObjectEvent event = LoganSquare.parse(json, StatusTargetObjectEvent.class); StatusTargetObjectEvent event = JsonSerializer.parse(json, StatusTargetObjectEvent.class);
return onFavorite(event.getCreatedAt(), event.getSource(), event.getTarget(), return onFavorite(event.getCreatedAt(), event.getSource(), event.getTarget(),
event.getTargetObject()); event.getTargetObject());
} }
case Type.UNFAVORITE: { case Type.UNFAVORITE: {
StatusTargetObjectEvent event = LoganSquare.parse(json, StatusTargetObjectEvent.class); StatusTargetObjectEvent event = JsonSerializer.parse(json, StatusTargetObjectEvent.class);
return onUnfavorite(event.getSource(), event.getTarget(), event.getTargetObject()); return onUnfavorite(event.getSource(), event.getTarget(), event.getTargetObject());
} }
case Type.QUOTED_TWEET: { case Type.QUOTED_TWEET: {
StatusTargetObjectEvent event = LoganSquare.parse(json, StatusTargetObjectEvent.class); StatusTargetObjectEvent event = JsonSerializer.parse(json, StatusTargetObjectEvent.class);
return onQuotedTweet(event.getCreatedAt(), event.getSource(), event.getTarget(), return onQuotedTweet(event.getCreatedAt(), event.getSource(), event.getTarget(),
event.getTargetObject()); event.getTargetObject());
} }
case Type.RETWEETED_RETWEET: { case Type.RETWEETED_RETWEET: {
StatusTargetObjectEvent event = LoganSquare.parse(json, StatusTargetObjectEvent.class); StatusTargetObjectEvent event = JsonSerializer.parse(json, StatusTargetObjectEvent.class);
return onRetweetedRetweet(event.getCreatedAt(), event.getSource(), event.getTarget(), return onRetweetedRetweet(event.getCreatedAt(), event.getSource(), event.getTarget(),
event.getTargetObject()); event.getTargetObject());
} }
case Type.FAVORITED_RETWEET: { case Type.FAVORITED_RETWEET: {
StatusTargetObjectEvent event = LoganSquare.parse(json, StatusTargetObjectEvent.class); StatusTargetObjectEvent event = JsonSerializer.parse(json, StatusTargetObjectEvent.class);
return onFavoritedRetweet(event.getCreatedAt(), event.getSource(), event.getTarget(), return onFavoritedRetweet(event.getCreatedAt(), event.getSource(), event.getTarget(),
event.getTargetObject()); event.getTargetObject());
} }
case Type.FOLLOW: { case Type.FOLLOW: {
StreamEvent event = LoganSquare.parse(json, StreamEvent.class); StreamEvent event = JsonSerializer.parse(json, StreamEvent.class);
return onFollow(event.getCreatedAt(), event.getSource(), event.getTarget()); return onFollow(event.getCreatedAt(), event.getSource(), event.getTarget());
} }
case Type.UNFOLLOW: { case Type.UNFOLLOW: {
StreamEvent event = LoganSquare.parse(json, StreamEvent.class); StreamEvent event = JsonSerializer.parse(json, StreamEvent.class);
return onUnfollow(event.getCreatedAt(), event.getSource(), event.getTarget()); return onUnfollow(event.getCreatedAt(), event.getSource(), event.getTarget());
} }
case Type.USER_LIST_MEMBER_ADDED: { case Type.USER_LIST_MEMBER_ADDED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class); UserListTargetObjectEvent event = JsonSerializer.parse(json, UserListTargetObjectEvent.class);
return onUserListMemberAddition(event.getCreatedAt(), event.getSource(), return onUserListMemberAddition(event.getCreatedAt(), event.getSource(),
event.getTarget(), event.getTargetObject()); event.getTarget(), event.getTargetObject());
} }
case Type.USER_LIST_MEMBER_DELETED: { case Type.USER_LIST_MEMBER_DELETED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class); UserListTargetObjectEvent event = JsonSerializer.parse(json, UserListTargetObjectEvent.class);
return onUserListMemberDeletion(event.getCreatedAt(), event.getSource(), return onUserListMemberDeletion(event.getCreatedAt(), event.getSource(),
event.getTarget(), event.getTargetObject()); event.getTarget(), event.getTargetObject());
} }
case Type.USER_LIST_SUBSCRIBED: { case Type.USER_LIST_SUBSCRIBED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class); UserListTargetObjectEvent event = JsonSerializer.parse(json, UserListTargetObjectEvent.class);
return onUserListSubscription(event.getCreatedAt(), event.getSource(), return onUserListSubscription(event.getCreatedAt(), event.getSource(),
event.getTarget(), event.getTargetObject()); event.getTarget(), event.getTargetObject());
} }
case Type.USER_LIST_UNSUBSCRIBED: { case Type.USER_LIST_UNSUBSCRIBED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class); UserListTargetObjectEvent event = JsonSerializer.parse(json, UserListTargetObjectEvent.class);
return onUserListUnsubscription(event.getCreatedAt(), event.getSource(), return onUserListUnsubscription(event.getCreatedAt(), event.getSource(),
event.getTarget(), event.getTargetObject()); event.getTarget(), event.getTargetObject());
} }
case Type.USER_LIST_CREATED: { case Type.USER_LIST_CREATED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class); UserListTargetObjectEvent event = JsonSerializer.parse(json, UserListTargetObjectEvent.class);
return onUserListCreation(event.getCreatedAt(), event.getSource(), return onUserListCreation(event.getCreatedAt(), event.getSource(),
event.getTargetObject()); event.getTargetObject());
} }
case Type.USER_LIST_UPDATED: { case Type.USER_LIST_UPDATED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class); UserListTargetObjectEvent event = JsonSerializer.parse(json, UserListTargetObjectEvent.class);
return onUserListUpdate(event.getCreatedAt(), event.getSource(), return onUserListUpdate(event.getCreatedAt(), event.getSource(),
event.getTargetObject()); event.getTargetObject());
} }
case Type.USER_LIST_DESTROYED: { case Type.USER_LIST_DESTROYED: {
UserListTargetObjectEvent event = LoganSquare.parse(json, UserListTargetObjectEvent.class); UserListTargetObjectEvent event = JsonSerializer.parse(json, UserListTargetObjectEvent.class);
return onUserListDeletion(event.getCreatedAt(), event.getSource(), return onUserListDeletion(event.getCreatedAt(), event.getSource(),
event.getTargetObject()); event.getTargetObject());
} }
case Type.USER_UPDATE: { case Type.USER_UPDATE: {
StreamEvent event = LoganSquare.parse(json, StreamEvent.class); StreamEvent event = JsonSerializer.parse(json, StreamEvent.class);
return onUserProfileUpdate(event.getCreatedAt(), event.getSource()); return onUserProfileUpdate(event.getCreatedAt(), event.getSource());
} }
case Type.BLOCK: { case Type.BLOCK: {
StreamEvent event = LoganSquare.parse(json, StreamEvent.class); StreamEvent event = JsonSerializer.parse(json, StreamEvent.class);
return onBlock(event.getCreatedAt(), event.getSource(), event.getTarget()); return onBlock(event.getCreatedAt(), event.getSource(), event.getTarget());
} }
case Type.UNBLOCK: { case Type.UNBLOCK: {
StreamEvent event = LoganSquare.parse(json, StreamEvent.class); StreamEvent event = JsonSerializer.parse(json, StreamEvent.class);
return onUnblock(event.getCreatedAt(), event.getSource(), event.getTarget()); return onUnblock(event.getCreatedAt(), event.getSource(), event.getTarget());
} }
case Type.DISCONNECTION: case Type.DISCONNECTION:

View File

@ -27,7 +27,6 @@ import android.os.Parcelable;
import android.support.annotation.ColorInt; import android.support.annotation.ColorInt;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.bluelinelabs.logansquare.LoganSquare;
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;
@ -45,6 +44,7 @@ import org.mariotaku.twidere.model.account.AccountExtras;
import org.mariotaku.twidere.model.account.cred.Credentials; import org.mariotaku.twidere.model.account.cred.Credentials;
import org.mariotaku.twidere.model.util.RGBHexColorConverter; import org.mariotaku.twidere.model.util.RGBHexColorConverter;
import org.mariotaku.twidere.model.util.UserKeyConverter; import org.mariotaku.twidere.model.util.UserKeyConverter;
import org.mariotaku.twidere.util.JsonSerializer;
import org.mariotaku.twidere.util.model.AccountDetailsUtils; import org.mariotaku.twidere.util.model.AccountDetailsUtils;
import java.io.IOException; import java.io.IOException;
@ -140,10 +140,10 @@ public class AccountDetails implements Parcelable, Comparable<AccountDetails> {
@OnPreJsonSerialize @OnPreJsonSerialize
void onPreJsonSerialize() throws IOException { void onPreJsonSerialize() throws IOException {
if (credentials != null) { if (credentials != null) {
credentials_json = LoganSquare.serialize(credentials); credentials_json = JsonSerializer.serialize(credentials);
} }
if (extras != null) { if (extras != null) {
extras_json = LoganSquare.serialize(extras); extras_json = JsonSerializer.serialize(extras);
} }
} }

View File

@ -25,10 +25,10 @@ import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.annotation.JsonObject; import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.model.ParcelableMessage.MessageType; import org.mariotaku.twidere.model.ParcelableMessage.MessageType;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
@ -42,14 +42,14 @@ public abstract class MessageExtras implements Parcelable {
if (json == null) return null; if (json == null) return null;
switch (messageType) { switch (messageType) {
case MessageType.STICKER: case MessageType.STICKER:
return LoganSquare.parse(json, StickerExtras.class); return JsonSerializer.parse(json, StickerExtras.class);
case MessageType.JOIN_CONVERSATION: case MessageType.JOIN_CONVERSATION:
case MessageType.PARTICIPANTS_LEAVE: case MessageType.PARTICIPANTS_LEAVE:
case MessageType.PARTICIPANTS_JOIN: case MessageType.PARTICIPANTS_JOIN:
return LoganSquare.parse(json, UserArrayExtras.class); return JsonSerializer.parse(json, UserArrayExtras.class);
case MessageType.CONVERSATION_NAME_UPDATE: case MessageType.CONVERSATION_NAME_UPDATE:
case MessageType.CONVERSATION_AVATAR_UPDATE: case MessageType.CONVERSATION_AVATAR_UPDATE:
return LoganSquare.parse(json, ConversationInfoUpdatedExtras.class); return JsonSerializer.parse(json, ConversationInfoUpdatedExtras.class);
} }
return null; return null;
} }

View File

@ -25,9 +25,8 @@ import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.twidere.model.ParcelableMessageConversation.ExtrasType; import org.mariotaku.twidere.model.ParcelableMessageConversation.ExtrasType;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
@ -40,9 +39,9 @@ public abstract class ConversationExtras implements Parcelable {
if (json == null) return null; if (json == null) return null;
switch (extrasType) { switch (extrasType) {
case ExtrasType.TWITTER_OFFICIAL: { case ExtrasType.TWITTER_OFFICIAL: {
return LoganSquare.parse(json, TwitterOfficialConversationExtras.class); return JsonSerializer.parse(json, TwitterOfficialConversationExtras.class);
} }
} }
return LoganSquare.parse(json, DefaultConversationExtras.class); return JsonSerializer.parse(json, DefaultConversationExtras.class);
} }
} }

View File

@ -26,13 +26,13 @@ import android.support.annotation.CallSuper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.LoganSquare;
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 org.mariotaku.twidere.TwidereConstants; import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.annotation.CustomTabType; import org.mariotaku.twidere.annotation.CustomTabType;
import org.mariotaku.twidere.model.UserKey; import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -102,17 +102,17 @@ public class TabArguments implements TwidereConstants {
case CustomTabType.NOTIFICATIONS_TIMELINE: case CustomTabType.NOTIFICATIONS_TIMELINE:
case CustomTabType.DIRECT_MESSAGES: case CustomTabType.DIRECT_MESSAGES:
case CustomTabType.TRENDS_SUGGESTIONS: { case CustomTabType.TRENDS_SUGGESTIONS: {
return LoganSquare.parse(json, TabArguments.class); return JsonSerializer.parse(json, TabArguments.class);
} }
case CustomTabType.USER_TIMELINE: case CustomTabType.USER_TIMELINE:
case CustomTabType.FAVORITES: { case CustomTabType.FAVORITES: {
return LoganSquare.parse(json, UserArguments.class); return JsonSerializer.parse(json, UserArguments.class);
} }
case CustomTabType.LIST_TIMELINE: { case CustomTabType.LIST_TIMELINE: {
return LoganSquare.parse(json, UserListArguments.class); return JsonSerializer.parse(json, UserListArguments.class);
} }
case CustomTabType.SEARCH_STATUSES: { case CustomTabType.SEARCH_STATUSES: {
return LoganSquare.parse(json, TextQueryArguments.class); return JsonSerializer.parse(json, TextQueryArguments.class);
} }
} }
return null; return null;

View File

@ -27,10 +27,10 @@ import android.support.annotation.CallSuper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.annotation.JsonObject; import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.annotation.CustomTabType; import org.mariotaku.twidere.annotation.CustomTabType;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
@ -49,13 +49,13 @@ public abstract class TabExtras implements Parcelable {
if (json == null) return null; if (json == null) return null;
switch (type) { switch (type) {
case CustomTabType.NOTIFICATIONS_TIMELINE: { case CustomTabType.NOTIFICATIONS_TIMELINE: {
return LoganSquare.parse(json, InteractionsTabExtras.class); return JsonSerializer.parse(json, InteractionsTabExtras.class);
} }
case CustomTabType.HOME_TIMELINE: { case CustomTabType.HOME_TIMELINE: {
return LoganSquare.parse(json, HomeTabExtras.class); return JsonSerializer.parse(json, HomeTabExtras.class);
} }
case CustomTabType.TRENDS_SUGGESTIONS: { case CustomTabType.TRENDS_SUGGESTIONS: {
return LoganSquare.parse(json, TrendsTabExtras.class); return JsonSerializer.parse(json, TrendsTabExtras.class);
} }
} }
return null; return null;

View File

@ -25,11 +25,10 @@ import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.text.TextUtils; import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter; import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
import org.mariotaku.twidere.model.message.conversation.ConversationExtras; import org.mariotaku.twidere.model.message.conversation.ConversationExtras;
import org.mariotaku.twidere.provider.TwidereDataStore.Messages; import org.mariotaku.twidere.provider.TwidereDataStore.Messages;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -48,6 +47,6 @@ public class ConversationExtrasConverter implements CursorFieldConverter<Convers
@Override @Override
public void writeField(ContentValues values, ConversationExtras object, String columnName, ParameterizedType fieldType) throws IOException { public void writeField(ContentValues values, ConversationExtras object, String columnName, ParameterizedType fieldType) throws IOException {
if (object == null) return; if (object == null) return;
values.put(columnName, LoganSquare.serialize(object)); values.put(columnName, JsonSerializer.serialize(object));
} }
} }

View File

@ -25,8 +25,6 @@ import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.text.TextUtils; import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter; import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
import org.mariotaku.twidere.model.Draft; import org.mariotaku.twidere.model.Draft;
import org.mariotaku.twidere.model.draft.ActionExtras; import org.mariotaku.twidere.model.draft.ActionExtras;
@ -34,6 +32,7 @@ import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras;
import org.mariotaku.twidere.model.draft.StatusObjectExtras; import org.mariotaku.twidere.model.draft.StatusObjectExtras;
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras; import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras;
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts; import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -53,15 +52,15 @@ public class DraftExtrasConverter implements CursorFieldConverter<ActionExtras>
case Draft.Action.UPDATE_STATUS: case Draft.Action.UPDATE_STATUS:
case Draft.Action.REPLY: case Draft.Action.REPLY:
case Draft.Action.QUOTE: { case Draft.Action.QUOTE: {
return LoganSquare.parse(json, UpdateStatusActionExtras.class); return JsonSerializer.parse(json, UpdateStatusActionExtras.class);
} }
case Draft.Action.SEND_DIRECT_MESSAGE_COMPAT: case Draft.Action.SEND_DIRECT_MESSAGE_COMPAT:
case Draft.Action.SEND_DIRECT_MESSAGE: { case Draft.Action.SEND_DIRECT_MESSAGE: {
return LoganSquare.parse(json, SendDirectMessageActionExtras.class); return JsonSerializer.parse(json, SendDirectMessageActionExtras.class);
} }
case Draft.Action.FAVORITE: case Draft.Action.FAVORITE:
case Draft.Action.RETWEET: { case Draft.Action.RETWEET: {
return LoganSquare.parse(json, StatusObjectExtras.class); return JsonSerializer.parse(json, StatusObjectExtras.class);
} }
} }
return null; return null;
@ -70,6 +69,6 @@ public class DraftExtrasConverter implements CursorFieldConverter<ActionExtras>
@Override @Override
public void writeField(ContentValues values, ActionExtras object, String columnName, ParameterizedType fieldType) throws IOException { public void writeField(ContentValues values, ActionExtras object, String columnName, ParameterizedType fieldType) throws IOException {
if (object == null) return; if (object == null) return;
values.put(columnName, LoganSquare.serialize(object)); values.put(columnName, JsonSerializer.serialize(object));
} }
} }

View File

@ -25,11 +25,10 @@ import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.text.TextUtils; import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter; import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
import org.mariotaku.twidere.model.message.MessageExtras; import org.mariotaku.twidere.model.message.MessageExtras;
import org.mariotaku.twidere.provider.TwidereDataStore.Messages; import org.mariotaku.twidere.provider.TwidereDataStore.Messages;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -48,6 +47,6 @@ public class MessageExtrasConverter implements CursorFieldConverter<MessageExtra
@Override @Override
public void writeField(ContentValues values, MessageExtras object, String columnName, ParameterizedType fieldType) throws IOException { public void writeField(ContentValues values, MessageExtras object, String columnName, ParameterizedType fieldType) throws IOException {
if (object == null) return; if (object == null) return;
values.put(columnName, LoganSquare.serialize(object)); values.put(columnName, JsonSerializer.serialize(object));
} }
} }

View File

@ -25,12 +25,11 @@ import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.text.TextUtils; import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter; import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
import org.mariotaku.twidere.model.Tab; import org.mariotaku.twidere.model.Tab;
import org.mariotaku.twidere.model.tab.argument.TabArguments; import org.mariotaku.twidere.model.tab.argument.TabArguments;
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs; import org.mariotaku.twidere.provider.TwidereDataStore.Tabs;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -51,7 +50,7 @@ public class TabArgumentsFieldConverter implements CursorFieldConverter<TabArgum
public void writeField(ContentValues values, TabArguments object, String columnName, ParameterizedType fieldType) { public void writeField(ContentValues values, TabArguments object, String columnName, ParameterizedType fieldType) {
if (object == null) return; if (object == null) return;
try { try {
values.put(columnName, LoganSquare.serialize(object)); values.put(columnName, JsonSerializer.serialize(object));
} catch (IOException e) { } catch (IOException e) {
// Ignore // Ignore
} }

View File

@ -25,12 +25,11 @@ import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.text.TextUtils; import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter; import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
import org.mariotaku.twidere.model.Tab; import org.mariotaku.twidere.model.Tab;
import org.mariotaku.twidere.model.tab.extra.TabExtras; import org.mariotaku.twidere.model.tab.extra.TabExtras;
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs; import org.mariotaku.twidere.provider.TwidereDataStore.Tabs;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -49,6 +48,6 @@ public class TabExtrasFieldConverter implements CursorFieldConverter<TabExtras>
@Override @Override
public void writeField(ContentValues values, TabExtras object, String columnName, ParameterizedType fieldType) throws IOException { public void writeField(ContentValues values, TabExtras object, String columnName, ParameterizedType fieldType) throws IOException {
if (object == null) return; if (object == null) return;
values.put(columnName, LoganSquare.serialize(object)); values.put(columnName, JsonSerializer.serialize(object));
} }
} }

View File

@ -0,0 +1,169 @@
/*
* 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.util;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.JsonMapper;
import com.bluelinelabs.logansquare.ParameterizedType;
import org.mariotaku.commons.logansquare.LoganSquareMapperFinder;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Created by mariotaku on 15/8/6.
*/
public class JsonSerializer {
static {
}
private JsonSerializer() {
}
@Nullable
public static <T> String serializeList(@Nullable final List<T> list, final Class<T> cls) {
if (list == null) return null;
try {
return LoganSquareMapperFinder.mapperFor(cls).serialize(list);
} catch (IOException e) {
return null;
}
}
@Nullable
public static <T> String serializeMap(@Nullable final Map<String, T> list, final Class<T> cls) {
if (list == null) return null;
try {
return LoganSquareMapperFinder.mapperFor(cls).serialize(list);
} catch (IOException e) {
return null;
}
}
@Nullable
public static <T> String serialize(@Nullable final T[] array, final Class<T> cls) {
if (array == null) return null;
try {
return LoganSquareMapperFinder.mapperFor(cls).serialize(Arrays.asList(array));
} catch (IOException e) {
return null;
}
}
@Nullable
public static <T> String serialize(@Nullable final T object, final Class<T> cls) {
if (object == null) return null;
try {
return LoganSquareMapperFinder.mapperFor(cls).serialize(object);
} catch (IOException e) {
return null;
}
}
@Nullable
public static <T> String serialize(@Nullable final T object, final ParameterizedType<T> cls) {
if (object == null) return null;
try {
return LoganSquareMapperFinder.mapperFor(cls).serialize(object);
} catch (IOException e) {
return null;
}
}
public static <T> void serialize(@Nullable final T object, final OutputStream st,
final Class<T> cls) throws IOException {
LoganSquareMapperFinder.mapperFor(cls).serialize(object, st);
}
public static <T> void serialize(@Nullable final List<T> list, final OutputStream st,
final Class<T> cls) throws IOException {
LoganSquareMapperFinder.mapperFor(cls).serialize(list, st);
}
@NonNull
public static <T> String serialize(@Nullable final T object) throws IOException {
if (object == null) throw new IOException();
//noinspection unchecked
final Class<T> cls = (Class<T>) object.getClass();
final JsonMapper<T> mapper = LoganSquareMapperFinder.mapperFor(cls);
return mapper.serialize(object);
}
@NonNull
public static <T> T[] parseArray(@Nullable final String string, final Class<T> cls) throws IOException {
if (string == null) throw new IOException();
final List<T> list = LoganSquareMapperFinder.mapperFor(cls).parseList(string);
//noinspection unchecked
return list.toArray((T[]) Array.newInstance(cls, list.size()));
}
@NonNull
public static <T> T parse(@Nullable final String string, final Class<T> cls) throws IOException {
if (string == null) throw new IOException();
return LoganSquareMapperFinder.mapperFor(cls).parse(string);
}
@NonNull
public static <T> T parse(@Nullable final String string, final ParameterizedType<T> cls) throws IOException {
if (string == null) throw new IOException();
return LoganSquareMapperFinder.mapperFor(cls).parse(string);
}
@NonNull
public static <T> T parse(@Nullable final InputStream stream, final Class<T> cls) throws IOException {
if (stream == null) throw new IOException();
return LoganSquareMapperFinder.mapperFor(cls).parse(stream);
}
@NonNull
public static <T> T parse(@Nullable final InputStream stream, final ParameterizedType<T> cls) throws IOException {
if (stream == null) throw new IOException();
return LoganSquareMapperFinder.mapperFor(cls).parse(stream);
}
@NonNull
public static <E> List<E> parseList(@Nullable InputStream stream, Class<E> cls) throws IOException {
if (stream == null) throw new IOException();
return LoganSquareMapperFinder.mapperFor(cls).parseList(stream);
}
@NonNull
public static <E> Map<String, E> parseMap(@Nullable InputStream stream, Class<E> cls) throws IOException {
if (stream == null) throw new IOException();
return LoganSquareMapperFinder.mapperFor(cls).parseMap(stream);
}
@NonNull
public static <E> List<E> parseList(@Nullable String json, Class<E> cls) throws IOException {
if (json == null) throw new IOException();
return LoganSquareMapperFinder.mapperFor(cls).parseList(json);
}
}

View File

@ -21,8 +21,6 @@
package org.mariotaku.twidere.util.model; package org.mariotaku.twidere.util.model;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.twidere.annotation.AccountType; import org.mariotaku.twidere.annotation.AccountType;
import org.mariotaku.twidere.model.account.AccountExtras; import org.mariotaku.twidere.model.account.AccountExtras;
import org.mariotaku.twidere.model.account.StatusNetAccountExtras; import org.mariotaku.twidere.model.account.StatusNetAccountExtras;
@ -32,6 +30,7 @@ import org.mariotaku.twidere.model.account.cred.Credentials;
import org.mariotaku.twidere.model.account.cred.EmptyCredentials; import org.mariotaku.twidere.model.account.cred.EmptyCredentials;
import org.mariotaku.twidere.model.account.cred.OAuth2Credentials; import org.mariotaku.twidere.model.account.cred.OAuth2Credentials;
import org.mariotaku.twidere.model.account.cred.OAuthCredentials; import org.mariotaku.twidere.model.account.cred.OAuthCredentials;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
@ -45,16 +44,16 @@ public class AccountDetailsUtils {
switch (type) { switch (type) {
case Credentials.Type.OAUTH: case Credentials.Type.OAUTH:
case Credentials.Type.XAUTH: { case Credentials.Type.XAUTH: {
return LoganSquare.parse(json, OAuthCredentials.class); return JsonSerializer.parse(json, OAuthCredentials.class);
} }
case Credentials.Type.BASIC: { case Credentials.Type.BASIC: {
return LoganSquare.parse(json, BasicCredentials.class); return JsonSerializer.parse(json, BasicCredentials.class);
} }
case Credentials.Type.EMPTY: { case Credentials.Type.EMPTY: {
return LoganSquare.parse(json, EmptyCredentials.class); return JsonSerializer.parse(json, EmptyCredentials.class);
} }
case Credentials.Type.OAUTH2: { case Credentials.Type.OAUTH2: {
return LoganSquare.parse(json, OAuth2Credentials.class); return JsonSerializer.parse(json, OAuth2Credentials.class);
} }
} }
} catch (IOException e) { } catch (IOException e) {
@ -68,10 +67,10 @@ public class AccountDetailsUtils {
try { try {
switch (type) { switch (type) {
case AccountType.TWITTER: { case AccountType.TWITTER: {
return LoganSquare.parse(json, TwitterAccountExtras.class); return JsonSerializer.parse(json, TwitterAccountExtras.class);
} }
case AccountType.STATUSNET: { case AccountType.STATUSNET: {
return LoganSquare.parse(json, StatusNetAccountExtras.class); return JsonSerializer.parse(json, StatusNetAccountExtras.class);
} }
} }
} catch (IOException e) { } catch (IOException e) {

View File

@ -38,8 +38,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.RequiresPermission; import android.support.annotation.RequiresPermission;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.twidere.model.AccountDetails; import org.mariotaku.twidere.model.AccountDetails;
import org.mariotaku.twidere.model.ComposingStatus; import org.mariotaku.twidere.model.ComposingStatus;
import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatus;
@ -47,6 +45,7 @@ import org.mariotaku.twidere.model.ParcelableUser;
import org.mariotaku.twidere.model.ParcelableUserList; import org.mariotaku.twidere.model.ParcelableUserList;
import org.mariotaku.twidere.model.UserKey; import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.provider.TwidereDataStore.Permissions; import org.mariotaku.twidere.provider.TwidereDataStore.Permissions;
import org.mariotaku.twidere.util.JsonSerializer;
import org.mariotaku.twidere.util.model.AccountDetailsUtils; import org.mariotaku.twidere.util.model.AccountDetailsUtils;
import java.io.IOException; import java.io.IOException;
@ -192,7 +191,8 @@ public final class Twidere implements TwidereConstants {
//noinspection WrongConstant //noinspection WrongConstant
details.credentials_type = am.getUserData(account, ACCOUNT_USER_DATA_CREDS_TYPE); details.credentials_type = am.getUserData(account, ACCOUNT_USER_DATA_CREDS_TYPE);
try { try {
details.user = LoganSquare.parse(am.getUserData(account, ACCOUNT_USER_DATA_USER), ParcelableUser.class); details.user = JsonSerializer.parse(am.getUserData(account, ACCOUNT_USER_DATA_USER),
ParcelableUser.class);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -32,8 +32,8 @@ import org.mariotaku.twidere.model.MediaUploadResult;
import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.ParcelableStatusUpdate; import org.mariotaku.twidere.model.ParcelableStatusUpdate;
import org.mariotaku.twidere.model.UploaderMediaItem; import org.mariotaku.twidere.model.UploaderMediaItem;
import org.mariotaku.commons.logansquare.LoganSquareMapperFinder;
import org.mariotaku.twidere.model.UserKey; import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -54,7 +54,7 @@ public abstract class MediaUploaderService extends Service {
} }
protected abstract MediaUploadResult upload(ParcelableStatusUpdate status, protected abstract MediaUploadResult upload(ParcelableStatusUpdate status,
UserKey currentAccount, UploaderMediaItem[] media); UserKey currentAccount, UploaderMediaItem[] media);
protected abstract boolean callback(MediaUploadResult result, ParcelableStatus status); protected abstract boolean callback(MediaUploadResult result, ParcelableStatus status);
@ -74,14 +74,12 @@ public abstract class MediaUploaderService extends Service {
@Override @Override
public String upload(String statusJson, String currentAccount, String mediaJson) throws RemoteException { public String upload(String statusJson, String currentAccount, String mediaJson) throws RemoteException {
try { try {
final ParcelableStatusUpdate statusUpdate = LoganSquareMapperFinder.mapperFor(ParcelableStatusUpdate.class) final ParcelableStatusUpdate statusUpdate = JsonSerializer.parse(statusJson, ParcelableStatusUpdate.class);
.parse(statusJson); final List<UploaderMediaItem> media = JsonSerializer.parseList(mediaJson, UploaderMediaItem.class);
final List<UploaderMediaItem> media = LoganSquareMapperFinder.mapperFor(UploaderMediaItem.class)
.parseList(mediaJson);
final MediaUploadResult shorten = mService.get().upload(statusUpdate, final MediaUploadResult shorten = mService.get().upload(statusUpdate,
UserKey.valueOf(currentAccount), UserKey.valueOf(currentAccount),
media.toArray(new UploaderMediaItem[media.size()])); media.toArray(new UploaderMediaItem[media.size()]));
return LoganSquareMapperFinder.mapperFor(MediaUploadResult.class).serialize(shorten); return JsonSerializer.serialize(shorten, MediaUploadResult.class);
} catch (IOException e) { } catch (IOException e) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
throw new RemoteException(e.getMessage()); throw new RemoteException(e.getMessage());
@ -94,10 +92,8 @@ public abstract class MediaUploaderService extends Service {
@Override @Override
public boolean callback(String resultJson, String statusJson) throws RemoteException { public boolean callback(String resultJson, String statusJson) throws RemoteException {
try { try {
final MediaUploadResult result = LoganSquareMapperFinder.mapperFor(MediaUploadResult.class) final MediaUploadResult result = JsonSerializer.parse(resultJson, MediaUploadResult.class);
.parse(resultJson); final ParcelableStatus status = JsonSerializer.parse(statusJson, ParcelableStatus.class);
final ParcelableStatus status = LoganSquareMapperFinder.mapperFor(ParcelableStatus.class)
.parse(statusJson);
return mService.get().callback(result, status); return mService.get().callback(result, status);
} catch (IOException e) { } catch (IOException e) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {

View File

@ -28,11 +28,11 @@ import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import org.mariotaku.twidere.IStatusShortener; import org.mariotaku.twidere.IStatusShortener;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.ParcelableStatusUpdate; import org.mariotaku.twidere.model.ParcelableStatusUpdate;
import org.mariotaku.twidere.model.StatusShortenResult; import org.mariotaku.twidere.model.StatusShortenResult;
import org.mariotaku.commons.logansquare.LoganSquareMapperFinder; import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -51,8 +51,8 @@ public abstract class StatusShortenerService extends Service {
} }
protected abstract StatusShortenResult shorten(ParcelableStatusUpdate status, protected abstract StatusShortenResult shorten(ParcelableStatusUpdate status,
UserKey currentAccountKey, UserKey currentAccountKey,
String overrideStatusText); String overrideStatusText);
protected abstract boolean callback(StatusShortenResult result, ParcelableStatus status); protected abstract boolean callback(StatusShortenResult result, ParcelableStatus status);
@ -71,15 +71,15 @@ public abstract class StatusShortenerService extends Service {
@Override @Override
public String shorten(final String statusJson, final String currentAccountIdStr, public String shorten(final String statusJson, final String currentAccountIdStr,
final String overrideStatusText) final String overrideStatusText)
throws RemoteException { throws RemoteException {
try { try {
final ParcelableStatusUpdate statusUpdate = LoganSquareMapperFinder.mapperFor(ParcelableStatusUpdate.class) final ParcelableStatusUpdate statusUpdate = JsonSerializer.parse(statusJson,
.parse(statusJson); ParcelableStatusUpdate.class);
final UserKey currentAccountId = UserKey.valueOf(currentAccountIdStr); final UserKey currentAccountId = UserKey.valueOf(currentAccountIdStr);
final StatusShortenResult shorten = mService.get().shorten(statusUpdate, currentAccountId, final StatusShortenResult shorten = mService.get().shorten(statusUpdate, currentAccountId,
overrideStatusText); overrideStatusText);
return LoganSquareMapperFinder.mapperFor(StatusShortenResult.class).serialize(shorten); return JsonSerializer.serialize(shorten, StatusShortenResult.class);
} catch (IOException e) { } catch (IOException e) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
throw new RemoteException(e.getMessage()); throw new RemoteException(e.getMessage());
@ -92,10 +92,8 @@ public abstract class StatusShortenerService extends Service {
@Override @Override
public boolean callback(String resultJson, String statusJson) throws RemoteException { public boolean callback(String resultJson, String statusJson) throws RemoteException {
try { try {
final StatusShortenResult result = LoganSquareMapperFinder.mapperFor(StatusShortenResult.class) final StatusShortenResult result = JsonSerializer.parse(resultJson, StatusShortenResult.class);
.parse(resultJson); final ParcelableStatus status = JsonSerializer.parse(statusJson, ParcelableStatus.class);
final ParcelableStatus status = LoganSquareMapperFinder.mapperFor(ParcelableStatus.class)
.parse(statusJson);
return mService.get().callback(result, status); return mService.get().callback(result, status);
} catch (IOException e) { } catch (IOException e) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {

View File

@ -187,6 +187,8 @@ dependencies {
compile "com.github.mariotaku.CommonsLibrary:text-kotlin:${libVersions['MariotakuCommons']}" compile "com.github.mariotaku.CommonsLibrary:text-kotlin:${libVersions['MariotakuCommons']}"
compile "com.github.mariotaku:KPreferences:${libVersions['KPreferences']}" compile "com.github.mariotaku:KPreferences:${libVersions['KPreferences']}"
compile 'com.github.mariotaku:Chameleon:0.9.16' compile 'com.github.mariotaku:Chameleon:0.9.16'
compile 'com.github.mariotaku.QR-Code-generator:core:8bc3053cb5'
compile 'com.github.mariotaku.QR-Code-generator:android:8bc3053cb5'
compile "org.jetbrains.kotlin:kotlin-stdlib:${libVersions['Kotlin']}" compile "org.jetbrains.kotlin:kotlin-stdlib:${libVersions['Kotlin']}"
compile "nl.komponents.kovenant:kovenant:${libVersions['Kovenant']}" compile "nl.komponents.kovenant:kovenant:${libVersions['Kovenant']}"

View File

@ -21,7 +21,6 @@ package org.mariotaku.twidere.extension.text.twitter
import android.support.test.InstrumentationRegistry import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4 import android.support.test.runner.AndroidJUnit4
import com.bluelinelabs.logansquare.LoganSquare
import com.twitter.Extractor import com.twitter.Extractor
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
@ -30,6 +29,7 @@ import org.junit.runner.RunWith
import org.mariotaku.twidere.model.ParcelableStatus import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.ParcelableUserMention import org.mariotaku.twidere.model.ParcelableUserMention
import org.mariotaku.twidere.test.R import org.mariotaku.twidere.test.R
import org.mariotaku.twidere.util.JsonSerializer
/** /**
* Created by mariotaku on 2017/4/2. * Created by mariotaku on 2017/4/2.
@ -46,7 +46,7 @@ class ExtractorExtensionsTest {
// This is a tweet by @t_deyarmin, mentioning @nixcraft // This is a tweet by @t_deyarmin, mentioning @nixcraft
inReplyTo = context.resources.openRawResource(R.raw.parcelable_status_848051071444410368).use { inReplyTo = context.resources.openRawResource(R.raw.parcelable_status_848051071444410368).use {
LoganSquare.parse(it, ParcelableStatus::class.java) JsonSerializer.parse(it, ParcelableStatus::class.java)
} }
} }

View File

@ -2,13 +2,13 @@ package org.mariotaku.twidere.model.util
import android.support.test.InstrumentationRegistry import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4 import android.support.test.runner.AndroidJUnit4
import com.bluelinelabs.logansquare.LoganSquare
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mariotaku.microblog.library.twitter.model.Status import org.mariotaku.microblog.library.twitter.model.Status
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.test.R import org.mariotaku.twidere.test.R
import org.mariotaku.twidere.util.JsonSerializer
/** /**
* Created by mariotaku on 2017/1/4. * Created by mariotaku on 2017/1/4.
@ -23,13 +23,13 @@ class ParcelableStatusUtilsTest {
fun testFromStatus() { fun testFromStatus() {
val context = InstrumentationRegistry.getContext() val context = InstrumentationRegistry.getContext()
val status_8754050 = context.resources.openRawResource(R.raw.status_8754050).use { val status_8754050 = context.resources.openRawResource(R.raw.status_8754050).use {
val status = LoganSquare.parse(it, Status::class.java) val status = JsonSerializer.parse(it, Status::class.java)
return@use ParcelableStatusUtils.fromStatus(status, UserKey("1234567", "gnusocial.de"), return@use ParcelableStatusUtils.fromStatus(status, UserKey("1234567", "gnusocial.de"),
"statusnet", false) "statusnet", false)
} }
val status_9171447 = context.resources.openRawResource(R.raw.status_9171447).use { val status_9171447 = context.resources.openRawResource(R.raw.status_9171447).use {
val status = LoganSquare.parse(it, Status::class.java) val status = JsonSerializer.parse(it, Status::class.java)
return@use ParcelableStatusUtils.fromStatus(status, UserKey("1234567", "gnusocial.de"), return@use ParcelableStatusUtils.fromStatus(status, UserKey("1234567", "gnusocial.de"),
"statusnet", false) "statusnet", false)
} }

View File

@ -25,7 +25,6 @@ import android.content.Context
import android.util.Base64 import android.util.Base64
import android.util.Base64InputStream import android.util.Base64InputStream
import android.util.Base64OutputStream import android.util.Base64OutputStream
import com.bluelinelabs.logansquare.LoganSquare
import com.facebook.stetho.dumpapp.DumpException import com.facebook.stetho.dumpapp.DumpException
import com.facebook.stetho.dumpapp.DumperContext import com.facebook.stetho.dumpapp.DumperContext
import com.facebook.stetho.dumpapp.DumperPlugin import com.facebook.stetho.dumpapp.DumperPlugin
@ -46,6 +45,7 @@ import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.util.DataStoreUtils import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.JsonSerializer
import org.mariotaku.twidere.util.Utils import org.mariotaku.twidere.util.Utils
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
@ -205,7 +205,7 @@ class AccountsDumperPlugin(val context: Context) : DumperPlugin {
val path = args[1] val path = args[1]
docContext.set(path, value) docContext.set(path, value)
val details = docContext.read("$", Object::class.java)?.let { val details = docContext.read("$", Object::class.java)?.let {
LoganSquare.parse(it.toString(), AccountDetails::class.java) JsonSerializer.parse(it.toString(), AccountDetails::class.java)
} ?: return } ?: return
details.account.updateDetails(am, details) details.account.updateDetails(am, details)
dumpContext.stdout.println("$path = ${docContext.read(path, Object::class.java)?.prettyPrint()}") dumpContext.stdout.println("$path = ${docContext.read(path, Object::class.java)?.prettyPrint()}")
@ -234,7 +234,7 @@ class AccountsDumperPlugin(val context: Context) : DumperPlugin {
val gz = GZIPOutputStream(CipherOutputStream(base64, cipher)) val gz = GZIPOutputStream(CipherOutputStream(base64, cipher))
// write accounts // write accounts
val accounts = AccountUtils.getAllAccountDetails(this, true).toList() val accounts = AccountUtils.getAllAccountDetails(this, true).toList()
LoganSquare.serialize(accounts, gz, AccountDetails::class.java) JsonSerializer.serialize(accounts, gz, AccountDetails::class.java)
} }
private fun readAccounts(password: String, input: InputStream): List<AccountDetails> { private fun readAccounts(password: String, input: InputStream): List<AccountDetails> {
@ -248,7 +248,7 @@ class AccountsDumperPlugin(val context: Context) : DumperPlugin {
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, secret, IvParameterSpec(iv)) cipher.init(Cipher.DECRYPT_MODE, secret, IvParameterSpec(iv))
val gz = GZIPInputStream(CipherInputStream(base64, cipher)) val gz = GZIPInputStream(CipherInputStream(base64, cipher))
return LoganSquare.parseList(gz, AccountDetails::class.java) return JsonSerializer.parseList(gz, AccountDetails::class.java)
} }
private fun AccountManager.importAccounts(allDetails: List<AccountDetails>) { private fun AccountManager.importAccounts(allDetails: List<AccountDetails>) {
@ -289,9 +289,9 @@ class AccountsDumperPlugin(val context: Context) : DumperPlugin {
am.setUserData(this, ACCOUNT_USER_DATA_ACTIVATED, true.toString()) am.setUserData(this, ACCOUNT_USER_DATA_ACTIVATED, true.toString())
am.setUserData(this, ACCOUNT_USER_DATA_COLOR, toHexColor(details.color, format = HexColorFormat.RGB)) am.setUserData(this, ACCOUNT_USER_DATA_COLOR, toHexColor(details.color, format = HexColorFormat.RGB))
am.setUserData(this, ACCOUNT_USER_DATA_USER, LoganSquare.serialize(details.user)) am.setUserData(this, ACCOUNT_USER_DATA_USER, JsonSerializer.serialize(details.user))
am.setUserData(this, ACCOUNT_USER_DATA_EXTRAS, details.extras?.let { LoganSquare.serialize(it) }) am.setUserData(this, ACCOUNT_USER_DATA_EXTRAS, details.extras?.let { JsonSerializer.serialize(it) })
am.setAuthToken(this, ACCOUNT_AUTH_TOKEN_TYPE, LoganSquare.serialize(details.credentials)) am.setAuthToken(this, ACCOUNT_AUTH_TOKEN_TYPE, JsonSerializer.serialize(details.credentials))
} }
private fun AccountManager.docContext(forKey: String): DocumentContext { private fun AccountManager.docContext(forKey: String): DocumentContext {
@ -301,7 +301,7 @@ class AccountsDumperPlugin(val context: Context) : DumperPlugin {
.jsonProvider(JsonOrgJsonProvider()) .jsonProvider(JsonOrgJsonProvider())
.mappingProvider(AsIsMappingProvider()) .mappingProvider(AsIsMappingProvider())
.build() .build()
return JsonPath.parse(LoganSquare.serialize(details), configuration) return JsonPath.parse(JsonSerializer.serialize(details), configuration)
} }
private fun Any.prettyPrint() = if (this is JSONObject) { private fun Any.prettyPrint() = if (this is JSONObject) {

View File

@ -12,6 +12,8 @@ import org.mariotaku.twidere.util.JsonSerializer;
import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.dagger.DependencyHolder; import org.mariotaku.twidere.util.dagger.DependencyHolder;
import java.io.IOException;
import edu.tsinghua.hotmobi.HotMobiConstants; import edu.tsinghua.hotmobi.HotMobiConstants;
import edu.tsinghua.hotmobi.HotMobiLogger; import edu.tsinghua.hotmobi.HotMobiLogger;
import edu.tsinghua.hotmobi.model.LatLng; import edu.tsinghua.hotmobi.model.LatLng;
@ -30,7 +32,11 @@ public class LocationUtils implements HotMobiConstants, Constants {
DebugLog.d(HotMobiLogger.LOGTAG, "getting cached location", null); DebugLog.d(HotMobiLogger.LOGTAG, "getting cached location", null);
final Location location = Utils.getCachedLocation(appContext); final Location location = Utils.getCachedLocation(appContext);
if (location == null) { if (location == null) {
return JsonSerializer.parse(prefs.getString(KEY_FALLBACK_CACHED_LOCATION, null), LatLng.class); try {
return JsonSerializer.parse(prefs.getString(KEY_FALLBACK_CACHED_LOCATION, null), LatLng.class);
} catch (IOException e) {
return null;
}
} }
final LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude()); final LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
AsyncTask.execute(new Runnable() { AsyncTask.execute(new Runnable() {

View File

@ -1,95 +0,0 @@
/*
* QR Code generator library (Java)
*
* Copyright (c) Project Nayuki
* https://www.nayuki.io/page/qr-code-generator-library
*
* (MIT License)
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* - The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* - The Software is provided "as is", without warranty of any kind, express or
* implied, including but not limited to the warranties of merchantability,
* fitness for a particular purpose and noninfringement. In no event shall the
* authors or copyright holders be liable for any claim, damages or other
* liability, whether in an action of contract, tort or otherwise, arising from,
* out of or in connection with the Software or the use or other dealings in the
* Software.
*/
package io.nayuki.qrcodegen;
import android.support.annotation.NonNull;
import java.util.Arrays;
/**
* An appendable sequence of bits. Bits are packed in big endian within a byte.
*/
final class BitBuffer {
/*---- Fields ----*/
private byte[] data;
private int bitLength;
/*---- Constructor ----*/
// Creates an empty bit buffer (length 0).
public BitBuffer() {
data = new byte[16];
bitLength = 0;
}
/*---- Methods ----*/
// Returns the number of bits in the buffer, which is a non-negative value.
public int bitLength() {
return bitLength;
}
// Returns a copy of all bytes, padding up to the nearest byte.
public byte[] getBytes() {
return Arrays.copyOf(data, (bitLength + 7) / 8);
}
// Appends the given number of bits of the given value to this sequence.
// If 0 <= len <= 31, then this requires 0 <= val < 2^len.
public void appendBits(int val, int len) {
if (len < 0 || len > 32 || len < 32 && (val >>> len) != 0)
throw new IllegalArgumentException("Value out of range");
ensureCapacity(bitLength + len);
for (int i = len - 1; i >= 0; i--, bitLength++) // Append bit by bit
data[bitLength >>> 3] |= ((val >>> i) & 1) << (7 - (bitLength & 7));
}
// Appends the data of the given segment to this bit buffer.
public void appendData(@NonNull QrSegment seg) {
ensureCapacity(bitLength + seg.bitLength);
for (int i = 0; i < seg.bitLength; i++, bitLength++) { // Append bit by bit
int bit = (seg.getByte(i >>> 3) >>> (7 - (i & 7))) & 1;
data[bitLength >>> 3] |= bit << (7 - (bitLength & 7));
}
}
// Expands the buffer if necessary, so that it can hold at least the given bit length.
private void ensureCapacity(int newBitLen) {
while (data.length * 8 < newBitLen)
data = Arrays.copyOf(data, data.length * 2);
}
}

View File

@ -1,857 +0,0 @@
/*
* QR Code generator library (Java)
*
* Copyright (c) Project Nayuki
* https://www.nayuki.io/page/qr-code-generator-library
*
* (MIT License)
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* - The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* - The Software is provided "as is", without warranty of any kind, express or
* implied, including but not limited to the warranties of merchantability,
* fitness for a particular purpose and noninfringement. In no event shall the
* authors or copyright holders be liable for any claim, damages or other
* liability, whether in an action of contract, tort or otherwise, arising from,
* out of or in connection with the Software or the use or other dealings in the
* Software.
*/
package io.nayuki.qrcodegen;
import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Represents an immutable square grid of black and white cells for a QR Code symbol, and
* provides static functions to create a QR Code from user-supplied textual or binary data.
* <p>This class covers the QR Code model 2 specification, supporting all versions (sizes)
* from 1 to 40, all 4 error correction levels, and only 3 character encoding modes.</p>
*/
public final class QrCode {
/*---- Public static factory functions ----*/
/**
* Returns a QR Code symbol representing the specified Unicode text string at the specified error correction level.
* As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode
* code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output.
* The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.
*
* @param text the text to be encoded, which can be any Unicode string
* @param ecl the error correction level to use (will be boosted)
* @return a QR Code representing the text
* @throws NullPointerException if the text or error correction level is {@code null}
* @throws IllegalArgumentException if the text fails to fit in the largest version QR Code, which means it is too long
*/
public static QrCode encodeText(@NonNull String text, @NonNull Ecc ecl) {
List<QrSegment> segs = QrSegment.makeSegments(text);
return encodeSegments(segs, ecl);
}
/**
* Returns a QR Code symbol representing the specified binary data string at the specified error correction level.
* This function always encodes using the binary segment mode, not any text mode. The maximum number of
* bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.
* The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.
*
* @param data the binary data to encode
* @param ecl the error correction level to use (will be boosted)
* @return a QR Code representing the binary data
* @throws NullPointerException if the data or error correction level is {@code null}
* @throws IllegalArgumentException if the data fails to fit in the largest version QR Code, which means it is too long
*/
public static QrCode encodeBinary(@NonNull byte[] data, @NonNull Ecc ecl) {
QrSegment seg = QrSegment.makeBytes(data);
return encodeSegments(Collections.singletonList(seg), ecl);
}
/**
* Returns a QR Code symbol representing the specified data segments at the specified error correction
* level or higher. The smallest possible QR Code version is automatically chosen for the output.
* <p>This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and binary) to encode text more efficiently. This
* function is considered to be lower level than simply encoding text or binary data.</p>
*
* @param segs the segments to encode
* @param ecl the error correction level to use (will be boosted)
* @return a QR Code representing the segments
* @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null}
* @throws IllegalArgumentException if the data is too long to fit in the largest version QR Code at the ECL
*/
public static QrCode encodeSegments(List<QrSegment> segs, Ecc ecl) {
return encodeSegments(segs, ecl, 1, 40, -1, true);
}
/**
* Returns a QR Code symbol representing the specified data segments with the specified encoding parameters.
* The smallest possible QR Code version within the specified range is automatically chosen for the output.
* <p>This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and binary) to encode text more efficiently.
* This function is considered to be lower level than simply encoding text or binary data.</p>
*
* @param segs the segments to encode
* @param ecl the error correction level to use (may be boosted)
* @param minVersion the minimum allowed version of the QR symbol (at least 1)
* @param maxVersion the maximum allowed version of the QR symbol (at most 40)
* @param mask the mask pattern to use, which is either -1 for automatic choice or from 0 to 7 for fixed choice
* @param boostEcl increases the error correction level if it can be done without increasing the version number
* @return a QR Code representing the segments
* @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null}
* @throws IllegalArgumentException if 1 &le; minVersion &le; maxVersion &le; 40 is violated, or if mask
* &lt; &minus;1 or mask > 7, or if the data is too long to fit in a QR Code at maxVersion at the ECL
*/
public static QrCode encodeSegments(@NonNull List<QrSegment> segs, @NonNull Ecc ecl,
int minVersion, int maxVersion, int mask, boolean boostEcl) {
if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7)
throw new IllegalArgumentException("Invalid value");
// Find the minimal version number to use
int version, dataUsedBits;
for (version = minVersion; ; version++) {
int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available
dataUsedBits = QrSegment.getTotalBits(segs, version);
if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits)
break; // This version number is found to be suitable
if (version >= maxVersion) // All versions in the range could not fit the given data
throw new IllegalArgumentException("Data too long");
}
if (dataUsedBits == -1)
throw new AssertionError();
// Increase the error correction level while the data still fits in the current version number
for (Ecc newEcl : Ecc.values()) {
if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8)
ecl = newEcl;
}
// Create the data bit string by concatenating all segments
int dataCapacityBits = getNumDataCodewords(version, ecl) * 8;
BitBuffer bb = new BitBuffer();
for (QrSegment seg : segs) {
bb.appendBits(seg.mode.modeBits, 4);
bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version));
bb.appendData(seg);
}
// Add terminator and pad up to a byte if applicable
bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength()));
bb.appendBits(0, (8 - bb.bitLength() % 8) % 8);
// Pad with alternate bytes until data capacity is reached
for (int padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11)
bb.appendBits(padByte, 8);
if (bb.bitLength() % 8 != 0)
throw new AssertionError();
// Create the QR Code symbol
return new QrCode(version, ecl, bb.getBytes(), mask);
}
/*---- Instance fields ----*/
// Public immutable scalar parameters
/**
* This QR Code symbol's version number, which is always between 1 and 40 (inclusive).
*/
public final int version;
/**
* The width and height of this QR Code symbol, measured in modules.
* Always equal to version &times; 4 + 17, in the range 21 to 177.
*/
public final int size;
/**
* The error correction level used in this QR Code symbol. Never {@code null}.
*/
public final Ecc errorCorrectionLevel;
/**
* The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer).
* Note that even if a constructor was called with automatic masking requested
* (mask = -1), the resulting object will still have a mask value between 0 and 7.
*/
public final int mask;
// Private grids of modules/pixels (conceptually immutable)
private boolean[][] modules; // The modules of this QR Code symbol (false = white, true = black)
private boolean[][] isFunction; // Indicates function modules that are not subjected to masking
/*---- Constructors ----*/
/**
* Creates a new QR Code symbol with the specified version number, error correction level, binary data array, and mask number.
* <p>This is a cumbersome low-level constructor that should not be invoked directly by the user.
* To go one level up, see the {@link #encodeSegments(List, Ecc)} function.</p>
*
* @param ver the version number to use, which must be in the range 1 to 40, inclusive
* @param ecl the error correction level to use
* @param dataCodewords the raw binary user data to encode
* @param mask the mask pattern to use, which is either -1 for automatic choice or from 0 to 7 for fixed choice
* @throws NullPointerException if the byte array or error correction level is {@code null}
* @throws IllegalArgumentException if the version or mask value is out of range
*/
public QrCode(int ver, @NonNull Ecc ecl, @NonNull byte[] dataCodewords, int mask) {
// Check arguments
if (ver < 1 || ver > 40 || mask < -1 || mask > 7)
throw new IllegalArgumentException("Value out of range");
// Initialize fields
version = ver;
size = ver * 4 + 17;
errorCorrectionLevel = ecl;
modules = new boolean[size][size]; // Entirely white grid
isFunction = new boolean[size][size];
// Draw function patterns, draw all codewords, do masking
drawFunctionPatterns();
byte[] allCodewords = appendErrorCorrection(dataCodewords);
drawCodewords(allCodewords);
this.mask = handleConstructorMasking(mask);
}
/**
* Creates a new QR Code symbol based on the specified existing object, but with a potentially
* different mask pattern. The version, error correction level, codewords, etc. of the newly
* created object are all identical to the argument object; only the mask may differ.
*
* @param qr the existing QR Code to copy and modify
* @param mask the new mask pattern, 0 to 7 to force a fixed choice or -1 for an automatic choice
* @throws NullPointerException if the QR Code is {@code null}
* @throws IllegalArgumentException if the mask value is out of range
*/
public QrCode(@NonNull QrCode qr, int mask) {
// Check arguments
if (mask < -1 || mask > 7)
throw new IllegalArgumentException("Mask value out of range");
// Copy scalar fields
version = qr.version;
size = qr.size;
errorCorrectionLevel = qr.errorCorrectionLevel;
// Handle grid fields
isFunction = qr.isFunction; // Shallow copy because the data is read-only
modules = qr.modules.clone(); // Deep copy
for (int i = 0; i < modules.length; i++)
modules[i] = modules[i].clone();
// Handle masking
applyMask(qr.mask); // Undo old mask
this.mask = handleConstructorMasking(mask);
}
/*---- Public instance methods ----*/
/**
* Returns the color of the module (pixel) at the specified coordinates, which is either 0 for white or 1 for black. The top
* left corner has the coordinates (x=0, y=0). If the specified coordinates are out of bounds, then 0 (white) is returned.
*
* @param x the x coordinate, where 0 is the left edge and size&minus;1 is the right edge
* @param y the y coordinate, where 0 is the top edge and size&minus;1 is the bottom edge
* @return the module's color, which is either 0 (white) or 1 (black)
*/
public int getModule(int x, int y) {
if (0 <= x && x < size && 0 <= y && y < size)
return modules[y][x] ? 1 : 0;
else
return 0; // Infinite white border
}
/**
* Returns a new image object representing this QR Code, with the specified module scale and number
* of border modules. For example, the arguments scale=10, border=4 means to pad the QR Code symbol
* with 4 white border modules on all four edges, then use 10*10 pixels to represent each module.
* The resulting image only contains the hex colors 000000 and FFFFFF.
*
* @param scale the module scale factor, which must be positive
* @param border the number of border modules to add, which must be non-negative
* @return an image representing this QR Code, with padding and scaling
* @throws IllegalArgumentException if the scale or border is out of range
*/
public Bitmap toBitmap(int scale, int border) {
if (scale <= 0 || border < 0)
throw new IllegalArgumentException("Value out of range");
Bitmap result = Bitmap.createBitmap((size + border * 2) * scale, (size + border * 2) * scale,
Bitmap.Config.ARGB_8888);
for (int y = 0; y < result.getHeight(); y++) {
for (int x = 0; x < result.getWidth(); x++) {
int val = getModule(x / scale - border, y / scale - border); // 0 or 1
result.setPixel(x, y, val == 1 ? 0xFF000000 : 0xFFFFFFFF);
}
}
return result;
}
/*---- Private helper methods for constructor: Drawing function modules ----*/
private void drawFunctionPatterns() {
// Draw the horizontal and vertical timing patterns
for (int i = 0; i < size; i++) {
setFunctionModule(6, i, i % 2 == 0);
setFunctionModule(i, 6, i % 2 == 0);
}
// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
drawFinderPattern(3, 3);
drawFinderPattern(size - 4, 3);
drawFinderPattern(3, size - 4);
// Draw the numerous alignment patterns
int[] alignPatPos = getAlignmentPatternPositions(version);
int numAlign = alignPatPos.length;
for (int i = 0; i < numAlign; i++) {
for (int j = 0; j < numAlign; j++) {
if (i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0)
continue; // Skip the three finder corners
else
drawAlignmentPattern(alignPatPos[i], alignPatPos[j]);
}
}
// Draw configuration data
drawFormatBits(0); // Dummy mask value; overwritten later in the constructor
drawVersion();
}
// Draws two copies of the format bits (with its own error correction code)
// based on the given mask and this object's error correction level field.
private void drawFormatBits(int mask) {
// Calculate error correction code and pack bits
int data = errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3
int rem = data;
for (int i = 0; i < 10; i++)
rem = (rem << 1) ^ ((rem >>> 9) * 0x537);
data = data << 10 | rem;
data ^= 0x5412; // uint15
if (data >>> 15 != 0)
throw new AssertionError();
// Draw first copy
for (int i = 0; i <= 5; i++)
setFunctionModule(8, i, ((data >>> i) & 1) != 0);
setFunctionModule(8, 7, ((data >>> 6) & 1) != 0);
setFunctionModule(8, 8, ((data >>> 7) & 1) != 0);
setFunctionModule(7, 8, ((data >>> 8) & 1) != 0);
for (int i = 9; i < 15; i++)
setFunctionModule(14 - i, 8, ((data >>> i) & 1) != 0);
// Draw second copy
for (int i = 0; i <= 7; i++)
setFunctionModule(size - 1 - i, 8, ((data >>> i) & 1) != 0);
for (int i = 8; i < 15; i++)
setFunctionModule(8, size - 15 + i, ((data >>> i) & 1) != 0);
setFunctionModule(8, size - 8, true);
}
// Draws two copies of the version bits (with its own error correction code),
// based on this object's version field (which only has an effect for 7 <= version <= 40).
private void drawVersion() {
if (version < 7)
return;
// Calculate error correction code and pack bits
int rem = version; // version is uint6, in the range [7, 40]
for (int i = 0; i < 12; i++)
rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25);
int data = version << 12 | rem; // uint18
if (data >>> 18 != 0)
throw new AssertionError();
// Draw two copies
for (int i = 0; i < 18; i++) {
boolean bit = ((data >>> i) & 1) != 0;
int a = size - 11 + i % 3, b = i / 3;
setFunctionModule(a, b, bit);
setFunctionModule(b, a, bit);
}
}
// Draws a 9*9 finder pattern including the border separator, with the center module at (x, y).
private void drawFinderPattern(int x, int y) {
for (int i = -4; i <= 4; i++) {
for (int j = -4; j <= 4; j++) {
int dist = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm
int xx = x + j, yy = y + i;
if (0 <= xx && xx < size && 0 <= yy && yy < size)
setFunctionModule(xx, yy, dist != 2 && dist != 4);
}
}
}
// Draws a 5*5 alignment pattern, with the center module at (x, y).
private void drawAlignmentPattern(int x, int y) {
for (int i = -2; i <= 2; i++) {
for (int j = -2; j <= 2; j++)
setFunctionModule(x + j, y + i, Math.max(Math.abs(i), Math.abs(j)) != 1);
}
}
// Sets the color of a module and marks it as a function module.
// Only used by the constructor. Coordinates must be in range.
private void setFunctionModule(int x, int y, boolean isBlack) {
modules[y][x] = isBlack;
isFunction[y][x] = true;
}
/*---- Private helper methods for constructor: Codewords and masking ----*/
// Returns a new byte string representing the given data with the appropriate error correction
// codewords appended to it, based on this object's version and error correction level.
private byte[] appendErrorCorrection(byte[] data) {
if (data.length != getNumDataCodewords(version, errorCorrectionLevel))
throw new IllegalArgumentException();
// Calculate parameter numbers
int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal()][version];
int totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[errorCorrectionLevel.ordinal()][version];
if (totalEcc % numBlocks != 0)
throw new AssertionError();
int blockEccLen = totalEcc / numBlocks;
int numShortBlocks = numBlocks - getNumRawDataModules(version) / 8 % numBlocks;
int shortBlockLen = getNumRawDataModules(version) / 8 / numBlocks;
// Split data into blocks and append ECC to each block
byte[][] blocks = new byte[numBlocks][];
ReedSolomonGenerator rs = new ReedSolomonGenerator(blockEccLen);
for (int i = 0, k = 0; i < numBlocks; i++) {
byte[] dat = Arrays.copyOfRange(data, k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1));
byte[] block = Arrays.copyOf(dat, shortBlockLen + 1);
k += dat.length;
byte[] ecc = rs.getRemainder(dat);
System.arraycopy(ecc, 0, block, block.length - blockEccLen, ecc.length);
blocks[i] = block;
}
// Interleave (not concatenate) the bytes from every block into a single sequence
byte[] result = new byte[getNumRawDataModules(version) / 8];
for (int i = 0, k = 0; i < blocks[0].length; i++) {
for (int j = 0; j < blocks.length; j++) {
// Skip the padding byte in short blocks
if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) {
result[k] = blocks[j][i];
k++;
}
}
}
return result;
}
// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
// data area of this QR Code symbol. Function modules need to be marked off before this is called.
private void drawCodewords(@NonNull byte[] data) {
if (data.length != getNumRawDataModules(version) / 8)
throw new IllegalArgumentException();
int i = 0; // Bit index into the data
// Do the funny zigzag scan
for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair
if (right == 6)
right = 5;
for (int vert = 0; vert < size; vert++) { // Vertical counter
for (int j = 0; j < 2; j++) {
int x = right - j; // Actual x coordinate
boolean upwards = ((right & 2) == 0) ^ (x < 6);
int y = upwards ? size - 1 - vert : vert; // Actual y coordinate
if (!isFunction[y][x] && i < data.length * 8) {
modules[y][x] = ((data[i >>> 3] >>> (7 - (i & 7))) & 1) != 0;
i++;
}
// If there are any remainder bits (0 to 7), they are already
// set to 0/false/white when the grid of modules was initialized
}
}
}
if (i != data.length * 8)
throw new AssertionError();
}
// XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical
// properties, calling applyMask(m) twice with the same value is equivalent to no change at all.
// This means it is possible to apply a mask, undo it, and try another mask. Note that a final
// well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.).
private void applyMask(int mask) {
if (mask < 0 || mask > 7)
throw new IllegalArgumentException("Mask value out of range");
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
boolean invert;
switch (mask) {
case 0:
invert = (x + y) % 2 == 0;
break;
case 1:
invert = y % 2 == 0;
break;
case 2:
invert = x % 3 == 0;
break;
case 3:
invert = (x + y) % 3 == 0;
break;
case 4:
invert = (x / 3 + y / 2) % 2 == 0;
break;
case 5:
invert = x * y % 2 + x * y % 3 == 0;
break;
case 6:
invert = (x * y % 2 + x * y % 3) % 2 == 0;
break;
case 7:
invert = ((x + y) % 2 + x * y % 3) % 2 == 0;
break;
default:
throw new AssertionError();
}
modules[y][x] ^= invert & !isFunction[y][x];
}
}
}
// A messy helper function for the constructors. This QR Code must be in an unmasked state when this
// method is called. The given argument is the requested mask, which is -1 for auto or 0 to 7 for fixed.
// This method applies and returns the actual mask chosen, from 0 to 7.
private int handleConstructorMasking(int mask) {
if (mask == -1) { // Automatically choose best mask
int minPenalty = Integer.MAX_VALUE;
for (int i = 0; i < 8; i++) {
drawFormatBits(i);
applyMask(i);
int penalty = getPenaltyScore();
if (penalty < minPenalty) {
mask = i;
minPenalty = penalty;
}
applyMask(i); // Undoes the mask due to XOR
}
}
if (mask < 0 || mask > 7)
throw new AssertionError();
drawFormatBits(mask); // Overwrite old format bits
applyMask(mask); // Apply the final choice of mask
return mask; // The caller shall assign this value to the final-declared field
}
// Calculates and returns the penalty score based on state of this QR Code's current modules.
// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
private int getPenaltyScore() {
int result = 0;
// Adjacent modules in row having same color
for (int y = 0; y < size; y++) {
boolean colorX = modules[y][0];
for (int x = 1, runX = 1; x < size; x++) {
if (modules[y][x] != colorX) {
colorX = modules[y][x];
runX = 1;
} else {
runX++;
if (runX == 5)
result += PENALTY_N1;
else if (runX > 5)
result++;
}
}
}
// Adjacent modules in column having same color
for (int x = 0; x < size; x++) {
boolean colorY = modules[0][x];
for (int y = 1, runY = 1; y < size; y++) {
if (modules[y][x] != colorY) {
colorY = modules[y][x];
runY = 1;
} else {
runY++;
if (runY == 5)
result += PENALTY_N1;
else if (runY > 5)
result++;
}
}
}
// 2*2 blocks of modules having same color
for (int y = 0; y < size - 1; y++) {
for (int x = 0; x < size - 1; x++) {
boolean color = modules[y][x];
if (color == modules[y][x + 1] &&
color == modules[y + 1][x] &&
color == modules[y + 1][x + 1])
result += PENALTY_N2;
}
}
// Finder-like pattern in rows
for (int y = 0; y < size; y++) {
for (int x = 0, bits = 0; x < size; x++) {
bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0);
if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated
result += PENALTY_N3;
}
}
// Finder-like pattern in columns
for (int x = 0; x < size; x++) {
for (int y = 0, bits = 0; y < size; y++) {
bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0);
if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated
result += PENALTY_N3;
}
}
// Balance of black and white modules
int black = 0;
for (boolean[] row : modules) {
for (boolean color : row) {
if (color)
black++;
}
}
int total = size * size;
// Find smallest k such that (45-5k)% <= dark/total <= (55+5k)%
for (int k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++)
result += PENALTY_N4;
return result;
}
/*---- Private static helper functions ----*/
// Returns a set of positions of the alignment patterns in ascending order. These positions are
// used on both the x and y axes. Each value in the resulting array is in the range [0, 177).
// This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes.
private static int[] getAlignmentPatternPositions(int ver) {
if (ver < 1 || ver > 40)
throw new IllegalArgumentException("Version number out of range");
else if (ver == 1)
return new int[]{};
else {
int numAlign = ver / 7 + 2;
int step;
if (ver != 32)
step = (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2
else // C-C-C-Combo breaker!
step = 26;
int[] result = new int[numAlign];
int size = ver * 4 + 17;
result[0] = 6;
for (int i = result.length - 1, pos = size - 7; i >= 1; i--, pos -= step)
result[i] = pos;
return result;
}
}
// Returns the number of raw data modules (bits) available at the given version number.
// These data modules are used for both user data codewords and error correction codewords.
// This stateless pure function could be implemented as a 40-entry lookup table.
private static int getNumRawDataModules(int ver) {
if (ver < 1 || ver > 40)
throw new IllegalArgumentException("Version number out of range");
int size = ver * 4 + 17;
int result = size * size; // Number of modules in the whole QR symbol square
result -= 64 * 3; // Subtract the three finders with separators
result -= 15 * 2 + 1; // Subtract the format information and black module
result -= (size - 16) * 2; // Subtract the timing patterns
// The five lines above are equivalent to: int result = (16 * ver + 128) * ver + 64;
if (ver >= 2) {
int numAlign = ver / 7 + 2;
result -= (numAlign - 1) * (numAlign - 1) * 25; // Subtract alignment patterns not overlapping with timing patterns
result -= (numAlign - 2) * 2 * 20; // Subtract alignment patterns that overlap with timing patterns
// The two lines above are equivalent to: result -= (25 * numAlign - 10) * numAlign - 55;
if (ver >= 7)
result -= 18 * 2; // Subtract version information
}
return result;
}
// Returns the number of 8-bit data (i.e. not error correction) codewords contained in any
// QR Code of the given version number and error correction level, with remainder bits discarded.
// This stateless pure function could be implemented as a (40*4)-cell lookup table.
static int getNumDataCodewords(int ver, Ecc ecl) {
if (ver < 1 || ver > 40)
throw new IllegalArgumentException("Version number out of range");
return getNumRawDataModules(ver) / 8 - NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal()][ver];
}
/*---- Private tables of constants ----*/
// For use in getPenaltyScore(), when evaluating which mask is best.
private static final int PENALTY_N1 = 3;
private static final int PENALTY_N2 = 3;
private static final int PENALTY_N3 = 40;
private static final int PENALTY_N4 = 10;
private static final short[][] NUM_ERROR_CORRECTION_CODEWORDS = {
// Version: (note that index 0 is for padding, and is set to an illegal value)
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
{-1, 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low
{-1, 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium
{-1, 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile
{-1, 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High
};
private static final byte[][] NUM_ERROR_CORRECTION_BLOCKS = {
// Version: (note that index 0 is for padding, and is set to an illegal value)
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
{-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low
{-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium
{-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile
{-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High
};
/*---- Public helper enumeration ----*/
/**
* Represents the error correction level used in a QR Code symbol.
*/
public enum Ecc {
// These enum constants must be declared in ascending order of error protection,
// for the sake of the implicit ordinal() method and values() function.
LOW(1), MEDIUM(0), QUARTILE(3), HIGH(2);
// In the range 0 to 3 (unsigned 2-bit integer).
final int formatBits;
// Constructor.
private Ecc(int fb) {
formatBits = fb;
}
}
/*---- Private helper class ----*/
/**
* Computes the Reed-Solomon error correction codewords for a sequence of data codewords
* at a given degree. Objects are immutable, and the state only depends on the degree.
* This class exists because the divisor polynomial does not need to be recalculated for every input.
*/
private static final class ReedSolomonGenerator {
/*-- Immutable field --*/
// Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which
// is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}.
private final byte[] coefficients;
/*-- Constructor --*/
/**
* Creates a Reed-Solomon ECC generator for the specified degree. This could be implemented
* as a lookup table over all possible parameter values, instead of as an algorithm.
*
* @param degree the divisor polynomial degree, which must be between 1 and 255
* @throws IllegalArgumentException if degree &lt; 1 or degree > 255
*/
public ReedSolomonGenerator(int degree) {
if (degree < 1 || degree > 255)
throw new IllegalArgumentException("Degree out of range");
// Start with the monomial x^0
coefficients = new byte[degree];
coefficients[degree - 1] = 1;
// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
// drop the highest term, and store the rest of the coefficients in order of descending powers.
// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
int root = 1;
for (int i = 0; i < degree; i++) {
// Multiply the current product by (x - r^i)
for (int j = 0; j < coefficients.length; j++) {
coefficients[j] = (byte) multiply(coefficients[j] & 0xFF, root);
if (j + 1 < coefficients.length)
coefficients[j] ^= coefficients[j + 1];
}
root = (root << 1) ^ ((root >>> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D)
}
}
/*-- Method --*/
/**
* Computes and returns the Reed-Solomon error correction codewords for the specified sequence of data codewords.
* The returned object is always a new byte array. This method does not alter this object's state (because it is immutable).
*
* @param data the sequence of data codewords
* @return the Reed-Solomon error correction codewords
* @throws NullPointerException if the data is {@code null}
*/
public byte[] getRemainder(@NonNull byte[] data) {
// Compute the remainder by performing polynomial division
byte[] result = new byte[coefficients.length];
for (byte b : data) {
int factor = (b ^ result[0]) & 0xFF;
System.arraycopy(result, 1, result, 0, result.length - 1);
result[result.length - 1] = 0;
for (int i = 0; i < result.length; i++)
result[i] ^= multiply(coefficients[i] & 0xFF, factor);
}
return result;
}
/*-- Static function --*/
// Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result
// are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8.
private static int multiply(int x, int y) {
if (x >>> 8 != 0 || y >>> 8 != 0)
throw new IllegalArgumentException("Byte out of range");
// Russian peasant multiplication
int z = 0;
for (int i = 7; i >= 0; i--) {
z = (z << 1) ^ ((z >>> 7) * 0x11D);
z ^= ((y >>> i) & 1) * x;
}
if (z >>> 8 != 0)
throw new AssertionError();
return z;
}
}
}

View File

@ -1,287 +0,0 @@
/*
* QR Code generator library (Java)
*
* Copyright (c) Project Nayuki
* https://www.nayuki.io/page/qr-code-generator-library
*
* (MIT License)
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* - The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* - The Software is provided "as is", without warranty of any kind, express or
* implied, including but not limited to the warranties of merchantability,
* fitness for a particular purpose and noninfringement. In no event shall the
* authors or copyright holders be liable for any claim, damages or other
* liability, whether in an action of contract, tort or otherwise, arising from,
* out of or in connection with the Software or the use or other dealings in the
* Software.
*/
package io.nayuki.qrcodegen;
import android.support.annotation.NonNull;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
/**
* Represents a character string to be encoded in a QR Code symbol. Each segment has
* a mode, and a sequence of characters that is already encoded as a sequence of bits.
* Instances of this class are immutable.
* <p>This segment class imposes no length restrictions, but QR Codes have restrictions.
* Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
* Any segment longer than this is meaningless for the purpose of generating QR Codes.</p>
*/
public final class QrSegment {
/*---- Static factory functions ----*/
/**
* Returns a segment representing the specified binary data encoded in byte mode.
*
* @param data the binary data
* @return a segment containing the data
* @throws NullPointerException if the array is {@code null}
*/
public static QrSegment makeBytes(@NonNull byte[] data) {
return new QrSegment(Mode.BYTE, data.length, data, data.length * 8);
}
/**
* Returns a segment representing the specified string of decimal digits encoded in numeric mode.
*
* @param digits a string consisting of digits from 0 to 9
* @return a segment containing the data
* @throws NullPointerException if the string is {@code null}
* @throws IllegalArgumentException if the string contains non-digit characters
*/
public static QrSegment makeNumeric(@NonNull String digits) {
if (!NUMERIC_REGEX.matcher(digits).matches())
throw new IllegalArgumentException("String contains non-numeric characters");
BitBuffer bb = new BitBuffer();
int i;
for (i = 0; i + 3 <= digits.length(); i += 3) // Process groups of 3
bb.appendBits(Integer.parseInt(digits.substring(i, i + 3)), 10);
int rem = digits.length() - i;
if (rem > 0) // 1 or 2 digits remaining
bb.appendBits(Integer.parseInt(digits.substring(i)), rem * 3 + 1);
return new QrSegment(Mode.NUMERIC, digits.length(), bb.getBytes(), bb.bitLength());
}
/**
* Returns a segment representing the specified text string encoded in alphanumeric mode. The characters allowed are:
* 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
*
* @param text a string of text, with only certain characters allowed
* @return a segment containing the data
* @throws NullPointerException if the string is {@code null}
* @throws IllegalArgumentException if the string contains non-encodable characters
*/
public static QrSegment makeAlphanumeric(@NonNull String text) {
if (!ALPHANUMERIC_REGEX.matcher(text).matches())
throw new IllegalArgumentException("String contains unencodable characters in alphanumeric mode");
BitBuffer bb = new BitBuffer();
int i;
for (i = 0; i + 2 <= text.length(); i += 2) { // Process groups of 2
int temp = ALPHANUMERIC_ENCODING_TABLE[text.charAt(i) - ' '] * 45;
temp += ALPHANUMERIC_ENCODING_TABLE[text.charAt(i + 1) - ' '];
bb.appendBits(temp, 11);
}
if (i < text.length()) // 1 character remaining
bb.appendBits(ALPHANUMERIC_ENCODING_TABLE[text.charAt(i) - ' '], 6);
return new QrSegment(Mode.ALPHANUMERIC, text.length(), bb.getBytes(), bb.bitLength());
}
/**
* Returns a new mutable list of zero or more segments to represent the specified Unicode text string.
* The result may use various segment modes and switch modes to optimize the length of the bit stream.
*
* @param text the text to be encoded, which can be any Unicode string
* @return a list of segments containing the text
* @throws NullPointerException if the text is {@code null}
*/
public static List<QrSegment> makeSegments(@NonNull String text) {
// Select the most efficient segment encoding automatically
List<QrSegment> result = new ArrayList<>();
if (text.equals(""))
return result;
else if (NUMERIC_REGEX.matcher(text).matches())
result.add(makeNumeric(text));
else if (ALPHANUMERIC_REGEX.matcher(text).matches())
result.add(makeAlphanumeric(text));
else
result.add(makeBytes(text.getBytes(Charset.forName("UTF-8"))));
return result;
}
/*---- Instance fields ----*/
/**
* The mode indicator for this segment. Never {@code null}.
*/
public final Mode mode;
/**
* The length of this segment's unencoded data, measured in characters. Always zero or positive.
*/
public final int numChars;
/**
* The bits of this segment packed into a byte array in big endian. Accessed through {@link #getByte(int)}. Not {@code null}.
*/
private final byte[] data;
/**
* The length of this segment's encoded data, measured in bits. Satisfies 0 &le; {@code bitLength} &le; {@code data.length} &times; 8.
*/
public final int bitLength;
/*---- Constructor ----*/
/**
* Creates a new QR Code data segment with the specified parameters and data.
*
* @param md the mode, which is not {@code null}
* @param numCh the data length in characters, which is non-negative
* @param bitLen the data length in bits, which is non-negative
* @param b the bits packed into bytes, which is not {@code null}
* @throws NullPointerException if the mode or array is {@code null}
* @throws IllegalArgumentException if the character count or bit length are negative or invalid
*/
public QrSegment(@NonNull Mode md, int numCh, @NonNull byte[] b, int bitLen) {
if (numCh < 0 || bitLen < 0 || bitLen > b.length * 8L)
throw new IllegalArgumentException("Invalid value");
mode = md;
numChars = numCh;
data = Arrays.copyOf(b, (bitLen + 7) / 8); // Trim to precise length and also make defensive copy
bitLength = bitLen;
}
/*---- Method ----*/
/**
* Returns the data byte at the specified index.
*
* @param index the index to retrieve from, satisfying 0 &le; {@code index} &lt; ceil({@code bitLength} &divide; 8)
* @return the data byte at the specified index
* @throws IndexOutOfBoundsException if the index is out of bounds
*/
public byte getByte(int index) {
if (index < 0 || index > data.length)
throw new IndexOutOfBoundsException();
return data[index];
}
// Package-private helper function.
static int getTotalBits(@NonNull List<QrSegment> segs, int version) {
if (version < 1 || version > 40)
throw new IllegalArgumentException("Version number out of range");
int result = 0;
for (QrSegment seg : segs) {
int ccbits = seg.mode.numCharCountBits(version);
// Fail if segment length value doesn't fit in the length field's bit-width
if (seg.numChars >= (1 << ccbits))
return -1;
result += 4 + ccbits + seg.bitLength;
}
return result;
}
/*---- Constants ----*/
/**
* Can test whether a string is encodable in numeric mode (such as by using {@link #makeNumeric(String)}).
*/
public static final Pattern NUMERIC_REGEX = Pattern.compile("[0-9]*");
/**
* Can test whether a string is encodable in alphanumeric mode (such as by using {@link #makeAlphanumeric(String)}).
*/
public static final Pattern ALPHANUMERIC_REGEX = Pattern.compile("[A-Z0-9 $%*+./:-]*");
/**
* Maps shifted ASCII codes to alphanumeric mode character codes.
*/
private static final byte[] ALPHANUMERIC_ENCODING_TABLE = {
// SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, // ASCII codes 32 to 64
36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, // Array indices 0 to 32
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, // Array indices 33 to 58
// A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, // ASCII codes 65 to 90
};
/*---- Public helper enumeration ----*/
/**
* The mode field of a segment. Immutable. Provides methods to retrieve closely related values.
*/
public enum Mode {
/*-- Constants --*/
NUMERIC(0x1, 10, 12, 14),
ALPHANUMERIC(0x2, 9, 11, 13),
BYTE(0x4, 8, 16, 16),
KANJI(0x8, 8, 10, 12);
/*-- Fields --*/
/**
* An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object.
*/
final int modeBits;
private final int[] numBitsCharCount;
/*-- Constructor --*/
private Mode(int mode, int... ccbits) {
this.modeBits = mode;
numBitsCharCount = ccbits;
}
/*-- Method --*/
/**
* Returns the bit width of the segment character count field for this mode object at the specified version number.
*
* @param ver the version number, which is between 1 to 40, inclusive
* @return the number of bits for the character count, which is between 8 to 16, inclusive
* @throws IllegalArgumentException if the version number is out of range
*/
int numCharCountBits(int ver) {
if (1 <= ver && ver <= 9) return numBitsCharCount[0];
else if (10 <= ver && ver <= 26) return numBitsCharCount[1];
else if (27 <= ver && ver <= 40) return numBitsCharCount[2];
else throw new IllegalArgumentException("Version number out of range");
}
}
}

View File

@ -23,11 +23,9 @@ import android.support.annotation.NonNull;
import android.support.v4.util.SimpleArrayMap; import android.support.v4.util.SimpleArrayMap;
import com.bluelinelabs.logansquare.Commons_ParameterizedTypeAccessor; import com.bluelinelabs.logansquare.Commons_ParameterizedTypeAccessor;
import com.bluelinelabs.logansquare.JsonMapper;
import com.bluelinelabs.logansquare.ParameterizedType; import com.bluelinelabs.logansquare.ParameterizedType;
import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParseException;
import org.mariotaku.commons.logansquare.LoganSquareMapperFinder;
import org.mariotaku.microblog.library.twitter.model.TwitterResponse; import org.mariotaku.microblog.library.twitter.model.TwitterResponse;
import org.mariotaku.restfu.RestConverter; import org.mariotaku.restfu.RestConverter;
import org.mariotaku.restfu.http.ContentType; import org.mariotaku.restfu.http.ContentType;
@ -35,6 +33,7 @@ import org.mariotaku.restfu.http.HttpResponse;
import org.mariotaku.restfu.http.mime.Body; import org.mariotaku.restfu.http.mime.Body;
import org.mariotaku.restfu.http.mime.SimpleBody; import org.mariotaku.restfu.http.mime.SimpleBody;
import org.mariotaku.restfu.http.mime.StringBody; import org.mariotaku.restfu.http.mime.StringBody;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@ -77,14 +76,13 @@ public class LoganSquareConverterFactory<E extends Exception> extends RestConver
try { try {
final Object parsed; final Object parsed;
if (type.rawType == List.class) { if (type.rawType == List.class) {
final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(type.typeParameters.get(0).rawType); final Class cls = type.typeParameters.get(0).rawType;
parsed = mapper.parseList(response.getBody().stream()); parsed = JsonSerializer.parseList(response.getBody().stream(), cls);
} else if (type.rawType == Map.class) { } else if (type.rawType == Map.class) {
final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(type.typeParameters.get(1).rawType); final Class cls = type.typeParameters.get(1).rawType;
parsed = mapper.parseMap(response.getBody().stream()); parsed = JsonSerializer.parseMap(response.getBody().stream(), cls);
} else { } else {
final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(type); parsed = JsonSerializer.parse(response.getBody().stream(), type);
parsed = mapper.parse(response.getBody().stream());
} }
if (parsed == null) { if (parsed == null) {
throw new IOException("Empty data"); throw new IOException("Empty data");
@ -123,17 +121,16 @@ public class LoganSquareConverterFactory<E extends Exception> extends RestConver
public Body convert(Object request) throws IOException, ConvertException, E { public Body convert(Object request) throws IOException, ConvertException, E {
final String json; final String json;
if (type.rawType == List.class) { if (type.rawType == List.class) {
final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(type.typeParameters.get(0).rawType); final Class<?> cls = type.typeParameters.get(0).rawType;
//noinspection unchecked //noinspection unchecked
json = mapper.serialize((List) request); json = JsonSerializer.serializeList((List) request, cls);
} else if (type.rawType == Map.class) { } else if (type.rawType == Map.class) {
final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(type.typeParameters.get(1).rawType); final Class<?> cls = type.typeParameters.get(1).rawType;
//noinspection unchecked //noinspection unchecked
json = mapper.serialize((Map) request); json = JsonSerializer.serializeMap((Map) request, cls);
} else { } else {
final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(type);
//noinspection unchecked //noinspection unchecked
json = mapper.serialize(request); json = JsonSerializer.serialize(request, (ParameterizedType) type);
} }
return new StringBody(json, ContentType.parse("application/json")); return new StringBody(json, ContentType.parse("application/json"));
} }

View File

@ -30,13 +30,11 @@ import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread; import android.support.annotation.WorkerThread;
import android.util.Log; import android.util.Log;
import com.bluelinelabs.logansquare.JsonMapper;
import com.bluelinelabs.logansquare.LoganSquare; import com.bluelinelabs.logansquare.LoganSquare;
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.JsonToken;
import org.mariotaku.commons.logansquare.LoganSquareMapperFinder;
import org.mariotaku.library.objectcursor.ObjectCursor; import org.mariotaku.library.objectcursor.ObjectCursor;
import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.annotation.Preference; import org.mariotaku.twidere.annotation.Preference;
@ -331,8 +329,7 @@ public class DataImportExportUtils implements Constants {
throws IOException { throws IOException {
final ZipEntry entry = zipFile.getEntry(entryName); final ZipEntry entry = zipFile.getEntry(entryName);
if (entry == null) return; if (entry == null) return;
final JsonMapper<T> mapper = LoganSquareMapperFinder.mapperFor(itemCls); List<T> itemsList = JsonSerializer.parseList(zipFile.getInputStream(entry), itemCls);
List<T> itemsList = mapper.parseList(zipFile.getInputStream(entry));
strategy.importItem(context.getContentResolver(), itemsList); strategy.importItem(context.getContentResolver(), itemsList);
} }
@ -342,9 +339,7 @@ public class DataImportExportUtils implements Constants {
@NonNull final Class<T> itemCls, @NonNull final Class<T> itemCls,
@NonNull final List<T> itemList) throws IOException { @NonNull final List<T> itemList) throws IOException {
zos.putNextEntry(new ZipEntry(entryName)); zos.putNextEntry(new ZipEntry(entryName));
final JsonGenerator jsonGenerator = LoganSquare.JSON_FACTORY.createGenerator(zos); JsonSerializer.serialize(itemList, zos, itemCls);
LoganSquareMapperFinder.mapperFor(itemCls).serialize(itemList, jsonGenerator);
jsonGenerator.flush();
zos.closeEntry(); zos.closeEntry();
} }
@ -356,8 +351,7 @@ public class DataImportExportUtils implements Constants {
throws IOException { throws IOException {
final ZipEntry entry = zipFile.getEntry(entryName); final ZipEntry entry = zipFile.getEntry(entryName);
if (entry == null) return; if (entry == null) return;
final JsonMapper<T> mapper = LoganSquareMapperFinder.mapperFor(itemCls); T item = JsonSerializer.parse(zipFile.getInputStream(entry), itemCls);
T item = mapper.parse(zipFile.getInputStream(entry));
strategy.importItem(context.getContentResolver(), item); strategy.importItem(context.getContentResolver(), item);
} }
@ -367,9 +361,7 @@ public class DataImportExportUtils implements Constants {
@NonNull final Class<T> itemCls, @NonNull final Class<T> itemCls,
@NonNull final T item) throws IOException { @NonNull final T item) throws IOException {
zos.putNextEntry(new ZipEntry(entryName)); zos.putNextEntry(new ZipEntry(entryName));
final JsonGenerator jsonGenerator = LoganSquare.JSON_FACTORY.createGenerator(zos); JsonSerializer.serialize(item, zos, itemCls);
LoganSquareMapperFinder.mapperFor(itemCls).serialize(item, jsonGenerator, true);
jsonGenerator.flush();
zos.closeEntry(); zos.closeEntry();
} }

View File

@ -1,152 +0,0 @@
/*
* 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.util;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.JsonMapper;
import org.mariotaku.commons.logansquare.LoganSquareMapperFinder;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Created by mariotaku on 15/8/6.
*/
public class JsonSerializer {
private JsonSerializer() {
}
@Nullable
public static <T> String serialize(@Nullable final List<T> list, final Class<T> cls) {
if (list == null) return null;
try {
return LoganSquareMapperFinder.mapperFor(cls).serialize(list);
} catch (IOException e) {
return null;
}
}
@Nullable
public static <T> String serialize(@Nullable final Map<String, T> list, final Class<T> cls) {
if (list == null) return null;
try {
return LoganSquareMapperFinder.mapperFor(cls).serialize(list);
} catch (IOException e) {
return null;
}
}
@Nullable
public static <T> String serialize(@Nullable final T[] array, final Class<T> cls) {
if (array == null) return null;
try {
return LoganSquareMapperFinder.mapperFor(cls).serialize(Arrays.asList(array));
} catch (IOException e) {
return null;
}
}
@Nullable
public static <T> String serialize(@Nullable final T object, final Class<T> cls) {
if (object == null) return null;
try {
return LoganSquareMapperFinder.mapperFor(cls).serialize(object);
} catch (IOException e) {
return null;
}
}
@Nullable
public static <T> String serialize(@Nullable final T object) {
if (object == null) return null;
try {
//noinspection unchecked
final JsonMapper<T> mapper = (JsonMapper<T>)
LoganSquareMapperFinder.mapperFor(object.getClass());
return mapper.serialize(object);
} catch (IOException e) {
return null;
}
}
@Nullable
public static <T> T[] parseArray(@Nullable final String string, final Class<T> cls) {
if (string == null) return null;
try {
final List<T> list = LoganSquareMapperFinder.mapperFor(cls).parseList(string);
//noinspection unchecked
return list.toArray((T[]) Array.newInstance(cls, list.size()));
} catch (IOException e) {
return null;
}
}
@Nullable
public static <T> T parse(@Nullable final String string, final Class<T> cls) {
if (string == null) return null;
try {
return LoganSquareMapperFinder.mapperFor(cls).parse(string);
} catch (IOException e) {
return null;
}
}
public static <E> List<E> parseList(File file, Class<E> cls) {
FileInputStream is = null;
//noinspection TryFinallyCanBeTryWithResources
try {
is = new FileInputStream(file);
return parseList(is, cls);
} catch (IOException e) {
return null;
} finally {
Utils.closeSilently(is);
}
}
public static <E> List<E> parseList(InputStream stream, Class<E> cls) {
try {
return LoganSquareMapperFinder.mapperFor(cls).parseList(stream);
} catch (IOException e) {
return null;
}
}
@Nullable
public static <E> List<E> parseList(@Nullable String json, Class<E> cls) {
if (json == null) return null;
//noinspection TryFinallyCanBeTryWithResources
try {
return LoganSquareMapperFinder.mapperFor(cls).parseList(json);
} catch (IOException e) {
return null;
}
}
}

View File

@ -35,7 +35,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.location.Location; import android.location.Location;
@ -68,7 +67,6 @@ import android.view.Gravity;
import android.view.KeyCharacterMap; import android.view.KeyCharacterMap;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
@ -90,9 +88,7 @@ import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.annotation.CustomTabType; import org.mariotaku.twidere.annotation.CustomTabType;
import org.mariotaku.twidere.extension.model.AccountDetailsExtensionsKt; import org.mariotaku.twidere.extension.model.AccountDetailsExtensionsKt;
import org.mariotaku.twidere.graphic.PaddingDrawable;
import org.mariotaku.twidere.model.AccountDetails; import org.mariotaku.twidere.model.AccountDetails;
import org.mariotaku.twidere.model.AccountPreferences;
import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.ParcelableUserMention; import org.mariotaku.twidere.model.ParcelableUserMention;
import org.mariotaku.twidere.model.PebbleMessage; import org.mariotaku.twidere.model.PebbleMessage;
@ -137,50 +133,6 @@ public final class Utils implements Constants {
throw new AssertionError("You are trying to create an instance for this utility class!"); throw new AssertionError("You are trying to create an instance for this utility class!");
} }
public static void addIntentToMenuForExtension(final Context context, final Menu menu,
final int groupId, final String action,
final String parcelableKey, final String parcelableJSONKey,
final Parcelable parcelable) {
if (context == null || menu == null || action == null || parcelableKey == null || parcelable == null)
return;
final PackageManager pm = context.getPackageManager();
final Resources res = context.getResources();
final float density = res.getDisplayMetrics().density;
final int padding = Math.round(density * 4);
final Intent queryIntent = new Intent(action);
queryIntent.setExtrasClassLoader(context.getClassLoader());
final List<ResolveInfo> activities = pm.queryIntentActivities(queryIntent, PackageManager.GET_META_DATA);
final String parcelableJson = JsonSerializer.serialize(parcelable);
for (final ResolveInfo info : activities) {
final Intent intent = new Intent(queryIntent);
if (isExtensionUseJSON(info) && parcelableJson != null) {
intent.putExtra(parcelableJSONKey, parcelableJson);
} else {
intent.putExtra(parcelableKey, parcelable);
}
intent.setClassName(info.activityInfo.packageName, info.activityInfo.name);
final MenuItem item = menu.add(groupId, Menu.NONE, Menu.NONE, info.loadLabel(pm));
item.setIntent(intent);
final Drawable metaDataDrawable = getMetadataDrawable(pm, info.activityInfo, METADATA_KEY_EXTENSION_ICON);
final int actionIconColor = ThemeUtils.getThemeForegroundColor(context);
if (metaDataDrawable != null) {
metaDataDrawable.mutate();
metaDataDrawable.setColorFilter(actionIconColor, Mode.SRC_ATOP);
item.setIcon(metaDataDrawable);
} else {
final Drawable icon = info.loadIcon(pm);
final int iw = icon.getIntrinsicWidth(), ih = icon.getIntrinsicHeight();
if (iw > 0 && ih > 0) {
final Drawable iconWithPadding = new PaddingDrawable(icon, padding);
iconWithPadding.setBounds(0, 0, iw, ih);
item.setIcon(iconWithPadding);
} else {
item.setIcon(icon);
}
}
}
}
public static void announceForAccessibilityCompat(final Context context, final View view, final CharSequence text, public static void announceForAccessibilityCompat(final Context context, final View view, final CharSequence text,
final Class<?> cls) { final Class<?> cls) {
@ -878,7 +830,7 @@ public final class Utils implements Constants {
return orig.replaceAll("\\n+", "\n"); return orig.replaceAll("\\n+", "\n");
} }
private static Drawable getMetadataDrawable(final PackageManager pm, final ActivityInfo info, final String key) { static Drawable getMetadataDrawable(final PackageManager pm, final ActivityInfo info, final String key) {
if (pm == null || info == null || info.metaData == null || key == null || !info.metaData.containsKey(key)) if (pm == null || info == null || info.metaData == null || key == null || !info.metaData.containsKey(key))
return null; return null;
return pm.getDrawable(info.packageName, info.metaData.getInt(key), info.applicationInfo); return pm.getDrawable(info.packageName, info.metaData.getInt(key), info.applicationInfo);
@ -890,7 +842,7 @@ public final class Utils implements Constants {
|| StatusCodeMessageUtils.containsTwitterError(te.getErrorCode()); || StatusCodeMessageUtils.containsTwitterError(te.getErrorCode());
} }
private static boolean isExtensionUseJSON(final ResolveInfo info) { static boolean isExtensionUseJSON(final ResolveInfo info) {
if (info == null || info.activityInfo == null) return false; if (info == null || info.activityInfo == null) return false;
final ActivityInfo activityInfo = info.activityInfo; final ActivityInfo activityInfo = info.activityInfo;
if (activityInfo.metaData != null && activityInfo.metaData.containsKey(METADATA_KEY_EXTENSION_USE_JSON)) if (activityInfo.metaData != null && activityInfo.metaData.containsKey(METADATA_KEY_EXTENSION_USE_JSON))
@ -1016,7 +968,7 @@ public final class Utils implements Constants {
final Intent intent = new Intent(INTENT_ACTION_PEBBLE_NOTIFICATION); final Intent intent = new Intent(INTENT_ACTION_PEBBLE_NOTIFICATION);
intent.putExtra("messageType", "PEBBLE_ALERT"); intent.putExtra("messageType", "PEBBLE_ALERT");
intent.putExtra("sender", appName); intent.putExtra("sender", appName);
intent.putExtra("notificationData", JsonSerializer.serialize(messages, PebbleMessage.class)); intent.putExtra("notificationData", JsonSerializer.serializeList(messages, PebbleMessage.class));
context.getApplicationContext().sendBroadcast(intent); context.getApplicationContext().sendBroadcast(intent);
} }

View File

@ -774,12 +774,12 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher, APIEditorDi
action(ACCOUNT_USER_DATA_ACTIVATED, true.toString()) action(ACCOUNT_USER_DATA_ACTIVATED, true.toString())
action(ACCOUNT_USER_DATA_COLOR, toHexColor(color, format = HexColorFormat.RGB)) action(ACCOUNT_USER_DATA_COLOR, toHexColor(color, format = HexColorFormat.RGB))
action(ACCOUNT_USER_DATA_USER, LoganSquare.serialize(user)) action(ACCOUNT_USER_DATA_USER, JsonSerializer.serialize(user))
action(ACCOUNT_USER_DATA_EXTRAS, typeExtras.second?.let { LoganSquare.serialize(it) }) action(ACCOUNT_USER_DATA_EXTRAS, typeExtras.second?.let { JsonSerializer.serialize(it) })
} }
private fun writeAuthToken(am: AccountManager, account: Account) { private fun writeAuthToken(am: AccountManager, account: Account) {
val authToken = LoganSquare.serialize(credentials) val authToken = JsonSerializer.serialize(credentials)
am.setAuthToken(account, ACCOUNT_AUTH_TOKEN_TYPE, authToken) am.setAuthToken(account, ACCOUNT_AUTH_TOKEN_TYPE, authToken)
} }

View File

@ -6,7 +6,6 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.support.annotation.RequiresApi import android.support.annotation.RequiresApi
import android.text.TextUtils import android.text.TextUtils
import com.bluelinelabs.logansquare.LoganSquare
import org.mariotaku.ktextension.HexColorFormat import org.mariotaku.ktextension.HexColorFormat
import org.mariotaku.ktextension.toHexColor import org.mariotaku.ktextension.toHexColor
import org.mariotaku.ktextension.toInt import org.mariotaku.ktextension.toInt
@ -23,6 +22,7 @@ import org.mariotaku.twidere.model.account.cred.EmptyCredentials
import org.mariotaku.twidere.model.account.cred.OAuthCredentials import org.mariotaku.twidere.model.account.cred.OAuthCredentials
import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.AccountUtils.ACCOUNT_USER_DATA_KEYS import org.mariotaku.twidere.model.util.AccountUtils.ACCOUNT_USER_DATA_KEYS
import org.mariotaku.twidere.util.JsonSerializer
import org.mariotaku.twidere.util.ParseUtils import org.mariotaku.twidere.util.ParseUtils
import java.io.IOException import java.io.IOException
import java.util.concurrent.FutureTask import java.util.concurrent.FutureTask
@ -56,13 +56,13 @@ fun Account.setAccountKey(am: AccountManager, accountKey: UserKey) {
} }
fun Account.getAccountUser(am: AccountManager): ParcelableUser { fun Account.getAccountUser(am: AccountManager): ParcelableUser {
val user = LoganSquare.parse(am.getNonNullUserData(this, ACCOUNT_USER_DATA_USER), ParcelableUser::class.java) val user = JsonSerializer.parse(am.getNonNullUserData(this, ACCOUNT_USER_DATA_USER), ParcelableUser::class.java)
user.is_cache = true user.is_cache = true
return user return user
} }
fun Account.setAccountUser(am: AccountManager, user: ParcelableUser) { fun Account.setAccountUser(am: AccountManager, user: ParcelableUser) {
am.setUserData(this, ACCOUNT_USER_DATA_USER, LoganSquare.serialize(user)) am.setUserData(this, ACCOUNT_USER_DATA_USER, JsonSerializer.serialize(user))
} }
@android.support.annotation.ColorInt @android.support.annotation.ColorInt
@ -78,10 +78,10 @@ fun Account.getAccountExtras(am: AccountManager): AccountExtras? {
val json = AccountDataQueue.getUserData(am, this, ACCOUNT_USER_DATA_EXTRAS) ?: return null val json = AccountDataQueue.getUserData(am, this, ACCOUNT_USER_DATA_EXTRAS) ?: return null
when (getAccountType(am)) { when (getAccountType(am)) {
AccountType.TWITTER -> { AccountType.TWITTER -> {
return LoganSquare.parse(json, TwitterAccountExtras::class.java) return JsonSerializer.parse(json, TwitterAccountExtras::class.java)
} }
AccountType.STATUSNET -> { AccountType.STATUSNET -> {
return LoganSquare.parse(json, StatusNetAccountExtras::class.java) return JsonSerializer.parse(json, StatusNetAccountExtras::class.java)
} }
} }
return null return null
@ -154,9 +154,9 @@ private fun AccountManager.getNonNullUserData(account: Account, key: String): St
private fun parseCredentials(authToken: String, @Credentials.Type authType: String): Credentials { private fun parseCredentials(authToken: String, @Credentials.Type authType: String): Credentials {
when (authType) { when (authType) {
Credentials.Type.OAUTH, Credentials.Type.XAUTH -> return LoganSquare.parse(authToken, OAuthCredentials::class.java) Credentials.Type.OAUTH, Credentials.Type.XAUTH -> return JsonSerializer.parse(authToken, OAuthCredentials::class.java)
Credentials.Type.BASIC -> return LoganSquare.parse(authToken, BasicCredentials::class.java) Credentials.Type.BASIC -> return JsonSerializer.parse(authToken, BasicCredentials::class.java)
Credentials.Type.EMPTY -> return LoganSquare.parse(authToken, EmptyCredentials::class.java) Credentials.Type.EMPTY -> return JsonSerializer.parse(authToken, EmptyCredentials::class.java)
} }
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }

View File

@ -3,7 +3,6 @@ package org.mariotaku.twidere.extension.model
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.text.TextUtils import android.text.TextUtils
import com.bluelinelabs.logansquare.LoganSquare
import org.apache.james.mime4j.dom.Header import org.apache.james.mime4j.dom.Header
import org.apache.james.mime4j.dom.MessageServiceFactory import org.apache.james.mime4j.dom.MessageServiceFactory
import org.apache.james.mime4j.dom.address.Mailbox import org.apache.james.mime4j.dom.address.Mailbox
@ -22,6 +21,7 @@ import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.Draft.Action import org.mariotaku.twidere.model.Draft.Action
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras
import org.mariotaku.twidere.util.JsonSerializer
import org.mariotaku.twidere.util.collection.NonEmptyHashMap import org.mariotaku.twidere.util.collection.NonEmptyHashMap
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@ -67,7 +67,7 @@ fun Draft.writeMimeMessageTo(context: Context, st: OutputStream) {
this.action_extras?.let { extras -> this.action_extras?.let { extras ->
multipart.addBodyPart(BodyPart().apply { multipart.addBodyPart(BodyPart().apply {
setText(bodyFactory.textBody(LoganSquare.serialize(extras)), "json") setText(bodyFactory.textBody(JsonSerializer.serialize(extras)), "json")
this.filename = "twidere.action.extras.json" this.filename = "twidere.action.extras.json"
}) })
} }
@ -215,10 +215,10 @@ private class BodyPartHandler(private val context: Context, private val draft: D
"twidere.action.extras.json" -> { "twidere.action.extras.json" -> {
draft.action_extras = when (draft.action_type) { draft.action_extras = when (draft.action_type) {
"0", "1", Action.UPDATE_STATUS, Action.REPLY, Action.QUOTE -> { "0", "1", Action.UPDATE_STATUS, Action.REPLY, Action.QUOTE -> {
LoganSquare.parse(st, UpdateStatusActionExtras::class.java) JsonSerializer.parse(st, UpdateStatusActionExtras::class.java)
} }
"2", Action.SEND_DIRECT_MESSAGE -> { "2", Action.SEND_DIRECT_MESSAGE -> {
LoganSquare.parse(st, SendDirectMessageActionExtras::class.java) JsonSerializer.parse(st, SendDirectMessageActionExtras::class.java)
} }
else -> { else -> {
null null

View File

@ -11,7 +11,6 @@ import android.support.v7.app.AlertDialog
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.* import android.widget.*
import com.bluelinelabs.logansquare.LoganSquare
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.rengwuxian.materialedittext.MaterialEditText import com.rengwuxian.materialedittext.MaterialEditText
import org.mariotaku.restfu.annotation.method.GET import org.mariotaku.restfu.annotation.method.GET
@ -27,6 +26,7 @@ import org.mariotaku.twidere.extension.applyTheme
import org.mariotaku.twidere.extension.setSelectedItem import org.mariotaku.twidere.extension.setSelectedItem
import org.mariotaku.twidere.model.CustomAPIConfig import org.mariotaku.twidere.model.CustomAPIConfig
import org.mariotaku.twidere.model.account.cred.Credentials import org.mariotaku.twidere.model.account.cred.Credentials
import org.mariotaku.twidere.util.JsonSerializer
import org.mariotaku.twidere.util.ParseUtils import org.mariotaku.twidere.util.ParseUtils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.util.view.ConsumerKeySecretValidator import org.mariotaku.twidere.util.view.ConsumerKeySecretValidator
@ -205,7 +205,7 @@ class APIEditorDialogFragment : BaseDialogFragment() {
return null return null
} }
// Save to cache // Save to cache
return LoganSquare.parseList(response.body.stream(), CustomAPIConfig::class.java) return JsonSerializer.parseList(response.body.stream(), CustomAPIConfig::class.java)
} }
} catch (e: IOException) { } catch (e: IOException) {
// Ignore // Ignore

View File

@ -19,12 +19,14 @@
package org.mariotaku.twidere.fragment package org.mariotaku.twidere.fragment
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import io.nayuki.qrcodegen.QrCode import io.nayuki.qrcodegen.QrCode
import io.nayuki.qrcodegen.QrCodeAndroid
import kotlinx.android.synthetic.main.fragment_user_qr.* import kotlinx.android.synthetic.main.fragment_user_qr.*
import org.mariotaku.twidere.R import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_USER import org.mariotaku.twidere.constant.IntentConstants.EXTRA_USER
@ -46,7 +48,8 @@ class UserQRDialogFragment : BaseDialogFragment() {
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val qrCode = QrCode.encodeText(LinkCreator.getUserWebLink(user).toString(), QrCode.Ecc.HIGH) val qrCode = QrCode.encodeText(LinkCreator.getUserWebLink(user).toString(), QrCode.Ecc.HIGH)
qrView.setImageDrawable(BitmapDrawable(resources, qrCode.toBitmap(1, 0)).apply { val bitmap = QrCodeAndroid.toBitmap(qrCode, 1, 0, Bitmap.Config.ARGB_8888)
qrView.setImageDrawable(BitmapDrawable(resources, bitmap).apply {
this.setAntiAlias(false) this.setAntiAlias(false)
this.isFilterBitmap = false this.isFilterBitmap = false
}) })

View File

@ -8,7 +8,6 @@ import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import okio.ByteString import okio.ByteString
import org.mariotaku.commons.logansquare.LoganSquareMapperFinder
import org.mariotaku.mediaviewer.library.FileCache import org.mariotaku.mediaviewer.library.FileCache
import org.mariotaku.twidere.TwidereConstants.AUTHORITY_TWIDERE_CACHE import org.mariotaku.twidere.TwidereConstants.AUTHORITY_TWIDERE_CACHE
import org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_TYPE import org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_TYPE
@ -16,6 +15,7 @@ import org.mariotaku.twidere.annotation.CacheFileType
import org.mariotaku.twidere.model.CacheMetadata import org.mariotaku.twidere.model.CacheMetadata
import org.mariotaku.twidere.task.SaveFileTask import org.mariotaku.twidere.task.SaveFileTask
import org.mariotaku.twidere.util.BitmapUtils import org.mariotaku.twidere.util.BitmapUtils
import org.mariotaku.twidere.util.JsonSerializer
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.FileNotFoundException import java.io.FileNotFoundException
@ -65,8 +65,7 @@ class CacheProvider : ContentProvider() {
val bytes = fileCache.getExtra(getCacheKey(uri)) ?: return null val bytes = fileCache.getExtra(getCacheKey(uri)) ?: return null
return try { return try {
ByteArrayInputStream(bytes).use { ByteArrayInputStream(bytes).use {
val mapper = LoganSquareMapperFinder.mapperFor(CacheMetadata::class.java) return@use JsonSerializer.parse(it, CacheMetadata::class.java)
return@use mapper.parse(it)
} }
} catch (e: IOException) { } catch (e: IOException) {
null null

View File

@ -3,7 +3,6 @@ package org.mariotaku.twidere.util
import android.accounts.Account import android.accounts.Account
import android.accounts.AccountManager import android.accounts.AccountManager
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import com.bluelinelabs.logansquare.LoganSquare
import org.mariotaku.ktextension.HexColorFormat import org.mariotaku.ktextension.HexColorFormat
import org.mariotaku.ktextension.toHexColor import org.mariotaku.ktextension.toHexColor
import org.mariotaku.library.objectcursor.ObjectCursor import org.mariotaku.library.objectcursor.ObjectCursor
@ -40,7 +39,7 @@ fun migrateAccounts(am: AccountManager, db: SQLiteDatabase) {
am.setUserData(account, ACCOUNT_USER_DATA_CREDS_TYPE, credentials.getCredentialsType()) am.setUserData(account, ACCOUNT_USER_DATA_CREDS_TYPE, credentials.getCredentialsType())
am.setUserData(account, ACCOUNT_USER_DATA_COLOR, toHexColor(credentials.color, format = HexColorFormat.RGB)) am.setUserData(account, ACCOUNT_USER_DATA_COLOR, toHexColor(credentials.color, format = HexColorFormat.RGB))
am.setUserData(account, ACCOUNT_USER_DATA_POSITION, credentials.sort_position) am.setUserData(account, ACCOUNT_USER_DATA_POSITION, credentials.sort_position)
am.setUserData(account, ACCOUNT_USER_DATA_USER, LoganSquare.serialize(credentials.account_user ?: run { am.setUserData(account, ACCOUNT_USER_DATA_USER, JsonSerializer.serialize(credentials.account_user ?: run {
val user = ParcelableUser() val user = ParcelableUser()
user.account_key = credentials.account_key user.account_key = credentials.account_key
user.key = credentials.account_key user.key = credentials.account_key
@ -52,7 +51,7 @@ fun migrateAccounts(am: AccountManager, db: SQLiteDatabase) {
return@run user return@run user
})) }))
am.setUserData(account, ACCOUNT_USER_DATA_EXTRAS, credentials.account_extras) am.setUserData(account, ACCOUNT_USER_DATA_EXTRAS, credentials.account_extras)
am.setAuthToken(account, ACCOUNT_AUTH_TOKEN_TYPE, LoganSquare.serialize(credentials.toCredentials())) am.setAuthToken(account, ACCOUNT_AUTH_TOKEN_TYPE, JsonSerializer.serialize(credentials.toCredentials()))
cur.moveToNext() cur.moveToNext()
} }
} finally { } finally {

View File

@ -390,8 +390,7 @@ object DataStoreUtils {
if (followingOnly) { if (followingOnly) {
val projection = arrayOf(Activities.SOURCES) val projection = arrayOf(Activities.SOURCES)
val cur = resolver.query(uri, projection, selection.sql, selectionArgs, null) ?: return -1 val cur = resolver.query(uri, projection, selection.sql, selectionArgs, null) ?: return -1
try { cur.useCursor { cur ->
val mapper = LoganSquare.mapperFor(UserFollowState::class.java)
var total = 0 var total = 0
cur.moveToFirst() cur.moveToFirst()
while (!cur.isAfterLast) { while (!cur.isAfterLast) {
@ -399,7 +398,7 @@ object DataStoreUtils {
if (TextUtils.isEmpty(string)) continue if (TextUtils.isEmpty(string)) continue
var hasFollowing = false var hasFollowing = false
try { try {
for (state in mapper.parseList(string)) { for (state in JsonSerializer.parseList(string, UserFollowState::class.java)) {
if (state.is_following) { if (state.is_following) {
hasFollowing = true hasFollowing = true
break break
@ -415,8 +414,6 @@ object DataStoreUtils {
cur.moveToNext() cur.moveToNext()
} }
return total return total
} finally {
cur.close()
} }
} }
return queryCount(resolver, uri, selection.sql, selectionArgs) return queryCount(resolver, uri, selection.sql, selectionArgs)

View File

@ -22,9 +22,11 @@ package org.mariotaku.twidere.util
import android.accounts.AccountManager import android.accounts.AccountManager
import android.app.Activity import android.app.Activity
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.ContentValues
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.PorterDuff
import android.os.Parcelable
import android.support.annotation.DrawableRes import android.support.annotation.DrawableRes
import android.support.annotation.StringRes import android.support.annotation.StringRes
import android.support.annotation.UiThread import android.support.annotation.UiThread
@ -42,8 +44,8 @@ import org.mariotaku.ktextension.Bundle
import org.mariotaku.ktextension.set import org.mariotaku.ktextension.set
import org.mariotaku.ktextension.setItemChecked import org.mariotaku.ktextension.setItemChecked
import org.mariotaku.ktextension.setMenuItemIcon import org.mariotaku.ktextension.setMenuItemIcon
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.Constants import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.Constants.*
import org.mariotaku.twidere.R import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.* import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.AccountSelectorActivity import org.mariotaku.twidere.activity.AccountSelectorActivity
@ -63,11 +65,11 @@ import org.mariotaku.twidere.menu.SupportStatusShareProvider
import org.mariotaku.twidere.model.AccountDetails import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableStatus import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
import org.mariotaku.twidere.task.CreateFavoriteTask import org.mariotaku.twidere.task.CreateFavoriteTask
import org.mariotaku.twidere.task.DestroyFavoriteTask import org.mariotaku.twidere.task.DestroyFavoriteTask
import org.mariotaku.twidere.task.RetweetStatusTask import org.mariotaku.twidere.task.RetweetStatusTask
import org.mariotaku.twidere.util.menu.TwidereMenuInfo import org.mariotaku.twidere.util.menu.TwidereMenuInfo
import java.io.IOException
/** /**
* Created by mariotaku on 15/4/12. * Created by mariotaku on 15/4/12.
@ -205,9 +207,9 @@ object MenuUtils {
val isOfficialKey = Utils.isOfficialCredentials(context, details) val isOfficialKey = Utils.isOfficialCredentials(context, details)
setItemAvailability(menu, R.id.translate, isOfficialKey) setItemAvailability(menu, R.id.translate, isOfficialKey)
} }
menu.removeGroup(Constants.MENU_GROUP_STATUS_EXTENSION) menu.removeGroup(MENU_GROUP_STATUS_EXTENSION)
Utils.addIntentToMenuForExtension(context, menu, Constants.MENU_GROUP_STATUS_EXTENSION, INTENT_ACTION_EXTENSION_OPEN_STATUS, addIntentToMenuForExtension(context, menu, MENU_GROUP_STATUS_EXTENSION,
EXTRA_STATUS, EXTRA_STATUS_JSON, status) INTENT_ACTION_EXTENSION_OPEN_STATUS, EXTRA_STATUS, EXTRA_STATUS_JSON, status)
val shareItem = menu.findItem(R.id.share) val shareItem = menu.findItem(R.id.share)
val shareProvider = MenuItemCompat.getActionProvider(shareItem) val shareProvider = MenuItemCompat.getActionProvider(shareItem)
if (shareProvider is SupportStatusShareProvider) { if (shareProvider is SupportStatusShareProvider) {
@ -218,8 +220,8 @@ object MenuUtils {
} else if (shareItem.hasSubMenu()) { } else if (shareItem.hasSubMenu()) {
val shareSubMenu = shareItem.subMenu val shareSubMenu = shareItem.subMenu
val shareIntent = Utils.createStatusShareIntent(context, status) val shareIntent = Utils.createStatusShareIntent(context, status)
shareSubMenu.removeGroup(Constants.MENU_GROUP_STATUS_SHARE) shareSubMenu.removeGroup(MENU_GROUP_STATUS_SHARE)
addIntentToMenu(context, shareSubMenu, shareIntent, Constants.MENU_GROUP_STATUS_SHARE) addIntentToMenu(context, shareSubMenu, shareIntent, MENU_GROUP_STATUS_SHARE)
} else { } else {
val shareIntent = Utils.createStatusShareIntent(context, status) val shareIntent = Utils.createStatusShareIntent(context, status)
val chooserIntent = Intent.createChooser(shareIntent, context.getString(R.string.share_status)) val chooserIntent = Intent.createChooser(shareIntent, context.getString(R.string.share_status))
@ -358,4 +360,55 @@ object MenuUtils {
} }
return true return true
} }
fun addIntentToMenuForExtension(context: Context?, menu: Menu?,
groupId: Int, action: String?,
parcelableKey: String?, parcelableJSONKey: String,
parcelable: Parcelable?) {
if (context == null || menu == null || action == null || parcelableKey == null || parcelable == null)
return
val pm = context.packageManager
val res = context.resources
val density = res.displayMetrics.density
val padding = Math.round(density * 4)
val queryIntent = Intent(action)
queryIntent.setExtrasClassLoader(context.classLoader)
val activities = pm.queryIntentActivities(queryIntent, PackageManager.GET_META_DATA)
val parcelableJson = try {
JsonSerializer.serialize(parcelable)
} catch (e: IOException) {
null
}
for (info in activities) {
val intent = Intent(queryIntent)
if (Utils.isExtensionUseJSON(info) && parcelableJson != null) {
intent.putExtra(parcelableJSONKey, parcelableJson)
} else {
intent.putExtra(parcelableKey, parcelable)
}
intent.setClassName(info.activityInfo.packageName, info.activityInfo.name)
val item = menu.add(groupId, Menu.NONE, Menu.NONE, info.loadLabel(pm))
item.intent = intent
val metaDataDrawable = Utils.getMetadataDrawable(pm, info.activityInfo, METADATA_KEY_EXTENSION_ICON)
val actionIconColor = ThemeUtils.getThemeForegroundColor(context)
if (metaDataDrawable != null) {
metaDataDrawable.mutate()
metaDataDrawable.setColorFilter(actionIconColor, PorterDuff.Mode.SRC_ATOP)
item.icon = metaDataDrawable
} else {
val icon = info.loadIcon(pm)
val iw = icon.intrinsicWidth
val ih = icon.intrinsicHeight
if (iw > 0 && ih > 0) {
val iconWithPadding = PaddingDrawable(icon, padding)
iconWithPadding.setBounds(0, 0, iw, ih)
item.icon = iconWithPadding
} else {
item.icon = icon
}
}
}
}
} }

View File

@ -19,9 +19,9 @@
package org.mariotaku.twidere.util.cache package org.mariotaku.twidere.util.cache
import com.bluelinelabs.logansquare.LoganSquare
import com.bumptech.glide.disklrucache.DiskLruCache import com.bumptech.glide.disklrucache.DiskLruCache
import org.mariotaku.twidere.BuildConfig import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.util.JsonSerializer
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@ -39,7 +39,7 @@ class JsonCache(val cacheDir: File) {
fun <T> getList(key: String, cls: Class<T>): List<T>? { fun <T> getList(key: String, cls: Class<T>): List<T>? {
return cache?.get(key)?.getFile(0)?.inputStream()?.use { return cache?.get(key)?.getFile(0)?.inputStream()?.use {
LoganSquare.parseList(it, cls) JsonSerializer.parseList(it, cls)
} }
} }
@ -47,7 +47,7 @@ class JsonCache(val cacheDir: File) {
val editor = cache?.edit(key) ?: return val editor = cache?.edit(key) ?: return
try { try {
editor.getFile(0)?.outputStream()?.use { editor.getFile(0)?.outputStream()?.use {
LoganSquare.serialize(list, it, cls) JsonSerializer.serialize(list, it, cls)
} }
editor.commit() editor.commit()
} finally { } finally {

View File

@ -2,6 +2,7 @@ package org.mariotaku.twidere.util.filter
import android.content.Context import android.content.Context
import org.mariotaku.twidere.util.JsonSerializer import org.mariotaku.twidere.util.JsonSerializer
import java.io.IOException
/** /**
* Created by mariotaku on 2017/1/9. * Created by mariotaku on 2017/1/9.
@ -13,8 +14,11 @@ abstract class LocalFiltersSubscriptionProvider(val context: Context) : FiltersS
when (name) { when (name) {
"url" -> { "url" -> {
if (arguments == null) return null if (arguments == null) return null
val argsObj = JsonSerializer.parse(arguments, val argsObj = try {
UrlFiltersSubscriptionProvider.Arguments::class.java) ?: return null JsonSerializer.parse(arguments, UrlFiltersSubscriptionProvider.Arguments::class.java)
} catch (e: IOException) {
return null
}
return UrlFiltersSubscriptionProvider(context, argsObj) return UrlFiltersSubscriptionProvider(context, argsObj)
} }
} }

View File

@ -14,6 +14,7 @@ import org.mariotaku.twidere.extension.model.parse
import org.mariotaku.twidere.extension.newPullParser import org.mariotaku.twidere.extension.newPullParser
import org.mariotaku.twidere.model.FiltersData import org.mariotaku.twidere.model.FiltersData
import org.mariotaku.twidere.util.ETagCache import org.mariotaku.twidere.util.ETagCache
import org.mariotaku.twidere.util.JsonSerializer
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import java.io.IOException import java.io.IOException
import javax.inject.Inject import javax.inject.Inject
@ -101,7 +102,7 @@ class UrlFiltersSubscriptionProvider(context: Context, val arguments: Arguments)
} }
private fun Body.toJsonFilters(): FiltersData? { private fun Body.toJsonFilters(): FiltersData? {
return LoganSquare.parse(stream(), FiltersData::class.java) return JsonSerializer.parse(stream(), FiltersData::class.java)
} }
private fun Body.toXmlFilters(): FiltersData? { private fun Body.toXmlFilters(): FiltersData? {

View File

@ -21,8 +21,7 @@
<org.mariotaku.twidere.view.SquareRelativeLayout <org.mariotaku.twidere.view.SquareRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:padding="@dimen/element_spacing_xlarge">
<ImageView <ImageView
android:id="@+id/qrView" android:id="@+id/qrView"
@ -34,6 +33,8 @@
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:background="@android:color/white"
android:padding="@dimen/element_spacing_xlarge"
android:scaleType="fitCenter"/> android:scaleType="fitCenter"/>
</org.mariotaku.twidere.view.SquareRelativeLayout> </org.mariotaku.twidere.view.SquareRelativeLayout>