From 0d79ab4b27ad86dde445bb493609bb30559e7213 Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Wed, 5 Apr 2017 13:03:18 +0800 Subject: [PATCH] made json mapper resolve single threaded --- build.gradle | 2 +- .../callback/FanfouUserStreamCallback.java | 5 +- .../fanfou/model/FanfouStreamObject.java | 4 +- .../twitter/callback/UserStreamCallback.java | 41 +- .../twidere/model/AccountDetails.java | 6 +- .../twidere/model/message/MessageExtras.java | 8 +- .../conversation/ConversationExtras.java | 7 +- .../model/tab/argument/TabArguments.java | 10 +- .../twidere/model/tab/extra/TabExtras.java | 8 +- .../util/ConversationExtrasConverter.java | 5 +- .../model/util/DraftExtrasConverter.java | 11 +- .../model/util/MessageExtrasConverter.java | 5 +- .../util/TabArgumentsFieldConverter.java | 5 +- .../model/util/TabExtrasFieldConverter.java | 5 +- .../twidere/util/JsonSerializer.java | 169 ++++ .../util/model/AccountDetailsUtils.java | 15 +- .../java/org/mariotaku/twidere/Twidere.java | 6 +- .../twidere/service/MediaUploaderService.java | 18 +- .../service/StatusShortenerService.java | 22 +- twidere/build.gradle | 2 + .../text/twitter/ExtractorExtensionsTest.kt | 4 +- .../model/util/ParcelableStatusUtilsTest.kt | 6 +- .../util/stetho/AccountsDumperPlugin.kt | 16 +- .../tsinghua/hotmobi/util/LocationUtils.java | 8 +- .../java/io/nayuki/qrcodegen/BitBuffer.java | 95 -- .../main/java/io/nayuki/qrcodegen/QrCode.java | 857 ------------------ .../java/io/nayuki/qrcodegen/QrSegment.java | 287 ------ .../util/LoganSquareConverterFactory.java | 25 +- .../twidere/util/DataImportExportUtils.java | 16 +- .../twidere/util/JsonSerializer.java | 152 ---- .../org/mariotaku/twidere/util/Utils.java | 54 +- .../twidere/activity/SignInActivity.kt | 6 +- .../extension/model/AccountExtensions.kt | 16 +- .../extension/model/DraftExtensions.kt | 8 +- .../fragment/APIEditorDialogFragment.kt | 4 +- .../twidere/fragment/UserQRDialogFragment.kt | 5 +- .../twidere/provider/CacheProvider.kt | 5 +- .../mariotaku/twidere/util/AccountMigrator.kt | 5 +- .../mariotaku/twidere/util/DataStoreUtils.kt | 7 +- .../org/mariotaku/twidere/util/MenuUtils.kt | 69 +- .../mariotaku/twidere/util/cache/JsonCache.kt | 6 +- .../LocalFiltersSubscriptionProvider.kt | 8 +- .../filter/UrlFiltersSubscriptionProvider.kt | 3 +- .../src/main/res/layout/fragment_user_qr.xml | 5 +- 44 files changed, 395 insertions(+), 1626 deletions(-) create mode 100644 twidere.component.common/src/main/java/org/mariotaku/twidere/util/JsonSerializer.java delete mode 100755 twidere/src/main/java/io/nayuki/qrcodegen/BitBuffer.java delete mode 100755 twidere/src/main/java/io/nayuki/qrcodegen/QrCode.java delete mode 100755 twidere/src/main/java/io/nayuki/qrcodegen/QrSegment.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/JsonSerializer.java diff --git a/build.gradle b/build.gradle index a10e18cd2..219a28fba 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,7 @@ subprojects { libVersions = [ Kotlin : '1.1.1', SupportLib : '25.3.1', - MariotakuCommons : '0.9.11', + MariotakuCommons : '0.9.12', RestFu : '0.9.44', ObjectCursor : '0.9.16', PlayServices : '10.2.1', diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/fanfou/callback/FanfouUserStreamCallback.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/fanfou/callback/FanfouUserStreamCallback.java index 6e34aaf0b..a1869ea22 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/fanfou/callback/FanfouUserStreamCallback.java +++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/fanfou/callback/FanfouUserStreamCallback.java @@ -23,8 +23,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; -import com.bluelinelabs.logansquare.LoganSquare; - import org.mariotaku.microblog.library.MicroBlogException; import org.mariotaku.microblog.library.fanfou.model.FanfouStreamObject; 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.restfu.callback.RawCallback; import org.mariotaku.restfu.http.HttpResponse; +import org.mariotaku.twidere.util.JsonSerializer; import java.io.IOException; import java.io.InputStreamReader; @@ -66,7 +65,7 @@ public abstract class FanfouUserStreamCallback implements RawCallback T getObject(Class cls) throws IOException { if (rawObject == null) return null; - return LoganSquare.parse(rawObject, cls); + return JsonSerializer.parse(rawObject, cls); } } diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/callback/UserStreamCallback.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/callback/UserStreamCallback.java index fcdd78793..49d063bf7 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/callback/UserStreamCallback.java +++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/callback/UserStreamCallback.java @@ -22,8 +22,6 @@ package org.mariotaku.microblog.library.twitter.callback; import android.support.annotation.NonNull; import android.text.TextUtils; -import com.bluelinelabs.logansquare.LoganSquare; - import org.mariotaku.microblog.library.MicroBlogException; import org.mariotaku.microblog.library.twitter.model.DeletionEvent; 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.restfu.callback.RawCallback; import org.mariotaku.restfu.http.HttpResponse; +import org.mariotaku.twidere.util.JsonSerializer; import java.io.IOException; import java.io.InputStreamReader; @@ -73,7 +72,7 @@ public abstract class UserStreamCallback implements RawCallback { @OnPreJsonSerialize void onPreJsonSerialize() throws IOException { if (credentials != null) { - credentials_json = LoganSquare.serialize(credentials); + credentials_json = JsonSerializer.serialize(credentials); } if (extras != null) { - extras_json = LoganSquare.serialize(extras); + extras_json = JsonSerializer.serialize(extras); } } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/MessageExtras.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/MessageExtras.java index cffd1203c..2234b0be7 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/MessageExtras.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/MessageExtras.java @@ -25,10 +25,10 @@ import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import com.bluelinelabs.logansquare.LoganSquare; import com.bluelinelabs.logansquare.annotation.JsonObject; import org.mariotaku.twidere.model.ParcelableMessage.MessageType; +import org.mariotaku.twidere.util.JsonSerializer; import java.io.IOException; @@ -42,14 +42,14 @@ public abstract class MessageExtras implements Parcelable { if (json == null) return null; switch (messageType) { case MessageType.STICKER: - return LoganSquare.parse(json, StickerExtras.class); + return JsonSerializer.parse(json, StickerExtras.class); case MessageType.JOIN_CONVERSATION: case MessageType.PARTICIPANTS_LEAVE: case MessageType.PARTICIPANTS_JOIN: - return LoganSquare.parse(json, UserArrayExtras.class); + return JsonSerializer.parse(json, UserArrayExtras.class); case MessageType.CONVERSATION_NAME_UPDATE: case MessageType.CONVERSATION_AVATAR_UPDATE: - return LoganSquare.parse(json, ConversationInfoUpdatedExtras.class); + return JsonSerializer.parse(json, ConversationInfoUpdatedExtras.class); } return null; } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/conversation/ConversationExtras.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/conversation/ConversationExtras.java index c11b4d78b..153239665 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/conversation/ConversationExtras.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/message/conversation/ConversationExtras.java @@ -25,9 +25,8 @@ import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import com.bluelinelabs.logansquare.LoganSquare; - import org.mariotaku.twidere.model.ParcelableMessageConversation.ExtrasType; +import org.mariotaku.twidere.util.JsonSerializer; import java.io.IOException; @@ -40,9 +39,9 @@ public abstract class ConversationExtras implements Parcelable { if (json == null) return null; switch (extrasType) { 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); } } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/tab/argument/TabArguments.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/tab/argument/TabArguments.java index 80fafecfe..cbe6bfa9f 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/tab/argument/TabArguments.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/tab/argument/TabArguments.java @@ -26,13 +26,13 @@ import android.support.annotation.CallSuper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import com.bluelinelabs.logansquare.LoganSquare; import com.bluelinelabs.logansquare.annotation.JsonField; import com.bluelinelabs.logansquare.annotation.JsonObject; import org.mariotaku.twidere.TwidereConstants; import org.mariotaku.twidere.annotation.CustomTabType; import org.mariotaku.twidere.model.UserKey; +import org.mariotaku.twidere.util.JsonSerializer; import java.io.IOException; import java.util.Arrays; @@ -102,17 +102,17 @@ public class TabArguments implements TwidereConstants { case CustomTabType.NOTIFICATIONS_TIMELINE: case CustomTabType.DIRECT_MESSAGES: case CustomTabType.TRENDS_SUGGESTIONS: { - return LoganSquare.parse(json, TabArguments.class); + return JsonSerializer.parse(json, TabArguments.class); } case CustomTabType.USER_TIMELINE: case CustomTabType.FAVORITES: { - return LoganSquare.parse(json, UserArguments.class); + return JsonSerializer.parse(json, UserArguments.class); } case CustomTabType.LIST_TIMELINE: { - return LoganSquare.parse(json, UserListArguments.class); + return JsonSerializer.parse(json, UserListArguments.class); } case CustomTabType.SEARCH_STATUSES: { - return LoganSquare.parse(json, TextQueryArguments.class); + return JsonSerializer.parse(json, TextQueryArguments.class); } } return null; diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/tab/extra/TabExtras.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/tab/extra/TabExtras.java index 26e5bce36..c1cfd18ce 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/tab/extra/TabExtras.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/tab/extra/TabExtras.java @@ -27,10 +27,10 @@ import android.support.annotation.CallSuper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import com.bluelinelabs.logansquare.LoganSquare; import com.bluelinelabs.logansquare.annotation.JsonObject; import org.mariotaku.twidere.annotation.CustomTabType; +import org.mariotaku.twidere.util.JsonSerializer; import java.io.IOException; @@ -49,13 +49,13 @@ public abstract class TabExtras implements Parcelable { if (json == null) return null; switch (type) { case CustomTabType.NOTIFICATIONS_TIMELINE: { - return LoganSquare.parse(json, InteractionsTabExtras.class); + return JsonSerializer.parse(json, InteractionsTabExtras.class); } case CustomTabType.HOME_TIMELINE: { - return LoganSquare.parse(json, HomeTabExtras.class); + return JsonSerializer.parse(json, HomeTabExtras.class); } case CustomTabType.TRENDS_SUGGESTIONS: { - return LoganSquare.parse(json, TrendsTabExtras.class); + return JsonSerializer.parse(json, TrendsTabExtras.class); } } return null; diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/ConversationExtrasConverter.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/ConversationExtrasConverter.java index d4dcc64dd..17f08de47 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/ConversationExtrasConverter.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/ConversationExtrasConverter.java @@ -25,11 +25,10 @@ import android.content.ContentValues; import android.database.Cursor; import android.text.TextUtils; -import com.bluelinelabs.logansquare.LoganSquare; - import org.mariotaku.library.objectcursor.converter.CursorFieldConverter; import org.mariotaku.twidere.model.message.conversation.ConversationExtras; import org.mariotaku.twidere.provider.TwidereDataStore.Messages; +import org.mariotaku.twidere.util.JsonSerializer; import java.io.IOException; import java.lang.reflect.ParameterizedType; @@ -48,6 +47,6 @@ public class ConversationExtrasConverter implements CursorFieldConverter case Draft.Action.UPDATE_STATUS: case Draft.Action.REPLY: 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: { - return LoganSquare.parse(json, SendDirectMessageActionExtras.class); + return JsonSerializer.parse(json, SendDirectMessageActionExtras.class); } case Draft.Action.FAVORITE: case Draft.Action.RETWEET: { - return LoganSquare.parse(json, StatusObjectExtras.class); + return JsonSerializer.parse(json, StatusObjectExtras.class); } } return null; @@ -70,6 +69,6 @@ public class DraftExtrasConverter implements CursorFieldConverter @Override public void writeField(ContentValues values, ActionExtras object, String columnName, ParameterizedType fieldType) throws IOException { if (object == null) return; - values.put(columnName, LoganSquare.serialize(object)); + values.put(columnName, JsonSerializer.serialize(object)); } } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/MessageExtrasConverter.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/MessageExtrasConverter.java index efd08ccb6..11f5ee554 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/MessageExtrasConverter.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/MessageExtrasConverter.java @@ -25,11 +25,10 @@ import android.content.ContentValues; import android.database.Cursor; import android.text.TextUtils; -import com.bluelinelabs.logansquare.LoganSquare; - import org.mariotaku.library.objectcursor.converter.CursorFieldConverter; import org.mariotaku.twidere.model.message.MessageExtras; import org.mariotaku.twidere.provider.TwidereDataStore.Messages; +import org.mariotaku.twidere.util.JsonSerializer; import java.io.IOException; import java.lang.reflect.ParameterizedType; @@ -48,6 +47,6 @@ public class MessageExtrasConverter implements CursorFieldConverter @Override public void writeField(ContentValues values, TabExtras object, String columnName, ParameterizedType fieldType) throws IOException { if (object == null) return; - values.put(columnName, LoganSquare.serialize(object)); + values.put(columnName, JsonSerializer.serialize(object)); } } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/util/JsonSerializer.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/util/JsonSerializer.java new file mode 100644 index 000000000..43ec4f701 --- /dev/null +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/util/JsonSerializer.java @@ -0,0 +1,169 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.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 String serializeList(@Nullable final List list, final Class cls) { + if (list == null) return null; + try { + return LoganSquareMapperFinder.mapperFor(cls).serialize(list); + } catch (IOException e) { + return null; + } + } + + @Nullable + public static String serializeMap(@Nullable final Map list, final Class cls) { + if (list == null) return null; + try { + return LoganSquareMapperFinder.mapperFor(cls).serialize(list); + } catch (IOException e) { + return null; + } + } + + @Nullable + public static String serialize(@Nullable final T[] array, final Class cls) { + if (array == null) return null; + try { + return LoganSquareMapperFinder.mapperFor(cls).serialize(Arrays.asList(array)); + } catch (IOException e) { + return null; + } + } + + @Nullable + public static String serialize(@Nullable final T object, final Class cls) { + if (object == null) return null; + try { + return LoganSquareMapperFinder.mapperFor(cls).serialize(object); + } catch (IOException e) { + return null; + } + } + + @Nullable + public static String serialize(@Nullable final T object, final ParameterizedType cls) { + if (object == null) return null; + try { + return LoganSquareMapperFinder.mapperFor(cls).serialize(object); + } catch (IOException e) { + return null; + } + } + + public static void serialize(@Nullable final T object, final OutputStream st, + final Class cls) throws IOException { + LoganSquareMapperFinder.mapperFor(cls).serialize(object, st); + } + + public static void serialize(@Nullable final List list, final OutputStream st, + final Class cls) throws IOException { + LoganSquareMapperFinder.mapperFor(cls).serialize(list, st); + } + + @NonNull + public static String serialize(@Nullable final T object) throws IOException { + if (object == null) throw new IOException(); + //noinspection unchecked + final Class cls = (Class) object.getClass(); + final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(cls); + return mapper.serialize(object); + } + + @NonNull + public static T[] parseArray(@Nullable final String string, final Class cls) throws IOException { + if (string == null) throw new IOException(); + final List list = LoganSquareMapperFinder.mapperFor(cls).parseList(string); + //noinspection unchecked + return list.toArray((T[]) Array.newInstance(cls, list.size())); + } + + @NonNull + public static T parse(@Nullable final String string, final Class cls) throws IOException { + if (string == null) throw new IOException(); + return LoganSquareMapperFinder.mapperFor(cls).parse(string); + } + + @NonNull + public static T parse(@Nullable final String string, final ParameterizedType cls) throws IOException { + if (string == null) throw new IOException(); + return LoganSquareMapperFinder.mapperFor(cls).parse(string); + } + + @NonNull + public static T parse(@Nullable final InputStream stream, final Class cls) throws IOException { + if (stream == null) throw new IOException(); + return LoganSquareMapperFinder.mapperFor(cls).parse(stream); + } + + + @NonNull + public static T parse(@Nullable final InputStream stream, final ParameterizedType cls) throws IOException { + if (stream == null) throw new IOException(); + return LoganSquareMapperFinder.mapperFor(cls).parse(stream); + } + + @NonNull + public static List parseList(@Nullable InputStream stream, Class cls) throws IOException { + if (stream == null) throw new IOException(); + return LoganSquareMapperFinder.mapperFor(cls).parseList(stream); + } + + @NonNull + public static Map parseMap(@Nullable InputStream stream, Class cls) throws IOException { + if (stream == null) throw new IOException(); + return LoganSquareMapperFinder.mapperFor(cls).parseMap(stream); + } + + @NonNull + public static List parseList(@Nullable String json, Class cls) throws IOException { + if (json == null) throw new IOException(); + return LoganSquareMapperFinder.mapperFor(cls).parseList(json); + } + +} diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/util/model/AccountDetailsUtils.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/util/model/AccountDetailsUtils.java index f7b66926b..aadedae91 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/util/model/AccountDetailsUtils.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/util/model/AccountDetailsUtils.java @@ -21,8 +21,6 @@ package org.mariotaku.twidere.util.model; -import com.bluelinelabs.logansquare.LoganSquare; - import org.mariotaku.twidere.annotation.AccountType; import org.mariotaku.twidere.model.account.AccountExtras; 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.OAuth2Credentials; import org.mariotaku.twidere.model.account.cred.OAuthCredentials; +import org.mariotaku.twidere.util.JsonSerializer; import java.io.IOException; @@ -45,16 +44,16 @@ public class AccountDetailsUtils { switch (type) { case Credentials.Type.OAUTH: case Credentials.Type.XAUTH: { - return LoganSquare.parse(json, OAuthCredentials.class); + return JsonSerializer.parse(json, OAuthCredentials.class); } case Credentials.Type.BASIC: { - return LoganSquare.parse(json, BasicCredentials.class); + return JsonSerializer.parse(json, BasicCredentials.class); } case Credentials.Type.EMPTY: { - return LoganSquare.parse(json, EmptyCredentials.class); + return JsonSerializer.parse(json, EmptyCredentials.class); } case Credentials.Type.OAUTH2: { - return LoganSquare.parse(json, OAuth2Credentials.class); + return JsonSerializer.parse(json, OAuth2Credentials.class); } } } catch (IOException e) { @@ -68,10 +67,10 @@ public class AccountDetailsUtils { try { switch (type) { case AccountType.TWITTER: { - return LoganSquare.parse(json, TwitterAccountExtras.class); + return JsonSerializer.parse(json, TwitterAccountExtras.class); } case AccountType.STATUSNET: { - return LoganSquare.parse(json, StatusNetAccountExtras.class); + return JsonSerializer.parse(json, StatusNetAccountExtras.class); } } } catch (IOException e) { diff --git a/twidere.library.extension/src/main/java/org/mariotaku/twidere/Twidere.java b/twidere.library.extension/src/main/java/org/mariotaku/twidere/Twidere.java index f4e8fb7d0..d14f5466d 100644 --- a/twidere.library.extension/src/main/java/org/mariotaku/twidere/Twidere.java +++ b/twidere.library.extension/src/main/java/org/mariotaku/twidere/Twidere.java @@ -38,8 +38,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RequiresPermission; -import com.bluelinelabs.logansquare.LoganSquare; - import org.mariotaku.twidere.model.AccountDetails; import org.mariotaku.twidere.model.ComposingStatus; 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.UserKey; import org.mariotaku.twidere.provider.TwidereDataStore.Permissions; +import org.mariotaku.twidere.util.JsonSerializer; import org.mariotaku.twidere.util.model.AccountDetailsUtils; import java.io.IOException; @@ -192,7 +191,8 @@ public final class Twidere implements TwidereConstants { //noinspection WrongConstant details.credentials_type = am.getUserData(account, ACCOUNT_USER_DATA_CREDS_TYPE); 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) { throw new RuntimeException(e); } diff --git a/twidere.library.extension/src/main/java/org/mariotaku/twidere/service/MediaUploaderService.java b/twidere.library.extension/src/main/java/org/mariotaku/twidere/service/MediaUploaderService.java index d44211bc7..a379fa46c 100644 --- a/twidere.library.extension/src/main/java/org/mariotaku/twidere/service/MediaUploaderService.java +++ b/twidere.library.extension/src/main/java/org/mariotaku/twidere/service/MediaUploaderService.java @@ -32,8 +32,8 @@ import org.mariotaku.twidere.model.MediaUploadResult; import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatusUpdate; import org.mariotaku.twidere.model.UploaderMediaItem; -import org.mariotaku.commons.logansquare.LoganSquareMapperFinder; import org.mariotaku.twidere.model.UserKey; +import org.mariotaku.twidere.util.JsonSerializer; import java.io.IOException; import java.lang.ref.WeakReference; @@ -54,7 +54,7 @@ public abstract class MediaUploaderService extends Service { } protected abstract MediaUploadResult upload(ParcelableStatusUpdate status, - UserKey currentAccount, UploaderMediaItem[] media); + UserKey currentAccount, UploaderMediaItem[] media); protected abstract boolean callback(MediaUploadResult result, ParcelableStatus status); @@ -74,14 +74,12 @@ public abstract class MediaUploaderService extends Service { @Override public String upload(String statusJson, String currentAccount, String mediaJson) throws RemoteException { try { - final ParcelableStatusUpdate statusUpdate = LoganSquareMapperFinder.mapperFor(ParcelableStatusUpdate.class) - .parse(statusJson); - final List media = LoganSquareMapperFinder.mapperFor(UploaderMediaItem.class) - .parseList(mediaJson); + final ParcelableStatusUpdate statusUpdate = JsonSerializer.parse(statusJson, ParcelableStatusUpdate.class); + final List media = JsonSerializer.parseList(mediaJson, UploaderMediaItem.class); final MediaUploadResult shorten = mService.get().upload(statusUpdate, UserKey.valueOf(currentAccount), media.toArray(new UploaderMediaItem[media.size()])); - return LoganSquareMapperFinder.mapperFor(MediaUploadResult.class).serialize(shorten); + return JsonSerializer.serialize(shorten, MediaUploadResult.class); } catch (IOException e) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { throw new RemoteException(e.getMessage()); @@ -94,10 +92,8 @@ public abstract class MediaUploaderService extends Service { @Override public boolean callback(String resultJson, String statusJson) throws RemoteException { try { - final MediaUploadResult result = LoganSquareMapperFinder.mapperFor(MediaUploadResult.class) - .parse(resultJson); - final ParcelableStatus status = LoganSquareMapperFinder.mapperFor(ParcelableStatus.class) - .parse(statusJson); + final MediaUploadResult result = JsonSerializer.parse(resultJson, MediaUploadResult.class); + final ParcelableStatus status = JsonSerializer.parse(statusJson, ParcelableStatus.class); return mService.get().callback(result, status); } catch (IOException e) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { diff --git a/twidere.library.extension/src/main/java/org/mariotaku/twidere/service/StatusShortenerService.java b/twidere.library.extension/src/main/java/org/mariotaku/twidere/service/StatusShortenerService.java index 122fd418a..9fc7af3c5 100644 --- a/twidere.library.extension/src/main/java/org/mariotaku/twidere/service/StatusShortenerService.java +++ b/twidere.library.extension/src/main/java/org/mariotaku/twidere/service/StatusShortenerService.java @@ -28,11 +28,11 @@ import android.os.IBinder; import android.os.RemoteException; import org.mariotaku.twidere.IStatusShortener; -import org.mariotaku.twidere.model.UserKey; import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatusUpdate; 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.lang.ref.WeakReference; @@ -51,8 +51,8 @@ public abstract class StatusShortenerService extends Service { } protected abstract StatusShortenResult shorten(ParcelableStatusUpdate status, - UserKey currentAccountKey, - String overrideStatusText); + UserKey currentAccountKey, + String overrideStatusText); protected abstract boolean callback(StatusShortenResult result, ParcelableStatus status); @@ -71,15 +71,15 @@ public abstract class StatusShortenerService extends Service { @Override public String shorten(final String statusJson, final String currentAccountIdStr, - final String overrideStatusText) + final String overrideStatusText) throws RemoteException { try { - final ParcelableStatusUpdate statusUpdate = LoganSquareMapperFinder.mapperFor(ParcelableStatusUpdate.class) - .parse(statusJson); + final ParcelableStatusUpdate statusUpdate = JsonSerializer.parse(statusJson, + ParcelableStatusUpdate.class); final UserKey currentAccountId = UserKey.valueOf(currentAccountIdStr); final StatusShortenResult shorten = mService.get().shorten(statusUpdate, currentAccountId, overrideStatusText); - return LoganSquareMapperFinder.mapperFor(StatusShortenResult.class).serialize(shorten); + return JsonSerializer.serialize(shorten, StatusShortenResult.class); } catch (IOException e) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { throw new RemoteException(e.getMessage()); @@ -92,10 +92,8 @@ public abstract class StatusShortenerService extends Service { @Override public boolean callback(String resultJson, String statusJson) throws RemoteException { try { - final StatusShortenResult result = LoganSquareMapperFinder.mapperFor(StatusShortenResult.class) - .parse(resultJson); - final ParcelableStatus status = LoganSquareMapperFinder.mapperFor(ParcelableStatus.class) - .parse(statusJson); + final StatusShortenResult result = JsonSerializer.parse(resultJson, StatusShortenResult.class); + final ParcelableStatus status = JsonSerializer.parse(statusJson, ParcelableStatus.class); return mService.get().callback(result, status); } catch (IOException e) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { diff --git a/twidere/build.gradle b/twidere/build.gradle index 1dc948435..e78367145 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -187,6 +187,8 @@ dependencies { compile "com.github.mariotaku.CommonsLibrary:text-kotlin:${libVersions['MariotakuCommons']}" compile "com.github.mariotaku:KPreferences:${libVersions['KPreferences']}" 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 "nl.komponents.kovenant:kovenant:${libVersions['Kovenant']}" diff --git a/twidere/src/androidTest/kotlin/org/mariotaku/twidere/extension/text/twitter/ExtractorExtensionsTest.kt b/twidere/src/androidTest/kotlin/org/mariotaku/twidere/extension/text/twitter/ExtractorExtensionsTest.kt index b4c88aebe..41d7ccff1 100644 --- a/twidere/src/androidTest/kotlin/org/mariotaku/twidere/extension/text/twitter/ExtractorExtensionsTest.kt +++ b/twidere/src/androidTest/kotlin/org/mariotaku/twidere/extension/text/twitter/ExtractorExtensionsTest.kt @@ -21,7 +21,6 @@ package org.mariotaku.twidere.extension.text.twitter import android.support.test.InstrumentationRegistry import android.support.test.runner.AndroidJUnit4 -import com.bluelinelabs.logansquare.LoganSquare import com.twitter.Extractor import org.junit.Assert import org.junit.Before @@ -30,6 +29,7 @@ import org.junit.runner.RunWith import org.mariotaku.twidere.model.ParcelableStatus import org.mariotaku.twidere.model.ParcelableUserMention import org.mariotaku.twidere.test.R +import org.mariotaku.twidere.util.JsonSerializer /** * Created by mariotaku on 2017/4/2. @@ -46,7 +46,7 @@ class ExtractorExtensionsTest { // This is a tweet by @t_deyarmin, mentioning @nixcraft inReplyTo = context.resources.openRawResource(R.raw.parcelable_status_848051071444410368).use { - LoganSquare.parse(it, ParcelableStatus::class.java) + JsonSerializer.parse(it, ParcelableStatus::class.java) } } diff --git a/twidere/src/androidTest/kotlin/org/mariotaku/twidere/model/util/ParcelableStatusUtilsTest.kt b/twidere/src/androidTest/kotlin/org/mariotaku/twidere/model/util/ParcelableStatusUtilsTest.kt index 41f273095..32269e742 100644 --- a/twidere/src/androidTest/kotlin/org/mariotaku/twidere/model/util/ParcelableStatusUtilsTest.kt +++ b/twidere/src/androidTest/kotlin/org/mariotaku/twidere/model/util/ParcelableStatusUtilsTest.kt @@ -2,13 +2,13 @@ package org.mariotaku.twidere.model.util import android.support.test.InstrumentationRegistry import android.support.test.runner.AndroidJUnit4 -import com.bluelinelabs.logansquare.LoganSquare import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith import org.mariotaku.microblog.library.twitter.model.Status import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.test.R +import org.mariotaku.twidere.util.JsonSerializer /** * Created by mariotaku on 2017/1/4. @@ -23,13 +23,13 @@ class ParcelableStatusUtilsTest { fun testFromStatus() { val context = InstrumentationRegistry.getContext() 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"), "statusnet", false) } 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"), "statusnet", false) } diff --git a/twidere/src/debug/kotlin/org/mariotaku/twidere/util/stetho/AccountsDumperPlugin.kt b/twidere/src/debug/kotlin/org/mariotaku/twidere/util/stetho/AccountsDumperPlugin.kt index b3159f341..c1f594023 100644 --- a/twidere/src/debug/kotlin/org/mariotaku/twidere/util/stetho/AccountsDumperPlugin.kt +++ b/twidere/src/debug/kotlin/org/mariotaku/twidere/util/stetho/AccountsDumperPlugin.kt @@ -25,7 +25,6 @@ import android.content.Context import android.util.Base64 import android.util.Base64InputStream import android.util.Base64OutputStream -import com.bluelinelabs.logansquare.LoganSquare import com.facebook.stetho.dumpapp.DumpException import com.facebook.stetho.dumpapp.DumperContext 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.util.AccountUtils import org.mariotaku.twidere.util.DataStoreUtils +import org.mariotaku.twidere.util.JsonSerializer import org.mariotaku.twidere.util.Utils import java.io.InputStream import java.io.OutputStream @@ -205,7 +205,7 @@ class AccountsDumperPlugin(val context: Context) : DumperPlugin { val path = args[1] docContext.set(path, value) val details = docContext.read("$", Object::class.java)?.let { - LoganSquare.parse(it.toString(), AccountDetails::class.java) + JsonSerializer.parse(it.toString(), AccountDetails::class.java) } ?: return details.account.updateDetails(am, details) 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)) // write accounts 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 { @@ -248,7 +248,7 @@ class AccountsDumperPlugin(val context: Context) : DumperPlugin { val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") cipher.init(Cipher.DECRYPT_MODE, secret, IvParameterSpec(iv)) 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) { @@ -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_COLOR, toHexColor(details.color, format = HexColorFormat.RGB)) - am.setUserData(this, ACCOUNT_USER_DATA_USER, LoganSquare.serialize(details.user)) - am.setUserData(this, ACCOUNT_USER_DATA_EXTRAS, details.extras?.let { LoganSquare.serialize(it) }) - am.setAuthToken(this, ACCOUNT_AUTH_TOKEN_TYPE, LoganSquare.serialize(details.credentials)) + am.setUserData(this, ACCOUNT_USER_DATA_USER, JsonSerializer.serialize(details.user)) + am.setUserData(this, ACCOUNT_USER_DATA_EXTRAS, details.extras?.let { JsonSerializer.serialize(it) }) + am.setAuthToken(this, ACCOUNT_AUTH_TOKEN_TYPE, JsonSerializer.serialize(details.credentials)) } private fun AccountManager.docContext(forKey: String): DocumentContext { @@ -301,7 +301,7 @@ class AccountsDumperPlugin(val context: Context) : DumperPlugin { .jsonProvider(JsonOrgJsonProvider()) .mappingProvider(AsIsMappingProvider()) .build() - return JsonPath.parse(LoganSquare.serialize(details), configuration) + return JsonPath.parse(JsonSerializer.serialize(details), configuration) } private fun Any.prettyPrint() = if (this is JSONObject) { diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/util/LocationUtils.java b/twidere/src/main/java/edu/tsinghua/hotmobi/util/LocationUtils.java index f3172de66..8935ff796 100644 --- a/twidere/src/main/java/edu/tsinghua/hotmobi/util/LocationUtils.java +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/util/LocationUtils.java @@ -12,6 +12,8 @@ import org.mariotaku.twidere.util.JsonSerializer; import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.util.dagger.DependencyHolder; +import java.io.IOException; + import edu.tsinghua.hotmobi.HotMobiConstants; import edu.tsinghua.hotmobi.HotMobiLogger; import edu.tsinghua.hotmobi.model.LatLng; @@ -30,7 +32,11 @@ public class LocationUtils implements HotMobiConstants, Constants { DebugLog.d(HotMobiLogger.LOGTAG, "getting cached location", null); final Location location = Utils.getCachedLocation(appContext); 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()); AsyncTask.execute(new Runnable() { diff --git a/twidere/src/main/java/io/nayuki/qrcodegen/BitBuffer.java b/twidere/src/main/java/io/nayuki/qrcodegen/BitBuffer.java deleted file mode 100755 index 45736619e..000000000 --- a/twidere/src/main/java/io/nayuki/qrcodegen/BitBuffer.java +++ /dev/null @@ -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); - } - -} diff --git a/twidere/src/main/java/io/nayuki/qrcodegen/QrCode.java b/twidere/src/main/java/io/nayuki/qrcodegen/QrCode.java deleted file mode 100755 index 421b52c02..000000000 --- a/twidere/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ /dev/null @@ -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. - *

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.

- */ -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 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. - *

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.

- * - * @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 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. - *

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.

- * - * @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 ≤ minVersion ≤ maxVersion ≤ 40 is violated, or if mask - * < −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 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 × 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. - *

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.

- * - * @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−1 is the right edge - * @param y the y coordinate, where 0 is the top edge and size−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 < 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; - } - - } - -} diff --git a/twidere/src/main/java/io/nayuki/qrcodegen/QrSegment.java b/twidere/src/main/java/io/nayuki/qrcodegen/QrSegment.java deleted file mode 100755 index dc9da5760..000000000 --- a/twidere/src/main/java/io/nayuki/qrcodegen/QrSegment.java +++ /dev/null @@ -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. - *

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.

- */ -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 makeSegments(@NonNull String text) { - - // Select the most efficient segment encoding automatically - List 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 ≤ {@code bitLength} ≤ {@code data.length} × 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 ≤ {@code index} < ceil({@code bitLength} ÷ 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 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"); - } - - } - -} diff --git a/twidere/src/main/java/org/mariotaku/microblog/library/twitter/util/LoganSquareConverterFactory.java b/twidere/src/main/java/org/mariotaku/microblog/library/twitter/util/LoganSquareConverterFactory.java index 9ab5a7a8f..add5d3cc6 100644 --- a/twidere/src/main/java/org/mariotaku/microblog/library/twitter/util/LoganSquareConverterFactory.java +++ b/twidere/src/main/java/org/mariotaku/microblog/library/twitter/util/LoganSquareConverterFactory.java @@ -23,11 +23,9 @@ import android.support.annotation.NonNull; import android.support.v4.util.SimpleArrayMap; import com.bluelinelabs.logansquare.Commons_ParameterizedTypeAccessor; -import com.bluelinelabs.logansquare.JsonMapper; import com.bluelinelabs.logansquare.ParameterizedType; import com.fasterxml.jackson.core.JsonParseException; -import org.mariotaku.commons.logansquare.LoganSquareMapperFinder; import org.mariotaku.microblog.library.twitter.model.TwitterResponse; import org.mariotaku.restfu.RestConverter; 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.SimpleBody; import org.mariotaku.restfu.http.mime.StringBody; +import org.mariotaku.twidere.util.JsonSerializer; import java.io.IOException; import java.lang.reflect.Type; @@ -77,14 +76,13 @@ public class LoganSquareConverterFactory extends RestConver try { final Object parsed; if (type.rawType == List.class) { - final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(type.typeParameters.get(0).rawType); - parsed = mapper.parseList(response.getBody().stream()); + final Class cls = type.typeParameters.get(0).rawType; + parsed = JsonSerializer.parseList(response.getBody().stream(), cls); } else if (type.rawType == Map.class) { - final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(type.typeParameters.get(1).rawType); - parsed = mapper.parseMap(response.getBody().stream()); + final Class cls = type.typeParameters.get(1).rawType; + parsed = JsonSerializer.parseMap(response.getBody().stream(), cls); } else { - final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(type); - parsed = mapper.parse(response.getBody().stream()); + parsed = JsonSerializer.parse(response.getBody().stream(), type); } if (parsed == null) { throw new IOException("Empty data"); @@ -123,17 +121,16 @@ public class LoganSquareConverterFactory extends RestConver public Body convert(Object request) throws IOException, ConvertException, E { final String json; 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 - json = mapper.serialize((List) request); + json = JsonSerializer.serializeList((List) request, cls); } 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 - json = mapper.serialize((Map) request); + json = JsonSerializer.serializeMap((Map) request, cls); } else { - final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(type); //noinspection unchecked - json = mapper.serialize(request); + json = JsonSerializer.serialize(request, (ParameterizedType) type); } return new StringBody(json, ContentType.parse("application/json")); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/DataImportExportUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/DataImportExportUtils.java index 459c81af4..de58a0402 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/DataImportExportUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/DataImportExportUtils.java @@ -30,13 +30,11 @@ import android.support.annotation.NonNull; import android.support.annotation.WorkerThread; import android.util.Log; -import com.bluelinelabs.logansquare.JsonMapper; import com.bluelinelabs.logansquare.LoganSquare; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; -import org.mariotaku.commons.logansquare.LoganSquareMapperFinder; import org.mariotaku.library.objectcursor.ObjectCursor; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.annotation.Preference; @@ -331,8 +329,7 @@ public class DataImportExportUtils implements Constants { throws IOException { final ZipEntry entry = zipFile.getEntry(entryName); if (entry == null) return; - final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(itemCls); - List itemsList = mapper.parseList(zipFile.getInputStream(entry)); + List itemsList = JsonSerializer.parseList(zipFile.getInputStream(entry), itemCls); strategy.importItem(context.getContentResolver(), itemsList); } @@ -342,9 +339,7 @@ public class DataImportExportUtils implements Constants { @NonNull final Class itemCls, @NonNull final List itemList) throws IOException { zos.putNextEntry(new ZipEntry(entryName)); - final JsonGenerator jsonGenerator = LoganSquare.JSON_FACTORY.createGenerator(zos); - LoganSquareMapperFinder.mapperFor(itemCls).serialize(itemList, jsonGenerator); - jsonGenerator.flush(); + JsonSerializer.serialize(itemList, zos, itemCls); zos.closeEntry(); } @@ -356,8 +351,7 @@ public class DataImportExportUtils implements Constants { throws IOException { final ZipEntry entry = zipFile.getEntry(entryName); if (entry == null) return; - final JsonMapper mapper = LoganSquareMapperFinder.mapperFor(itemCls); - T item = mapper.parse(zipFile.getInputStream(entry)); + T item = JsonSerializer.parse(zipFile.getInputStream(entry), itemCls); strategy.importItem(context.getContentResolver(), item); } @@ -367,9 +361,7 @@ public class DataImportExportUtils implements Constants { @NonNull final Class itemCls, @NonNull final T item) throws IOException { zos.putNextEntry(new ZipEntry(entryName)); - final JsonGenerator jsonGenerator = LoganSquare.JSON_FACTORY.createGenerator(zos); - LoganSquareMapperFinder.mapperFor(itemCls).serialize(item, jsonGenerator, true); - jsonGenerator.flush(); + JsonSerializer.serialize(item, zos, itemCls); zos.closeEntry(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/JsonSerializer.java b/twidere/src/main/java/org/mariotaku/twidere/util/JsonSerializer.java deleted file mode 100644 index 73d0e1240..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/util/JsonSerializer.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2015 Mariotaku Lee - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.mariotaku.twidere.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 String serialize(@Nullable final List list, final Class cls) { - if (list == null) return null; - try { - return LoganSquareMapperFinder.mapperFor(cls).serialize(list); - } catch (IOException e) { - return null; - } - } - - @Nullable - public static String serialize(@Nullable final Map list, final Class cls) { - if (list == null) return null; - try { - return LoganSquareMapperFinder.mapperFor(cls).serialize(list); - } catch (IOException e) { - return null; - } - } - - @Nullable - public static String serialize(@Nullable final T[] array, final Class cls) { - if (array == null) return null; - try { - return LoganSquareMapperFinder.mapperFor(cls).serialize(Arrays.asList(array)); - } catch (IOException e) { - return null; - } - } - - @Nullable - public static String serialize(@Nullable final T object, final Class cls) { - if (object == null) return null; - try { - return LoganSquareMapperFinder.mapperFor(cls).serialize(object); - } catch (IOException e) { - return null; - } - } - - @Nullable - public static String serialize(@Nullable final T object) { - if (object == null) return null; - try { - //noinspection unchecked - final JsonMapper mapper = (JsonMapper) - LoganSquareMapperFinder.mapperFor(object.getClass()); - return mapper.serialize(object); - } catch (IOException e) { - return null; - } - } - - @Nullable - public static T[] parseArray(@Nullable final String string, final Class cls) { - if (string == null) return null; - try { - final List 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 parse(@Nullable final String string, final Class cls) { - if (string == null) return null; - try { - return LoganSquareMapperFinder.mapperFor(cls).parse(string); - } catch (IOException e) { - return null; - } - } - - public static List parseList(File file, Class 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 List parseList(InputStream stream, Class cls) { - try { - return LoganSquareMapperFinder.mapperFor(cls).parseList(stream); - } catch (IOException e) { - return null; - } - } - - @Nullable - public static List parseList(@Nullable String json, Class cls) { - if (json == null) return null; - //noinspection TryFinallyCanBeTryWithResources - try { - return LoganSquareMapperFinder.mapperFor(cls).parseList(json); - } catch (IOException e) { - return null; - } - } - -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java index 0e8bd53ee..3b554c8b5 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java @@ -35,7 +35,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; -import android.graphics.PorterDuff.Mode; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.location.Location; @@ -68,7 +67,6 @@ import android.view.Gravity; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.Menu; -import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; @@ -90,9 +88,7 @@ import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; import org.mariotaku.twidere.annotation.CustomTabType; import org.mariotaku.twidere.extension.model.AccountDetailsExtensionsKt; -import org.mariotaku.twidere.graphic.PaddingDrawable; import org.mariotaku.twidere.model.AccountDetails; -import org.mariotaku.twidere.model.AccountPreferences; import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableUserMention; 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!"); } - 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 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, final Class cls) { @@ -878,7 +830,7 @@ public final class Utils implements Constants { 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)) return null; 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()); } - private static boolean isExtensionUseJSON(final ResolveInfo info) { + static boolean isExtensionUseJSON(final ResolveInfo info) { if (info == null || info.activityInfo == null) return false; final ActivityInfo activityInfo = info.activityInfo; 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); intent.putExtra("messageType", "PEBBLE_ALERT"); intent.putExtra("sender", appName); - intent.putExtra("notificationData", JsonSerializer.serialize(messages, PebbleMessage.class)); + intent.putExtra("notificationData", JsonSerializer.serializeList(messages, PebbleMessage.class)); context.getApplicationContext().sendBroadcast(intent); } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt index a5dfe8726..3d004e03b 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt @@ -774,12 +774,12 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher, APIEditorDi action(ACCOUNT_USER_DATA_ACTIVATED, true.toString()) action(ACCOUNT_USER_DATA_COLOR, toHexColor(color, format = HexColorFormat.RGB)) - action(ACCOUNT_USER_DATA_USER, LoganSquare.serialize(user)) - action(ACCOUNT_USER_DATA_EXTRAS, typeExtras.second?.let { LoganSquare.serialize(it) }) + action(ACCOUNT_USER_DATA_USER, JsonSerializer.serialize(user)) + action(ACCOUNT_USER_DATA_EXTRAS, typeExtras.second?.let { JsonSerializer.serialize(it) }) } 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) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountExtensions.kt index 38fd37071..f15c12d51 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountExtensions.kt @@ -6,7 +6,6 @@ import android.os.Handler import android.os.Looper import android.support.annotation.RequiresApi import android.text.TextUtils -import com.bluelinelabs.logansquare.LoganSquare import org.mariotaku.ktextension.HexColorFormat import org.mariotaku.ktextension.toHexColor 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.util.AccountUtils import org.mariotaku.twidere.model.util.AccountUtils.ACCOUNT_USER_DATA_KEYS +import org.mariotaku.twidere.util.JsonSerializer import org.mariotaku.twidere.util.ParseUtils import java.io.IOException import java.util.concurrent.FutureTask @@ -56,13 +56,13 @@ fun Account.setAccountKey(am: AccountManager, accountKey: UserKey) { } 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 return user } 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 @@ -78,10 +78,10 @@ fun Account.getAccountExtras(am: AccountManager): AccountExtras? { val json = AccountDataQueue.getUserData(am, this, ACCOUNT_USER_DATA_EXTRAS) ?: return null when (getAccountType(am)) { AccountType.TWITTER -> { - return LoganSquare.parse(json, TwitterAccountExtras::class.java) + return JsonSerializer.parse(json, TwitterAccountExtras::class.java) } AccountType.STATUSNET -> { - return LoganSquare.parse(json, StatusNetAccountExtras::class.java) + return JsonSerializer.parse(json, StatusNetAccountExtras::class.java) } } 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 { when (authType) { - Credentials.Type.OAUTH, Credentials.Type.XAUTH -> return LoganSquare.parse(authToken, OAuthCredentials::class.java) - Credentials.Type.BASIC -> return LoganSquare.parse(authToken, BasicCredentials::class.java) - Credentials.Type.EMPTY -> return LoganSquare.parse(authToken, EmptyCredentials::class.java) + Credentials.Type.OAUTH, Credentials.Type.XAUTH -> return JsonSerializer.parse(authToken, OAuthCredentials::class.java) + Credentials.Type.BASIC -> return JsonSerializer.parse(authToken, BasicCredentials::class.java) + Credentials.Type.EMPTY -> return JsonSerializer.parse(authToken, EmptyCredentials::class.java) } throw UnsupportedOperationException() } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/DraftExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/DraftExtensions.kt index 80d39ade0..8ed039fec 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/DraftExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/DraftExtensions.kt @@ -3,7 +3,6 @@ package org.mariotaku.twidere.extension.model import android.content.Context import android.net.Uri import android.text.TextUtils -import com.bluelinelabs.logansquare.LoganSquare import org.apache.james.mime4j.dom.Header import org.apache.james.mime4j.dom.MessageServiceFactory 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.SendDirectMessageActionExtras import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras +import org.mariotaku.twidere.util.JsonSerializer import org.mariotaku.twidere.util.collection.NonEmptyHashMap import java.io.File import java.io.FileOutputStream @@ -67,7 +67,7 @@ fun Draft.writeMimeMessageTo(context: Context, st: OutputStream) { this.action_extras?.let { extras -> multipart.addBodyPart(BodyPart().apply { - setText(bodyFactory.textBody(LoganSquare.serialize(extras)), "json") + setText(bodyFactory.textBody(JsonSerializer.serialize(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" -> { draft.action_extras = when (draft.action_type) { "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 -> { - LoganSquare.parse(st, SendDirectMessageActionExtras::class.java) + JsonSerializer.parse(st, SendDirectMessageActionExtras::class.java) } else -> { null diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/APIEditorDialogFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/APIEditorDialogFragment.kt index 556a8ccf4..a0c728c2a 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/APIEditorDialogFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/APIEditorDialogFragment.kt @@ -11,7 +11,6 @@ import android.support.v7.app.AlertDialog import android.view.View import android.view.ViewGroup import android.widget.* -import com.bluelinelabs.logansquare.LoganSquare import com.bumptech.glide.Glide import com.rengwuxian.materialedittext.MaterialEditText 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.model.CustomAPIConfig 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.dagger.GeneralComponentHelper import org.mariotaku.twidere.util.view.ConsumerKeySecretValidator @@ -205,7 +205,7 @@ class APIEditorDialogFragment : BaseDialogFragment() { return null } // Save to cache - return LoganSquare.parseList(response.body.stream(), CustomAPIConfig::class.java) + return JsonSerializer.parseList(response.body.stream(), CustomAPIConfig::class.java) } } catch (e: IOException) { // Ignore diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserQRDialogFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserQRDialogFragment.kt index 71b999fd0..2bcd551db 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserQRDialogFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserQRDialogFragment.kt @@ -19,12 +19,14 @@ package org.mariotaku.twidere.fragment +import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import io.nayuki.qrcodegen.QrCode +import io.nayuki.qrcodegen.QrCodeAndroid import kotlinx.android.synthetic.main.fragment_user_qr.* import org.mariotaku.twidere.R import org.mariotaku.twidere.constant.IntentConstants.EXTRA_USER @@ -46,7 +48,8 @@ class UserQRDialogFragment : BaseDialogFragment() { override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) 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.isFilterBitmap = false }) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/provider/CacheProvider.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/provider/CacheProvider.kt index b626c81cd..f6d298161 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/provider/CacheProvider.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/provider/CacheProvider.kt @@ -8,7 +8,6 @@ import android.database.Cursor import android.net.Uri import android.os.ParcelFileDescriptor import okio.ByteString -import org.mariotaku.commons.logansquare.LoganSquareMapperFinder import org.mariotaku.mediaviewer.library.FileCache import org.mariotaku.twidere.TwidereConstants.AUTHORITY_TWIDERE_CACHE 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.task.SaveFileTask import org.mariotaku.twidere.util.BitmapUtils +import org.mariotaku.twidere.util.JsonSerializer import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import java.io.ByteArrayInputStream import java.io.FileNotFoundException @@ -65,8 +65,7 @@ class CacheProvider : ContentProvider() { val bytes = fileCache.getExtra(getCacheKey(uri)) ?: return null return try { ByteArrayInputStream(bytes).use { - val mapper = LoganSquareMapperFinder.mapperFor(CacheMetadata::class.java) - return@use mapper.parse(it) + return@use JsonSerializer.parse(it, CacheMetadata::class.java) } } catch (e: IOException) { null diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/AccountMigrator.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/AccountMigrator.kt index 319435152..a5fa3ea0a 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/AccountMigrator.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/AccountMigrator.kt @@ -3,7 +3,6 @@ package org.mariotaku.twidere.util import android.accounts.Account import android.accounts.AccountManager import android.database.sqlite.SQLiteDatabase -import com.bluelinelabs.logansquare.LoganSquare import org.mariotaku.ktextension.HexColorFormat import org.mariotaku.ktextension.toHexColor 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_COLOR, toHexColor(credentials.color, format = HexColorFormat.RGB)) 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() user.account_key = credentials.account_key user.key = credentials.account_key @@ -52,7 +51,7 @@ fun migrateAccounts(am: AccountManager, db: SQLiteDatabase) { return@run user })) 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() } } finally { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt index c4067634e..9fe7f7dea 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt @@ -390,8 +390,7 @@ object DataStoreUtils { if (followingOnly) { val projection = arrayOf(Activities.SOURCES) val cur = resolver.query(uri, projection, selection.sql, selectionArgs, null) ?: return -1 - try { - val mapper = LoganSquare.mapperFor(UserFollowState::class.java) + cur.useCursor { cur -> var total = 0 cur.moveToFirst() while (!cur.isAfterLast) { @@ -399,7 +398,7 @@ object DataStoreUtils { if (TextUtils.isEmpty(string)) continue var hasFollowing = false try { - for (state in mapper.parseList(string)) { + for (state in JsonSerializer.parseList(string, UserFollowState::class.java)) { if (state.is_following) { hasFollowing = true break @@ -415,8 +414,6 @@ object DataStoreUtils { cur.moveToNext() } return total - } finally { - cur.close() } } return queryCount(resolver, uri, selection.sql, selectionArgs) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/MenuUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/MenuUtils.kt index 5c916a89a..3ed9bfc66 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/MenuUtils.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/MenuUtils.kt @@ -22,9 +22,11 @@ package org.mariotaku.twidere.util import android.accounts.AccountManager import android.app.Activity import android.content.ActivityNotFoundException -import android.content.ContentValues import android.content.Context 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.StringRes import android.support.annotation.UiThread @@ -42,8 +44,8 @@ import org.mariotaku.ktextension.Bundle import org.mariotaku.ktextension.set import org.mariotaku.ktextension.setItemChecked 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.R import org.mariotaku.twidere.TwidereConstants.* 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.ParcelableStatus 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.DestroyFavoriteTask import org.mariotaku.twidere.task.RetweetStatusTask import org.mariotaku.twidere.util.menu.TwidereMenuInfo +import java.io.IOException /** * Created by mariotaku on 15/4/12. @@ -205,9 +207,9 @@ object MenuUtils { val isOfficialKey = Utils.isOfficialCredentials(context, details) setItemAvailability(menu, R.id.translate, isOfficialKey) } - menu.removeGroup(Constants.MENU_GROUP_STATUS_EXTENSION) - Utils.addIntentToMenuForExtension(context, menu, Constants.MENU_GROUP_STATUS_EXTENSION, INTENT_ACTION_EXTENSION_OPEN_STATUS, - EXTRA_STATUS, EXTRA_STATUS_JSON, status) + menu.removeGroup(MENU_GROUP_STATUS_EXTENSION) + addIntentToMenuForExtension(context, menu, MENU_GROUP_STATUS_EXTENSION, + INTENT_ACTION_EXTENSION_OPEN_STATUS, EXTRA_STATUS, EXTRA_STATUS_JSON, status) val shareItem = menu.findItem(R.id.share) val shareProvider = MenuItemCompat.getActionProvider(shareItem) if (shareProvider is SupportStatusShareProvider) { @@ -218,8 +220,8 @@ object MenuUtils { } else if (shareItem.hasSubMenu()) { val shareSubMenu = shareItem.subMenu val shareIntent = Utils.createStatusShareIntent(context, status) - shareSubMenu.removeGroup(Constants.MENU_GROUP_STATUS_SHARE) - addIntentToMenu(context, shareSubMenu, shareIntent, Constants.MENU_GROUP_STATUS_SHARE) + shareSubMenu.removeGroup(MENU_GROUP_STATUS_SHARE) + addIntentToMenu(context, shareSubMenu, shareIntent, MENU_GROUP_STATUS_SHARE) } else { val shareIntent = Utils.createStatusShareIntent(context, status) val chooserIntent = Intent.createChooser(shareIntent, context.getString(R.string.share_status)) @@ -358,4 +360,55 @@ object MenuUtils { } 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 + } + } + + } + } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/cache/JsonCache.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/cache/JsonCache.kt index 40cf01d9b..7eb3f342b 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/cache/JsonCache.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/cache/JsonCache.kt @@ -19,9 +19,9 @@ package org.mariotaku.twidere.util.cache -import com.bluelinelabs.logansquare.LoganSquare import com.bumptech.glide.disklrucache.DiskLruCache import org.mariotaku.twidere.BuildConfig +import org.mariotaku.twidere.util.JsonSerializer import java.io.File import java.io.IOException @@ -39,7 +39,7 @@ class JsonCache(val cacheDir: File) { fun getList(key: String, cls: Class): List? { 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 try { editor.getFile(0)?.outputStream()?.use { - LoganSquare.serialize(list, it, cls) + JsonSerializer.serialize(list, it, cls) } editor.commit() } finally { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/filter/LocalFiltersSubscriptionProvider.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/filter/LocalFiltersSubscriptionProvider.kt index 6e27b4d08..920a8c736 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/filter/LocalFiltersSubscriptionProvider.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/filter/LocalFiltersSubscriptionProvider.kt @@ -2,6 +2,7 @@ package org.mariotaku.twidere.util.filter import android.content.Context import org.mariotaku.twidere.util.JsonSerializer +import java.io.IOException /** * Created by mariotaku on 2017/1/9. @@ -13,8 +14,11 @@ abstract class LocalFiltersSubscriptionProvider(val context: Context) : FiltersS when (name) { "url" -> { if (arguments == null) return null - val argsObj = JsonSerializer.parse(arguments, - UrlFiltersSubscriptionProvider.Arguments::class.java) ?: return null + val argsObj = try { + JsonSerializer.parse(arguments, UrlFiltersSubscriptionProvider.Arguments::class.java) + } catch (e: IOException) { + return null + } return UrlFiltersSubscriptionProvider(context, argsObj) } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/filter/UrlFiltersSubscriptionProvider.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/filter/UrlFiltersSubscriptionProvider.kt index 8c3ea09a2..f0b5fc90b 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/filter/UrlFiltersSubscriptionProvider.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/filter/UrlFiltersSubscriptionProvider.kt @@ -14,6 +14,7 @@ import org.mariotaku.twidere.extension.model.parse import org.mariotaku.twidere.extension.newPullParser import org.mariotaku.twidere.model.FiltersData import org.mariotaku.twidere.util.ETagCache +import org.mariotaku.twidere.util.JsonSerializer import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import java.io.IOException import javax.inject.Inject @@ -101,7 +102,7 @@ class UrlFiltersSubscriptionProvider(context: Context, val arguments: Arguments) } private fun Body.toJsonFilters(): FiltersData? { - return LoganSquare.parse(stream(), FiltersData::class.java) + return JsonSerializer.parse(stream(), FiltersData::class.java) } private fun Body.toXmlFilters(): FiltersData? { diff --git a/twidere/src/main/res/layout/fragment_user_qr.xml b/twidere/src/main/res/layout/fragment_user_qr.xml index 9cd760eab..2a925cc46 100644 --- a/twidere/src/main/res/layout/fragment_user_qr.xml +++ b/twidere/src/main/res/layout/fragment_user_qr.xml @@ -21,8 +21,7 @@ + android:layout_height="wrap_content"> \ No newline at end of file