From 00c0740d382d0d107c6dd12a88f511d224446209 Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Thu, 4 Dec 2014 22:31:29 +0800 Subject: [PATCH] added border to user type icon redesigning settings screen removed staggered grid support, will use new implementation soon redesigning direct message entries list --- resources/ic_user_type_protected.png | Bin 0 -> 17270 bytes resources/ic_user_type_verified.png | Bin 20145 -> 29956 bytes twidere/build.gradle | 1 - .../mariotaku/querybuilder/AllColumns.java | 28 +- .../org/mariotaku/querybuilder/Columns.java | 109 ++-- .../mariotaku/querybuilder/Expression.java | 124 ++++ .../org/mariotaku/querybuilder/NewColumn.java | 53 +- .../mariotaku/querybuilder/OnConflict.java | 17 + .../org/mariotaku/querybuilder/OrderBy.java | 23 +- .../mariotaku/querybuilder/RawItemArray.java | 30 +- .../mariotaku/querybuilder/SQLFunctions.java | 10 +- .../org/mariotaku/querybuilder/SQLLang.java | 18 +- .../org/mariotaku/querybuilder/SQLQuery.java | 7 + .../querybuilder/SQLQueryBuilder.java | 109 ++-- .../querybuilder/SQLQueryException.java | 30 +- .../mariotaku/querybuilder/Selectable.java | 6 +- .../org/mariotaku/querybuilder/SetValue.java | 23 + .../org/mariotaku/querybuilder/Table.java | 45 ++ .../org/mariotaku/querybuilder/Tables.java | 25 +- .../org/mariotaku/querybuilder/Utils.java | 53 +- .../org/mariotaku/querybuilder/Where.java | 103 ---- .../querybuilder/query/IBuilder.java | 14 +- .../query/SQLAlterTableQuery.java | 103 ++-- .../query/SQLCreateTableQuery.java | 168 ++--- .../query/SQLCreateTriggerQuery.java | 201 ++++++ .../query/SQLCreateViewQuery.java | 138 ++--- .../querybuilder/query/SQLDeleteQuery.java | 84 +-- .../querybuilder/query/SQLDropQuery.java | 27 +- .../querybuilder/query/SQLDropTableQuery.java | 21 +- .../query/SQLDropTriggerQuery.java | 9 + .../querybuilder/query/SQLDropViewQuery.java | 21 +- .../query/SQLInsertIntoQuery.java | 106 ---- .../querybuilder/query/SQLInsertQuery.java | 94 +++ .../querybuilder/query/SQLSelectQuery.java | 371 +++++------ .../querybuilder/query/SQLUpdateQuery.java | 100 +++ .../mariotaku/twidere/TwidereConstants.java | 2 +- .../twidere/activity/FiltersActivity.java | 72 ++- .../twidere/activity/SettingsActivity.java | 12 +- .../activity/support/ComposeActivity.java | 33 +- .../support/CustomTabEditorActivity.java | 6 +- .../activity/support/DraftsActivity.java | 8 +- .../activity/support/HomeActivity.java | 6 +- .../activity/support/LinkHandlerActivity.java | 5 +- .../twidere/adapter/AbsStatusesAdapter.java | 59 +- .../twidere/adapter/AccountsAdapter.java | 8 +- .../adapter/AccountsSpinnerAdapter.java | 10 +- .../adapter/CursorStatusesAdapter.java | 5 - .../adapter/CursorStatusesListAdapter.java | 10 +- ...rectMessageConversationEntriesAdapter.java | 24 +- .../adapter/ParcelableStatusesAdapter.java | 5 - .../ParcelableStatusesListAdapter.java | 10 +- .../UserHashtagAutoCompleteAdapter.java | 4 +- ...dapter.java => ICardSupportedAdapter.java} | 6 +- .../adapter/iface/IGapSupportedAdapter.java | 4 +- .../adapter/iface/IStatusesAdapter.java | 3 +- .../twidere/constant/IntentConstants.java | 2 +- .../constant/SharedPreferenceConstants.java | 2 +- .../AccountNotificationSettingsFragment.java | 4 +- .../BaseAccountPreferenceFragment.java | 6 +- .../twidere/fragment/BaseFiltersFragment.java | 4 +- .../twidere/fragment/CustomTabsFragment.java | 6 +- .../fragment/support/AbsStatusesFragment.java | 246 +++++--- .../support/AccountsDashboardFragment.java | 314 +++++----- .../support/AccountsManagerFragment.java | 10 +- ...asePullToRefreshStaggeredGridFragment.java | 211 ------- .../support/BaseStatusesListFragment.java | 8 +- .../BaseStatusesStaggeredGridFragment.java | 578 ------------------ .../fragment/support/BaseSupportFragment.java | 3 +- .../BaseSupportStaggeredGridFragment.java | 277 --------- .../support/CursorStatusesFragment.java | 116 ++++ .../support/CursorStatusesListFragment.java | 229 ------- .../CursorStatusesStaggeredGridFragment.java | 213 ------- .../DirectMessagesConversationFragment.java | 10 +- .../support/DirectMessagesFragment.java | 9 +- .../support/HomeTimelineFragment.java | 58 +- .../support/HomeTimelineListFragment.java | 98 --- ...IncomingFriendshipsMenuDialogFragment.java | 8 +- .../support/MentionsTimelineFragment.java | 124 ++-- .../support/ParcelableStatusesFragment.java | 66 +- .../ParcelableStatusesListFragment.java | 4 +- .../fragment/support/SearchFragment.java | 9 + .../support/StaggeredGridFragment.java | 364 ----------- .../StaggeredHomeTimelineFragment.java | 103 ---- .../fragment/support/StatusFragment.java | 16 +- .../support/UserFavoritesFragment.java | 5 +- .../fragment/support/UserFragment.java | 12 +- .../fragment/support/UserListFragment.java | 167 +++-- .../support/UserListTimelineFragment.java | 72 +-- .../support/UserListTimelineListFragment.java | 75 +++ .../twidere/menu/AccountActionProvider.java | 10 +- .../twidere/model/AccountPreferences.java | 2 +- .../model/CustomTabConfiguration2.java | 176 ------ .../mariotaku/twidere/model/DraftItem.java | 2 +- .../{Account.java => ParcelableAccount.java} | 68 +-- .../twidere/model/ParcelableStatusUpdate.java | 12 +- .../preference/AccountsListPreference.java | 22 +- .../AutoRefreshAccountsListPreference.java | 4 +- .../NotificationAccountsListPreference.java | 4 +- .../provider/TwidereCommandProvider.java | 2 +- .../twidere/provider/TwidereDataProvider.java | 12 +- .../service/BackgroundOperationService.java | 6 +- .../twidere/service/RefreshService.java | 4 +- .../twidere/task/CacheUsersStatusesTask.java | 4 +- .../twidere/util/AsyncTwitterWrapper.java | 59 +- .../twidere/util/ContentValuesCreator.java | 4 +- .../twidere/util/CustomTabUtils.java | 6 - .../twidere/util/ImageLoaderWrapper.java | 18 +- .../twidere/util/MultiSelectEventHandler.java | 4 +- .../twidere/util/TwidereQueryBuilder.java | 46 +- .../org/mariotaku/twidere/util/Utils.java | 163 +++-- .../util/content/DatabaseUpgradeHelper.java | 276 ++++----- .../imageloader/TwidereImageDownloader.java | 8 +- .../view/BottomDividerFrameLayout.java | 71 +++ .../view/RefreshNowStaggeredGridView.java | 101 --- .../holder/DirectMessageEntryViewHolder.java | 88 ++- .../twidere/view/holder/StatusViewHolder.java | 17 +- .../drawable-hdpi/ic_user_type_protected.png | Bin 1042 -> 1540 bytes .../drawable-hdpi/ic_user_type_verified.png | Bin 1456 -> 1661 bytes .../drawable-mdpi/ic_user_type_protected.png | Bin 711 -> 964 bytes .../drawable-mdpi/ic_user_type_verified.png | Bin 851 -> 1051 bytes .../drawable-xhdpi/ic_user_type_protected.png | Bin 1404 -> 2065 bytes .../drawable-xhdpi/ic_user_type_verified.png | Bin 2018 -> 2305 bytes .../ic_user_type_protected.png | Bin 2174 -> 3515 bytes .../drawable-xxhdpi/ic_user_type_verified.png | Bin 3435 -> 4121 bytes .../list_item_preference_header_category.xml | 43 ++ .../list_item_preference_header_item.xml | 75 +++ .../list_item_preference_header_space.xml | 23 + .../src/main/res/layout/activity_filters.xml | 1 + .../res/layout/card_item_message_entry.xml | 95 --- .../card_item_message_entry_compact.xml | 98 --- .../res/layout/fragment_content_pages.xml | 4 +- .../layout/header_drawer_account_selector.xml | 3 +- twidere/src/main/res/layout/header_user.xml | 8 +- .../res/layout/list_item_message_entry.xml | 70 +++ .../list_item_preference_header_category.xml | 22 +- .../list_item_preference_header_item.xml | 79 ++- .../layout/refreshnow_staggered_gridview.xml | 7 - .../main/res/layout/staggered_gridview.xml | 7 - twidere/src/main/res/values-v21/dimens.xml | 23 + twidere/src/main/res/values/attrs.xml | 1 - twidere/src/main/res/values/dimens.xml | 1 + twidere/src/main/res/values/styles.xml | 5 - twidere/src/main/res/values/themes.xml | 4 - 143 files changed, 3142 insertions(+), 4608 deletions(-) create mode 100644 resources/ic_user_type_protected.png create mode 100644 twidere/src/main/java/org/mariotaku/querybuilder/Expression.java create mode 100644 twidere/src/main/java/org/mariotaku/querybuilder/OnConflict.java create mode 100644 twidere/src/main/java/org/mariotaku/querybuilder/SQLQuery.java create mode 100644 twidere/src/main/java/org/mariotaku/querybuilder/SetValue.java create mode 100644 twidere/src/main/java/org/mariotaku/querybuilder/Table.java delete mode 100644 twidere/src/main/java/org/mariotaku/querybuilder/Where.java create mode 100644 twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTriggerQuery.java create mode 100644 twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropTriggerQuery.java delete mode 100644 twidere/src/main/java/org/mariotaku/querybuilder/query/SQLInsertIntoQuery.java create mode 100644 twidere/src/main/java/org/mariotaku/querybuilder/query/SQLInsertQuery.java create mode 100644 twidere/src/main/java/org/mariotaku/querybuilder/query/SQLUpdateQuery.java rename twidere/src/main/java/org/mariotaku/twidere/adapter/iface/{IItemMenuSupportedAdapter.java => ICardSupportedAdapter.java} (84%) delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/fragment/support/BasePullToRefreshStaggeredGridFragment.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesStaggeredGridFragment.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportStaggeredGridFragment.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesListFragment.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesStaggeredGridFragment.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/fragment/support/HomeTimelineListFragment.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/fragment/support/StaggeredGridFragment.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/fragment/support/StaggeredHomeTimelineFragment.java create mode 100644 twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListTimelineListFragment.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/model/CustomTabConfiguration2.java rename twidere/src/main/java/org/mariotaku/twidere/model/{Account.java => ParcelableAccount.java} (81%) create mode 100644 twidere/src/main/java/org/mariotaku/twidere/view/BottomDividerFrameLayout.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/view/RefreshNowStaggeredGridView.java create mode 100644 twidere/src/main/res/layout-v21/list_item_preference_header_category.xml create mode 100644 twidere/src/main/res/layout-v21/list_item_preference_header_item.xml create mode 100644 twidere/src/main/res/layout-v21/list_item_preference_header_space.xml delete mode 100644 twidere/src/main/res/layout/card_item_message_entry.xml delete mode 100644 twidere/src/main/res/layout/card_item_message_entry_compact.xml create mode 100644 twidere/src/main/res/layout/list_item_message_entry.xml delete mode 100644 twidere/src/main/res/layout/refreshnow_staggered_gridview.xml delete mode 100644 twidere/src/main/res/layout/staggered_gridview.xml create mode 100644 twidere/src/main/res/values-v21/dimens.xml diff --git a/resources/ic_user_type_protected.png b/resources/ic_user_type_protected.png new file mode 100644 index 0000000000000000000000000000000000000000..34f11cfb6dd7b5a21c2189b689e475e26f012934 GIT binary patch literal 17270 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 literal 0 HcmV?d00001 diff --git a/resources/ic_user_type_verified.png b/resources/ic_user_type_verified.png index d4c3f7d22908fe5e00770dbb2d850fb39c263d6c..c44eff6e6116739ba6e7f05df31f0703a088c463 100755 GIT binary patch literal 29956 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 literal 20145 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 */ @@ -29,19 +29,19 @@ package org.mariotaku.querybuilder; public class AllColumns implements Selectable { - private final String table; + private final String table; - public AllColumns() { - this(null); - } + public AllColumns() { + this(null); + } - public AllColumns(final String table) { - this.table = table; - } + public AllColumns(final String table) { + this.table = table; + } - @Override - public String getSQL() { - return table != null ? table + ".*" : "*"; - } + @Override + public String getSQL() { + return table != null ? table + ".*" : "*"; + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/Columns.java b/twidere/src/main/java/org/mariotaku/querybuilder/Columns.java index 858cb2fbb..fcc485036 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/Columns.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/Columns.java @@ -1,11 +1,11 @@ /** * This is free and unencumbered software released into the public domain. - * + * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. - * + * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit @@ -21,7 +21,7 @@ * 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. - * + * * For more information, please refer to */ @@ -29,64 +29,81 @@ package org.mariotaku.querybuilder; public class Columns implements Selectable { - private final AbsColumn[] columns; + private final AbsColumn[] columns; - public Columns(final AbsColumn... columns) { - this.columns = columns; - } + public Columns(String... columns) { + this(Columns.fromStrings(columns)); + } - @Override - public String getSQL() { - return Utils.toString(columns); - } + public Columns(final AbsColumn... columns) { + this.columns = columns; + } - public abstract static class AbsColumn implements Selectable { + private static Column[] fromStrings(String... columnsString) { + final Column[] columns = new Column[columnsString.length]; + for (int i = 0, j = columnsString.length; i < j; i++) { + columns[i] = new Column(columnsString[i]); + } + return columns; + } - } + @Override + public String getSQL() { + return Utils.toString(columns, ',', true); + } - public static class AllColumn extends AbsColumn { + public abstract static class AbsColumn implements Selectable { - private final String table; + } - public AllColumn() { - this(null); - } + public static class AllColumn extends AbsColumn { - public AllColumn(final String table) { - this.table = table; - } + private final Table table; - @Override - public String getSQL() { - return table != null ? table + ".*" : "*"; - } + public AllColumn() { + this(null); + } - } + public AllColumn(final Table table) { + this.table = table; + } - public static class Column extends AbsColumn { + @Override + public String getSQL() { + return table != null ? table.getSQL() + ".*" : "*"; + } - private final String table, columnName, alias; + } - public Column(final String columnName) { - this(null, columnName, null); - } + public static class Column extends AbsColumn { - public Column(final String columnName, final String alias) { - this(null, columnName, alias); - } + private final Table table; + private final String columnName, alias; - public Column(final String table, final String columnName, final String alias) { - if (columnName == null) throw new IllegalArgumentException(""); - this.table = table; - this.columnName = columnName; - this.alias = alias; - } + public Column(final String columnName) { + this(null, columnName, null); + } - @Override - public String getSQL() { - final String col = table != null ? table + "." + columnName : columnName; - return alias != null ? col + " AS " + alias : col; - } - } + public Column(final String columnName, final String alias) { + this(null, columnName, alias); + } + + public Column(final Table table, final String columnName) { + this(table, columnName, null); + } + + public Column(final Table table, final String columnName, final String alias) { + if (columnName == null) throw new IllegalArgumentException(""); + this.table = table; + this.columnName = columnName; + this.alias = alias; + } + + @Override + public String getSQL() { + final String col = table != null ? table.getSQL() + "." + columnName : columnName; + return alias != null ? col + " AS " + alias : col; + } + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/Expression.java b/twidere/src/main/java/org/mariotaku/querybuilder/Expression.java new file mode 100644 index 000000000..12b183ab5 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/querybuilder/Expression.java @@ -0,0 +1,124 @@ +/** + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + + * 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 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. + * + * For more information, please refer to + */ + +package org.mariotaku.querybuilder; + +import org.mariotaku.querybuilder.Columns.Column; + +import java.util.Locale; + +public class Expression implements SQLLang { + private final String expr; + + public Expression(final String expr) { + this.expr = expr; + } + + public Expression(SQLLang lang) { + this(lang.getSQL()); + } + + public static Expression and(final Expression... expressions) { + return new Expression(toExpr(expressions, "AND")); + } + + public static Expression equals(final Column l, final Column r) { + return new Expression(String.format(Locale.US, "%s = %s", l.getSQL(), r.getSQL())); + } + + public static Expression equals(final Column l, final long r) { + return new Expression(String.format(Locale.US, "%s = %d", l.getSQL(), r)); + } + + public static Expression equals(final Column l, final String r) { + return new Expression(String.format(Locale.US, "%s = '%s'", l.getSQL(), r)); + } + + public static Expression equals(final String l, final long r) { + return new Expression(String.format(Locale.US, "%s = %d", l, r)); + } + + public static Expression in(final Column column, final Selectable in) { + return new Expression(String.format("%s IN(%s)", column.getSQL(), in.getSQL())); + } + + public static Expression notEquals(final String l, final long r) { + return new Expression(String.format(Locale.US, "%s != %d", l, r)); + } + + public static Expression notEquals(final String l, final String r) { + return new Expression(String.format("%s != %s", l, r)); + } + + public static Expression notIn(final Column column, final Selectable in) { + return new Expression(String.format("%s NOT IN(%s)", column.getSQL(), in.getSQL())); + } + + public static Expression notNull(final Column column) { + return new Expression(String.format("%s NOT NULL", column.getSQL())); + } + + public static Expression or(final Expression... expressions) { + return new Expression(toExpr(expressions, "OR")); + } + + private static String toExpr(final Expression[] array, final String token) { + final StringBuilder builder = new StringBuilder(); + builder.append('('); + final int length = array.length; + for (int i = 0; i < length; i++) { + if (i > 0) { + builder.append(String.format(" %s ", token)); + } + builder.append(array[i].getSQL()); + } + builder.append(')'); + return builder.toString(); + } + + public static Expression equalsArgs(String l) { + return new Expression(String.format(Locale.US, "%s = ?", l)); + } + + public static Expression isNull(Column column) { + return new Expression(String.format(Locale.ROOT, "%s IS NULL", column.getSQL())); + } + + public static Expression greaterThan(Column column1, Column column2) { + return new Expression(String.format(Locale.ROOT, "%s > %s", column1.getSQL(), column2.getSQL())); + } + + public static Expression like(final Column l, final String r) { + return new Expression(String.format(Locale.US, "%s LIKE '%s'", l.getSQL(), r)); + } + + + @Override + public String getSQL() { + return expr; + } +} diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/NewColumn.java b/twidere/src/main/java/org/mariotaku/querybuilder/NewColumn.java index 266f24cec..c1fe99a62 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/NewColumn.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/NewColumn.java @@ -2,36 +2,37 @@ package org.mariotaku.querybuilder; public class NewColumn implements SQLLang { - private final String name; - private final String type; + private final String name; + private final String type; - public NewColumn(final String name, final String type) { - this.name = name; - this.type = type; - } + public NewColumn(final String name, final String type) { + this.name = name; + this.type = type; + } - public String getName() { - return name; - } + public static NewColumn[] createNewColumns(final String[] colNames, final String[] colTypes) { + if (colNames == null || colTypes == null || colNames.length != colTypes.length) + throw new IllegalArgumentException("length of columns and types not match."); + final NewColumn[] newColumns = new NewColumn[colNames.length]; + for (int i = 0, j = colNames.length; i < j; i++) { + newColumns[i] = new NewColumn(colNames[i], colTypes[i]); + } + return newColumns; + } - @Override - public String getSQL() { - if (name == null || type == null) throw new NullPointerException("name and type must not be null!"); - return String.format("%s %s", name, type); - } + public String getName() { + return name; + } - public String getType() { - return type; - } + @Override + public String getSQL() { + if (name == null || type == null) + throw new NullPointerException("name and type must not be null!"); + return String.format("%s %s", name, type); + } - public static NewColumn[] createNewColumns(final String[] colNames, final String[] colTypes) { - if (colNames == null || colTypes == null || colNames.length != colTypes.length) - throw new IllegalArgumentException("length of columns and types not match."); - final NewColumn[] newColumns = new NewColumn[colNames.length]; - for (int i = 0, j = colNames.length; i < j; i++) { - newColumns[i] = new NewColumn(colNames[i], colTypes[i]); - } - return newColumns; - } + public String getType() { + return type; + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/OnConflict.java b/twidere/src/main/java/org/mariotaku/querybuilder/OnConflict.java new file mode 100644 index 000000000..6c9b638a3 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/querybuilder/OnConflict.java @@ -0,0 +1,17 @@ +package org.mariotaku.querybuilder; + +/** + * Created by mariotaku on 14-8-7. + */ +public enum OnConflict { + ROLLBACK("ROLLBACK"), ABORT("ABORT"), REPLACE("REPLACE"), FAIL("FAIL"), IGNORE("IGNORE"); + private final String action; + + OnConflict(final String action) { + this.action = action; + } + + public String getAction() { + return action; + } +} diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/OrderBy.java b/twidere/src/main/java/org/mariotaku/querybuilder/OrderBy.java index f7c5096db..d593d5036 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/OrderBy.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/OrderBy.java @@ -1,11 +1,11 @@ /** * This is free and unencumbered software released into the public domain. - * + * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. - * + * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit @@ -21,25 +21,26 @@ * 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. - * + * * For more information, please refer to */ package org.mariotaku.querybuilder; + import org.mariotaku.twidere.util.ArrayUtils; public class OrderBy implements SQLLang { - private final String[] orderBy; + private final String[] orderBy; - public OrderBy(final String... orderBy) { - this.orderBy = orderBy; - } + public OrderBy(final String... orderBy) { + this.orderBy = orderBy; + } - @Override - public String getSQL() { - return ArrayUtils.toString(orderBy, ',', false); - } + @Override + public String getSQL() { + return ArrayUtils.toString(orderBy, ',', false); + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/RawItemArray.java b/twidere/src/main/java/org/mariotaku/querybuilder/RawItemArray.java index 5fbd410d5..648e8a45d 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/RawItemArray.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/RawItemArray.java @@ -2,23 +2,23 @@ package org.mariotaku.querybuilder; public class RawItemArray implements Selectable { - private final Object[] array; + private final Object[] array; - public RawItemArray(final long[] array) { - final Long[] converted = new Long[array.length]; - for (int i = 0, j = array.length; i < j; i++) { - converted[i] = array[i]; - } - this.array = converted; - } + public RawItemArray(final long[] array) { + final Long[] converted = new Long[array.length]; + for (int i = 0, j = array.length; i < j; i++) { + converted[i] = array[i]; + } + this.array = converted; + } - public RawItemArray(final Object[] array) { - this.array = array; - } + public RawItemArray(final String[] array) { + this.array = array; + } - @Override - public String getSQL() { - return Utils.toString(array, ',', false); - } + @Override + public String getSQL() { + return Utils.toString(array, ',', false); + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/SQLFunctions.java b/twidere/src/main/java/org/mariotaku/querybuilder/SQLFunctions.java index 0d42e41ba..903018b06 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/SQLFunctions.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/SQLFunctions.java @@ -2,8 +2,12 @@ package org.mariotaku.querybuilder; public class SQLFunctions { - public static String SUM(final String val) { - return String.format("SUM (%s)", val); - } + public static String SUM(final String val) { + return String.format("SUM (%s)", val); + } + + public static String COUNT(final String val) { + return String.format("COUNT (%s)", val); + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/SQLLang.java b/twidere/src/main/java/org/mariotaku/querybuilder/SQLLang.java index 4ad782d3d..de444fae6 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/SQLLang.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/SQLLang.java @@ -1,11 +1,11 @@ /** * This is free and unencumbered software released into the public domain. - * + * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. - * + * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit @@ -21,7 +21,7 @@ * 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. - * + * * For more information, please refer to */ @@ -29,10 +29,10 @@ package org.mariotaku.querybuilder; public interface SQLLang extends Cloneable { - /** - * Build SQL query string - * - * @return SQL query - */ - public String getSQL(); + /** + * Build SQL query string + * + * @return SQL query + */ + public String getSQL(); } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/SQLQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/SQLQuery.java new file mode 100644 index 000000000..7f64967b5 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/querybuilder/SQLQuery.java @@ -0,0 +1,7 @@ +package org.mariotaku.querybuilder; + +/** + * Created by mariotaku on 14-8-6. + */ +public interface SQLQuery extends SQLLang { +} diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/SQLQueryBuilder.java b/twidere/src/main/java/org/mariotaku/querybuilder/SQLQueryBuilder.java index cf034ce12..5f00d7b44 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/SQLQueryBuilder.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/SQLQueryBuilder.java @@ -1,11 +1,11 @@ /** * This is free and unencumbered software released into the public domain. - * + * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. - * + * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit @@ -21,7 +21,7 @@ * 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. - * + * * For more information, please refer to */ @@ -29,70 +29,81 @@ package org.mariotaku.querybuilder; import org.mariotaku.querybuilder.query.SQLAlterTableQuery; import org.mariotaku.querybuilder.query.SQLCreateTableQuery; +import org.mariotaku.querybuilder.query.SQLCreateTriggerQuery; import org.mariotaku.querybuilder.query.SQLCreateViewQuery; import org.mariotaku.querybuilder.query.SQLDropTableQuery; +import org.mariotaku.querybuilder.query.SQLDropTriggerQuery; import org.mariotaku.querybuilder.query.SQLDropViewQuery; -import org.mariotaku.querybuilder.query.SQLInsertIntoQuery; -import org.mariotaku.querybuilder.query.SQLInsertIntoQuery.OnConflict; +import org.mariotaku.querybuilder.query.SQLInsertQuery; import org.mariotaku.querybuilder.query.SQLSelectQuery; public class SQLQueryBuilder { - private SQLQueryBuilder() { - throw new AssertionError("You can't create instance for this class"); - } + private SQLQueryBuilder() { + throw new AssertionError("You can't create instance for this class"); + } - public static SQLAlterTableQuery.Builder alterTable(final String table) { - return new SQLAlterTableQuery.Builder().alterTable(table); - } + public static SQLAlterTableQuery.Builder alterTable(final String table) { + return new SQLAlterTableQuery.Builder().alterTable(table); + } - public static SQLCreateTableQuery.Builder createTable(final boolean temporary, final boolean createIfNotExists, - final String name) { - return new SQLCreateTableQuery.Builder().createTable(temporary, createIfNotExists, name); - } + public static SQLCreateTableQuery.Builder createTable(final boolean temporary, final boolean createIfNotExists, + final String name) { + return new SQLCreateTableQuery.Builder().createTable(temporary, createIfNotExists, name); + } - public static SQLCreateTableQuery.Builder createTable(final boolean createIfNotExists, final String name) { - return createTable(false, createIfNotExists, name); - } + public static SQLCreateTableQuery.Builder createTable(final boolean createIfNotExists, final String name) { + return createTable(false, createIfNotExists, name); + } - public static SQLCreateTableQuery.Builder createTable(final String name) { - return createTable(false, false, name); - } + public static SQLCreateTableQuery.Builder createTable(final String name) { + return createTable(false, false, name); + } - public static SQLCreateViewQuery.Builder createView(final boolean temporary, final boolean createIfNotExists, - final String name) { - return new SQLCreateViewQuery.Builder().createView(temporary, createIfNotExists, name); - } + public static SQLCreateViewQuery.Builder createView(final boolean temporary, final boolean createIfNotExists, + final String name) { + return new SQLCreateViewQuery.Builder().createView(temporary, createIfNotExists, name); + } - public static SQLCreateViewQuery.Builder createView(final boolean createIfNotExists, final String name) { - return createView(false, createIfNotExists, name); - } - public static SQLCreateViewQuery.Builder createView(final String name) { - return createView(false, false, name); - } + public static SQLCreateTriggerQuery.Builder createTrigger(final boolean temporary, final boolean createIfNotExists, + final String name) { + return new SQLCreateTriggerQuery.Builder().createTrigger(temporary, createIfNotExists, name); + } - public static SQLDropTableQuery dropTable(final boolean dropIfExists, final String table) { - return new SQLDropTableQuery(dropIfExists, table); - } + public static SQLCreateViewQuery.Builder createView(final boolean createIfNotExists, final String name) { + return createView(false, createIfNotExists, name); + } - public static SQLDropViewQuery dropView(final boolean dropIfExists, final String table) { - return new SQLDropViewQuery(dropIfExists, table); - } + public static SQLCreateViewQuery.Builder createView(final String name) { + return createView(false, false, name); + } - public static SQLInsertIntoQuery.Builder insertInto(final OnConflict onConflict, final String table) { - return new SQLInsertIntoQuery.Builder().insertInto(onConflict, table); - } + public static SQLDropTableQuery dropTable(final boolean dropIfExists, final String table) { + return new SQLDropTableQuery(dropIfExists, table); + } - public static SQLInsertIntoQuery.Builder insertInto(final String table) { - return insertInto(null, table); - } + public static SQLDropViewQuery dropView(final boolean dropIfExists, final String table) { + return new SQLDropViewQuery(dropIfExists, table); + } - public static SQLSelectQuery.Builder select(final boolean distinct, final Selectable select) { - return new SQLSelectQuery.Builder().select(distinct, select); - } + public static SQLDropTriggerQuery dropTrigger(final boolean dropIfExists, final String table) { + return new SQLDropTriggerQuery(dropIfExists, table); + } - public static SQLSelectQuery.Builder select(final Selectable select) { - return select(false, select); - } + public static SQLInsertQuery.Builder insertInto(final OnConflict onConflict, final String table) { + return new SQLInsertQuery.Builder().insertInto(onConflict, table); + } + + public static SQLInsertQuery.Builder insertInto(final String table) { + return insertInto(null, table); + } + + public static SQLSelectQuery.Builder select(final boolean distinct, final Selectable select) { + return new SQLSelectQuery.Builder().select(distinct, select); + } + + public static SQLSelectQuery.Builder select(final Selectable select) { + return select(false, select); + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/SQLQueryException.java b/twidere/src/main/java/org/mariotaku/querybuilder/SQLQueryException.java index 619bd7158..829c7573f 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/SQLQueryException.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/SQLQueryException.java @@ -1,11 +1,11 @@ /** * This is free and unencumbered software released into the public domain. - * + * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. - * + * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit @@ -21,7 +21,7 @@ * 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. - * + * * For more information, please refer to */ @@ -29,21 +29,21 @@ package org.mariotaku.querybuilder; public class SQLQueryException extends RuntimeException { - private static final long serialVersionUID = 910158450604676104L; + private static final long serialVersionUID = 910158450604676104L; - public SQLQueryException() { - } + public SQLQueryException() { + } - public SQLQueryException(final String detailMessage) { - super(detailMessage); - } + public SQLQueryException(final String detailMessage) { + super(detailMessage); + } - public SQLQueryException(final String detailMessage, final Throwable throwable) { - super(detailMessage, throwable); - } + public SQLQueryException(final String detailMessage, final Throwable throwable) { + super(detailMessage, throwable); + } - public SQLQueryException(final Throwable throwable) { - super(throwable); - } + public SQLQueryException(final Throwable throwable) { + super(throwable); + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/Selectable.java b/twidere/src/main/java/org/mariotaku/querybuilder/Selectable.java index f7414cc9a..2a154572e 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/Selectable.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/Selectable.java @@ -1,11 +1,11 @@ /** * This is free and unencumbered software released into the public domain. - * + * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. - * + * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit @@ -21,7 +21,7 @@ * 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. - * + * * For more information, please refer to */ diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/SetValue.java b/twidere/src/main/java/org/mariotaku/querybuilder/SetValue.java new file mode 100644 index 000000000..d509f8a3b --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/querybuilder/SetValue.java @@ -0,0 +1,23 @@ +package org.mariotaku.querybuilder; + +import java.util.Locale; + +/** + * Created by mariotaku on 14-8-7. + */ +public class SetValue implements SQLLang { + + private final Columns.Column column; + private final Expression expression; + + public SetValue(Columns.Column column, Expression expression) { + this.column = column; + this.expression = expression; + } + + + @Override + public String getSQL() { + return String.format(Locale.ROOT, "%s = %s", column.getSQL(), expression.getSQL()); + } +} diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/Table.java b/twidere/src/main/java/org/mariotaku/querybuilder/Table.java new file mode 100644 index 000000000..d224e1bfb --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/querybuilder/Table.java @@ -0,0 +1,45 @@ +/** + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + + * 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 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. + * + * For more information, please refer to + */ + +package org.mariotaku.querybuilder; + +public class Table implements Selectable { + + public static final Table NEW = new Table("NEW"); + + private final String table; + + public Table(final String table) { + this.table = table; + } + + @Override + public String getSQL() { + return table; + } + +} diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/Tables.java b/twidere/src/main/java/org/mariotaku/querybuilder/Tables.java index c214dad8b..f489979c0 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/Tables.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/Tables.java @@ -1,11 +1,11 @@ /** * This is free and unencumbered software released into the public domain. - * + * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. - * + * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit @@ -21,23 +21,24 @@ * 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. - * + * * For more information, please refer to */ package org.mariotaku.querybuilder; -public class Tables implements Selectable { +public class Tables extends Table { - private final String[] tables; + private final String[] tables; - public Tables(final String... tables) { - this.tables = tables; - } + public Tables(final String... tables) { + super(null); + this.tables = tables; + } - @Override - public String getSQL() { - return Utils.toString(tables, ',', false); - } + @Override + public String getSQL() { + return Utils.toString(tables, ',', false); + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/Utils.java b/twidere/src/main/java/org/mariotaku/querybuilder/Utils.java index 246764b2f..c21568ce2 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/Utils.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/Utils.java @@ -1,11 +1,11 @@ /** * This is free and unencumbered software released into the public domain. - * + * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. - * + * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit @@ -21,7 +21,7 @@ * 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. - * + * * For more information, please refer to */ @@ -29,34 +29,25 @@ package org.mariotaku.querybuilder; public class Utils { - public static String toString(final Object[] array, final char token, final boolean include_space) { - final StringBuilder builder = new StringBuilder(); - final int length = array.length; - for (int i = 0; i < length; i++) { - final String id_string = String.valueOf(array[i]); - if (id_string != null) { - if (i > 0) { - builder.append(include_space ? token + " " : token); - } - builder.append(id_string); - } - } - return builder.toString(); - } + public static String toString(final Object[] array, final char token, final boolean includeSpace) { + final StringBuilder builder = new StringBuilder(); + final int length = array.length; + for (int i = 0; i < length; i++) { + final String string = objectToString(array[i]); + if (string != null) { + if (i > 0) { + builder.append(includeSpace ? token + " " : token); + } + builder.append(string); + } + } + return builder.toString(); + } - public static String toString(final SQLLang[] array) { - final StringBuilder builder = new StringBuilder(); - final int length = array.length; - for (int i = 0; i < length; i++) { - final String id_string = array[i].getSQL(); - if (id_string != null) { - if (i > 0) { - builder.append(", "); - } - builder.append(id_string); - } - } - return builder.toString(); - } + private static String objectToString(Object o) { + if (o instanceof SQLLang) + return ((SQLLang) o).getSQL(); + return o != null ? o.toString() : null; + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/Where.java b/twidere/src/main/java/org/mariotaku/querybuilder/Where.java deleted file mode 100644 index 6f395a0dd..000000000 --- a/twidere/src/main/java/org/mariotaku/querybuilder/Where.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * This is free and unencumbered software released into the public domain. - * - * Anyone is free to copy, modify, publish, use, compile, sell, or - * distribute this software, either in source code form or as a compiled - * binary, for any purpose, commercial or non-commercial, and by any - * means. - * - * In jurisdictions that recognize copyright laws, the author or authors - * of this software dedicate any and all copyright interest in the - * software to the public domain. We make this dedication for the benefit - * of the public at large and to the detriment of our heirs and - * successors. We intend this dedication to be an overt act of - * relinquishment in perpetuity of all present and future rights to this - * software under copyright law. - - * 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 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. - * - * For more information, please refer to - */ - -package org.mariotaku.querybuilder; - -import org.mariotaku.querybuilder.Columns.Column; - -import java.util.Locale; - -public class Where implements SQLLang { - private final String expr; - - public Where(final String expr) { - this.expr = expr; - } - - @Override - public String getSQL() { - return expr; - } - - public static Where and(final Where... expressions) { - return new Where(toExpr(expressions, "AND")); - } - - public static Where equals(final Column l, final Column r) { - return new Where(String.format(Locale.ROOT, "%s = %s", l.getSQL(), r.getSQL())); - } - - public static Where equals(final Column l, final long r) { - return new Where(String.format(Locale.ROOT, "%s = %d", l.getSQL(), r)); - } - - public static Where equals(final Column l, final String r) { - return new Where(String.format(Locale.ROOT, "%s = '%s'", l.getSQL(), r)); - } - - public static Where equals(final String l, final long r) { - return new Where(String.format(Locale.ROOT, "%s = %d", l, r)); - } - - public static Where in(final Column column, final Selectable in) { - return new Where(String.format("%s IN(%s)", column.getSQL(), in.getSQL())); - } - - public static Where notEquals(final String l, final long r) { - return new Where(String.format(Locale.ROOT, "%s != %d", l, r)); - } - - public static Where notEquals(final String l, final String r) { - return new Where(String.format("%s != %s", l, r)); - } - - public static Where notIn(final Column column, final Selectable in) { - return new Where(String.format("%s NOT IN(%s)", column.getSQL(), in.getSQL())); - } - - public static Where notNull(final Column column) { - return new Where(String.format("%s NOT NULL", column.getSQL())); - } - - public static Where or(final Where... expressions) { - return new Where(toExpr(expressions, "OR")); - } - - private static String toExpr(final Where[] array, final String token) { - final StringBuilder builder = new StringBuilder(); - builder.append('('); - final int length = array.length; - for (int i = 0; i < length; i++) { - if (i > 0) { - builder.append(String.format(" %s ", token)); - } - builder.append(array[i].getSQL()); - } - builder.append(')'); - return builder.toString(); - } -} diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/IBuilder.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/IBuilder.java index fa65c3ff8..7bc31e6a4 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/query/IBuilder.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/IBuilder.java @@ -4,13 +4,13 @@ import org.mariotaku.querybuilder.SQLLang; public interface IBuilder { - public T build(); + public T build(); - /** - * Equivalent to {@link #build()}.{@link #SQLLang.getSQL()} - * - * @return - */ - public String buildSQL(); + /** + * Equivalent to {@link #build()}.{@link SQLLang#getSQL()} + * + * @return + */ + public String buildSQL(); } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLAlterTableQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLAlterTableQuery.java index 789aa82f3..a8aaa93ce 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLAlterTableQuery.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLAlterTableQuery.java @@ -1,71 +1,70 @@ package org.mariotaku.querybuilder.query; import org.mariotaku.querybuilder.NewColumn; -import org.mariotaku.querybuilder.SQLLang; +import org.mariotaku.querybuilder.SQLQuery; -public class SQLAlterTableQuery implements SQLLang { +public class SQLAlterTableQuery implements SQLQuery { - private String table; - private String renameTo; - private NewColumn addColumn; + private String table; + private String renameTo; + private NewColumn addColumn; - @Override - public String getSQL() { - if (table == null) throw new NullPointerException("table must not be null!"); - if (renameTo == null && addColumn == null) throw new NullPointerException(); - if (renameTo != null) return String.format("ALTER TABLE %s RENAME TO %s", table, renameTo); - return String.format("ALTER TABLE %s ADD COLUMN %s", table, addColumn.getSQL()); - } + @Override + public String getSQL() { + if (table == null) throw new NullPointerException("table must not be null!"); + if (renameTo == null && addColumn == null) throw new NullPointerException(); + if (renameTo != null) return String.format("ALTER TABLE %s RENAME TO %s", table, renameTo); + return String.format("ALTER TABLE %s ADD COLUMN %s", table, addColumn.getSQL()); + } - void setAddColumn(final NewColumn addColumn) { - this.addColumn = addColumn; - } + void setAddColumn(final NewColumn addColumn) { + this.addColumn = addColumn; + } - void setRenameTo(final String renameTo) { - this.renameTo = renameTo; - } + void setRenameTo(final String renameTo) { + this.renameTo = renameTo; + } - void setTable(final String table) { - this.table = table; - } + void setTable(final String table) { + this.table = table; + } - public static final class Builder implements IBuilder { + public static final class Builder implements IBuilder { - private boolean buildCalled; + private final SQLAlterTableQuery query = new SQLAlterTableQuery(); + private boolean buildCalled; - private final SQLAlterTableQuery query = new SQLAlterTableQuery(); + public Builder addColumn(final NewColumn addColumn) { + checkNotBuilt(); + query.setAddColumn(addColumn); + return this; + } - public Builder addColumn(final NewColumn addColumn) { - checkNotBuilt(); - query.setAddColumn(addColumn); - return this; - } + public Builder alterTable(final String table) { + checkNotBuilt(); + query.setTable(table); + return this; + } - public Builder alterTable(final String table) { - checkNotBuilt(); - query.setTable(table); - return this; - } + @Override + public SQLAlterTableQuery build() { + return query; + } - @Override - public SQLAlterTableQuery build() { - return query; - } + @Override + public String buildSQL() { + return build().getSQL(); + } - @Override - public String buildSQL() { - return build().getSQL(); - } + public Builder renameTo(final String renameTo) { + checkNotBuilt(); + query.setRenameTo(renameTo); + return this; + } - public Builder renameTo(final String renameTo) { - checkNotBuilt(); - query.setRenameTo(renameTo); - return this; - } - - private void checkNotBuilt() { - if (buildCalled) throw new IllegalStateException(); - } - } + private void checkNotBuilt() { + if (buildCalled) throw new IllegalStateException(); + } + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTableQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTableQuery.java index ab6d8ce6f..f0610e380 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTableQuery.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTableQuery.java @@ -1,111 +1,111 @@ package org.mariotaku.querybuilder.query; import org.mariotaku.querybuilder.NewColumn; -import org.mariotaku.querybuilder.SQLLang; +import org.mariotaku.querybuilder.SQLQuery; import org.mariotaku.querybuilder.Utils; -public class SQLCreateTableQuery implements SQLLang { +public class SQLCreateTableQuery implements SQLQuery { - private boolean temporary; - private boolean createIfNotExists; - private String table; - private NewColumn[] newColumns; - private SQLSelectQuery selectStmt; + private boolean temporary; + private boolean createIfNotExists; + private String table; + private NewColumn[] newColumns; + private SQLSelectQuery selectStmt; - SQLCreateTableQuery() { - } + SQLCreateTableQuery() { + } - @Override - public String getSQL() { - if (table == null) throw new NullPointerException("NAME must not be null!"); - if ((newColumns == null || newColumns.length == 0) && selectStmt == null) - throw new NullPointerException("Columns or AS must not be null!"); - final StringBuilder sb = new StringBuilder("CREATE "); - if (temporary) { - sb.append("TEMPORARY "); - } - sb.append("TABLE "); - if (createIfNotExists) { - sb.append("IF NOT EXISTS "); - } - sb.append(String.format("%s ", table)); - if (newColumns != null && newColumns.length > 0) { - sb.append(String.format("(%s)", Utils.toString(newColumns))); - } else { - sb.append(String.format("AS %s", selectStmt.getSQL())); - } - return sb.toString(); - } + @Override + public String getSQL() { + if (table == null) throw new NullPointerException("NAME must not be null!"); + if ((newColumns == null || newColumns.length == 0) && selectStmt == null) + throw new NullPointerException("Columns or AS must not be null!"); + final StringBuilder sb = new StringBuilder("CREATE "); + if (temporary) { + sb.append("TEMPORARY "); + } + sb.append("TABLE "); + if (createIfNotExists) { + sb.append("IF NOT EXISTS "); + } + sb.append(String.format("%s ", table)); + if (newColumns != null && newColumns.length > 0) { + sb.append(String.format("(%s)", Utils.toString(newColumns, ',', true))); + } else { + sb.append(String.format("AS %s", selectStmt.getSQL())); + } + return sb.toString(); + } - void setAs(final SQLSelectQuery selectStmt) { - this.selectStmt = selectStmt; - } + void setAs(final SQLSelectQuery selectStmt) { + this.selectStmt = selectStmt; + } - void setCreateIfNotExists(final boolean createIfNotExists) { - this.createIfNotExists = createIfNotExists; - } + void setCreateIfNotExists(final boolean createIfNotExists) { + this.createIfNotExists = createIfNotExists; + } - void setNewColumns(final NewColumn[] newColumns) { - this.newColumns = newColumns; - } + void setNewColumns(final NewColumn[] newColumns) { + this.newColumns = newColumns; + } - void setTable(final String table) { - this.table = table; - } + void setTable(final String table) { + this.table = table; + } - void setTemporary(final boolean temporary) { - this.temporary = temporary; - } + void setTemporary(final boolean temporary) { + this.temporary = temporary; + } - public static final class Builder implements IBuilder { + public static final class Builder implements IBuilder { - private final SQLCreateTableQuery query = new SQLCreateTableQuery(); + private final SQLCreateTableQuery query = new SQLCreateTableQuery(); - private boolean buildCalled; + private boolean buildCalled; - public Builder as(final SQLSelectQuery selectStmt) { - checkNotBuilt(); - query.setAs(selectStmt); - return this; - } + public Builder as(final SQLSelectQuery selectStmt) { + checkNotBuilt(); + query.setAs(selectStmt); + return this; + } - @Override - public SQLCreateTableQuery build() { - buildCalled = true; - return query; - } + @Override + public SQLCreateTableQuery build() { + buildCalled = true; + return query; + } - @Override - public String buildSQL() { - return build().getSQL(); - } + @Override + public String buildSQL() { + return build().getSQL(); + } - public Builder columns(final NewColumn... newColumns) { - checkNotBuilt(); - query.setNewColumns(newColumns); - return this; - } + public Builder columns(final NewColumn... newColumns) { + checkNotBuilt(); + query.setNewColumns(newColumns); + return this; + } - public Builder createTable(final boolean temporary, final boolean createIfNotExists, final String table) { - checkNotBuilt(); - query.setTemporary(temporary); - query.setCreateIfNotExists(createIfNotExists); - query.setTable(table); - return this; - } + public Builder createTable(final boolean temporary, final boolean createIfNotExists, final String table) { + checkNotBuilt(); + query.setTemporary(temporary); + query.setCreateIfNotExists(createIfNotExists); + query.setTable(table); + return this; + } - public Builder createTable(final boolean createIfNotExists, final String table) { - return createTable(false, createIfNotExists, table); - } + public Builder createTable(final boolean createIfNotExists, final String table) { + return createTable(false, createIfNotExists, table); + } - public Builder createTemporaryTable(final boolean createIfNotExists, final String table) { - return createTable(true, createIfNotExists, table); - } + public Builder createTemporaryTable(final boolean createIfNotExists, final String table) { + return createTable(true, createIfNotExists, table); + } - private void checkNotBuilt() { - if (buildCalled) throw new IllegalStateException(); - } + private void checkNotBuilt() { + if (buildCalled) throw new IllegalStateException(); + } - } + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTriggerQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTriggerQuery.java new file mode 100644 index 000000000..00aa9c8e6 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTriggerQuery.java @@ -0,0 +1,201 @@ +package org.mariotaku.querybuilder.query; + +import org.mariotaku.querybuilder.Columns; +import org.mariotaku.querybuilder.Expression; +import org.mariotaku.querybuilder.SQLLang; +import org.mariotaku.querybuilder.SQLQuery; +import org.mariotaku.querybuilder.Table; +import org.mariotaku.querybuilder.Utils; + +import java.util.Locale; + +/** + * Created by mariotaku on 14-8-6. + */ +public class SQLCreateTriggerQuery implements SQLQuery { + + private boolean temporary; + private boolean createIfNotExists; + private boolean forEachRow; + private String name; + private Table on; + private Type type; + private Event event; + private Columns updateOf; + private SQLQuery[] actions; + private Expression when; + + void setActions(SQLQuery[] actions) { + this.actions = actions; + } + + void setForEachRow(boolean forEachRow) { + this.forEachRow = forEachRow; + } + + void setOn(Table on) { + this.on = on; + } + + void setUpdateOf(Columns updateOf) { + this.updateOf = updateOf; + } + + void setType(Type type) { + this.type = type; + } + + void setEvent(Event event) { + this.event = event; + } + + void setWhen(Expression when) { + this.when = when; + } + + @Override + public String getSQL() { + if (name == null) throw new NullPointerException("NAME must not be null!"); + if (event == null) throw new NullPointerException("EVENT must not be null!"); + if (on == null) throw new NullPointerException("ON must not be null!"); + if (actions == null) throw new NullPointerException("ACTIONS must not be null!"); + final StringBuilder sb = new StringBuilder("CREATE "); + if (temporary) { + sb.append("TEMPORARY "); + } + sb.append("TRIGGER "); + if (createIfNotExists) { + sb.append("IF NOT EXISTS "); + } + sb.append(name); + sb.append(' '); + if (type != null) { + sb.append(type.getSQL()); + sb.append(' '); + } + sb.append(event.getSQL()); + sb.append(' '); + if (event == Event.UPDATE) { + sb.append(String.format(Locale.ROOT, "%s ", updateOf.getSQL())); + } + sb.append(String.format(Locale.ROOT, "ON %s ", on.getSQL())); + if (forEachRow) { + sb.append("FOR EACH ROW "); + } + if (when != null) { + sb.append(String.format(Locale.ROOT, "WHEN %s ", when.getSQL())); + } + sb.append(String.format(Locale.ROOT, "BEGIN %s; END", Utils.toString(actions, ';', true))); + return sb.toString(); + } + + void setCreateIfNotExists(final boolean createIfNotExists) { + this.createIfNotExists = createIfNotExists; + } + + void setName(final String name) { + this.name = name; + } + + void setTemporary(final boolean temporary) { + this.temporary = temporary; + } + + public static enum Type implements SQLLang { + BEFORE("BEFORE"), AFTER("AFTER"), INSTEAD_OF("INSTEAD OF"); + private final String lang; + + Type(String lang) { + this.lang = lang; + } + + @Override + public String getSQL() { + return lang; + } + } + + public static enum Event implements SQLLang { + INSERT("INSERT"), DELETE("DELETE"), UPDATE("UPDATE"); + private final String lang; + + Event(String lang) { + this.lang = lang; + } + + @Override + public String getSQL() { + return lang; + } + } + + public static class Builder implements IBuilder { + + private final SQLCreateTriggerQuery query = new SQLCreateTriggerQuery(); + private boolean buildCalled; + + public Builder forEachRow(final boolean forEachRow) { + checkNotBuilt(); + query.setForEachRow(forEachRow); + return this; + } + + public Builder on(final Table on) { + checkNotBuilt(); + query.setOn(on); + return this; + } + + public Builder event(Event event) { + checkNotBuilt(); + query.setEvent(event); + return this; + } + + public Builder type(Type type) { + checkNotBuilt(); + query.setType(type); + return this; + } + + public Builder updateOf(Columns updateOf) { + checkNotBuilt(); + query.setUpdateOf(updateOf); + return this; + } + + public Builder actions(SQLQuery... actions) { + checkNotBuilt(); + query.setActions(actions); + return this; + } + + public Builder when(Expression when) { + checkNotBuilt(); + query.setWhen(when); + return this; + } + + @Override + public SQLCreateTriggerQuery build() { + return query; + } + + @Override + public String buildSQL() { + return build().getSQL(); + } + + private void checkNotBuilt() { + if (buildCalled) throw new IllegalStateException(); + } + + public Builder createTrigger(boolean temporary, boolean createIfNotExists, String name) { + checkNotBuilt(); + query.setTemporary(temporary); + query.setCreateIfNotExists(createIfNotExists); + query.setName(name); + return this; + } + } +} diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateViewQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateViewQuery.java index b15285a1e..031dc5047 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateViewQuery.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLCreateViewQuery.java @@ -1,90 +1,90 @@ package org.mariotaku.querybuilder.query; -import org.mariotaku.querybuilder.SQLLang; +import org.mariotaku.querybuilder.SQLQuery; -public class SQLCreateViewQuery implements SQLLang { +public class SQLCreateViewQuery implements SQLQuery { - private boolean temporary; - private boolean createIfNotExists; - private String name; - private SQLSelectQuery selectStmt; + private boolean temporary; + private boolean createIfNotExists; + private String name; + private SQLSelectQuery selectStmt; - SQLCreateViewQuery() { + SQLCreateViewQuery() { - } + } - @Override - public String getSQL() { - if (name == null) throw new NullPointerException("NAME must not be null!"); - if (selectStmt == null) throw new NullPointerException("SELECT statement must not be null!"); - final StringBuilder sb = new StringBuilder("CREATE "); - if (temporary) { - sb.append("TEMPORARY "); - } - sb.append("VIEW "); - if (createIfNotExists) { - sb.append("IF NOT EXISTS "); - } - sb.append(String.format("%s AS %s", name, selectStmt.getSQL())); - return sb.toString(); - } + @Override + public String getSQL() { + if (name == null) throw new NullPointerException("NAME must not be null!"); + if (selectStmt == null) + throw new NullPointerException("SELECT statement must not be null!"); + final StringBuilder sb = new StringBuilder("CREATE "); + if (temporary) { + sb.append("TEMPORARY "); + } + sb.append("VIEW "); + if (createIfNotExists) { + sb.append("IF NOT EXISTS "); + } + sb.append(String.format("%s AS %s", name, selectStmt.getSQL())); + return sb.toString(); + } - void setAs(final SQLSelectQuery selectStmt) { - this.selectStmt = selectStmt; - } + void setAs(final SQLSelectQuery selectStmt) { + this.selectStmt = selectStmt; + } - void setCreateIfNotExists(final boolean createIfNotExists) { - this.createIfNotExists = createIfNotExists; - } + void setCreateIfNotExists(final boolean createIfNotExists) { + this.createIfNotExists = createIfNotExists; + } - void setName(final String name) { - this.name = name; - } + void setName(final String name) { + this.name = name; + } - void setTemporary(final boolean temporary) { - this.temporary = temporary; - } + void setTemporary(final boolean temporary) { + this.temporary = temporary; + } - public static final class Builder implements IBuilder { + public static final class Builder implements IBuilder { - private boolean buildCalled; + private final SQLCreateViewQuery query = new SQLCreateViewQuery(); + private boolean buildCalled; - private final SQLCreateViewQuery query = new SQLCreateViewQuery(); + public Builder as(final SQLSelectQuery selectStmt) { + checkNotBuilt(); + query.setAs(selectStmt); + return this; + } - public Builder as(final SQLSelectQuery selectStmt) { - checkNotBuilt(); - query.setAs(selectStmt); - return this; - } + @Override + public SQLCreateViewQuery build() { + return query; + } - @Override - public SQLCreateViewQuery build() { - return query; - } + @Override + public String buildSQL() { + return build().getSQL(); + } - @Override - public String buildSQL() { - return build().getSQL(); - } + public Builder createTemporaryView(final boolean createIfNotExists, final String name) { + return createView(true, createIfNotExists, name); + } - public Builder createTemporaryView(final boolean createIfNotExists, final String name) { - return createView(true, createIfNotExists, name); - } + public Builder createView(final boolean temporary, final boolean createIfNotExists, final String name) { + checkNotBuilt(); + query.setTemporary(temporary); + query.setCreateIfNotExists(createIfNotExists); + query.setName(name); + return this; + } - public Builder createView(final boolean temporary, final boolean createIfNotExists, final String name) { - checkNotBuilt(); - query.setTemporary(temporary); - query.setCreateIfNotExists(createIfNotExists); - query.setName(name); - return this; - } + public Builder createView(final boolean createIfNotExists, final String name) { + return createView(false, createIfNotExists, name); + } - public Builder createView(final boolean createIfNotExists, final String name) { - return createView(false, createIfNotExists, name); - } - - private void checkNotBuilt() { - if (buildCalled) throw new IllegalStateException(); - } - } + private void checkNotBuilt() { + if (buildCalled) throw new IllegalStateException(); + } + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDeleteQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDeleteQuery.java index cf4862dd5..e187f04fa 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDeleteQuery.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDeleteQuery.java @@ -1,57 +1,57 @@ package org.mariotaku.querybuilder.query; -import org.mariotaku.querybuilder.SQLLang; -import org.mariotaku.querybuilder.Where; +import org.mariotaku.querybuilder.Expression; +import org.mariotaku.querybuilder.SQLQuery; -public class SQLDeleteQuery implements SQLLang { +public class SQLDeleteQuery implements SQLQuery { - private String table; - private Where where; + private String table; + private Expression where; - @Override - public String getSQL() { - if (where != null) return String.format("DELETE FROM %s", table); - return String.format("DELETE FROM %S WHERE %s", table, where.getSQL()); - } + @Override + public String getSQL() { + if (where != null) return String.format("DELETE FROM %s", table); + return String.format("DELETE FROM %S WHERE %s", table, where.getSQL()); + } - void setFrom(final String table) { - this.table = table; - } + void setFrom(final String table) { + this.table = table; + } - void setWhere(final Where where) { - this.where = where; - } + void setWhere(final Expression where) { + this.where = where; + } - public static final class Builder implements IBuilder { - private boolean buildCalled; - private final SQLDeleteQuery query = new SQLDeleteQuery(); + public static final class Builder implements IBuilder { + private final SQLDeleteQuery query = new SQLDeleteQuery(); + private boolean buildCalled; - @Override - public SQLDeleteQuery build() { - buildCalled = true; - return query; - } + @Override + public SQLDeleteQuery build() { + buildCalled = true; + return query; + } - @Override - public String buildSQL() { - return build().getSQL(); - } + @Override + public String buildSQL() { + return build().getSQL(); + } - public Builder from(final String table) { - checkNotBuilt(); - query.setFrom(table); - return this; - } + public Builder from(final String table) { + checkNotBuilt(); + query.setFrom(table); + return this; + } - public Builder where(final Where where) { - checkNotBuilt(); - query.setWhere(where); - return this; - } + public Builder where(final Expression where) { + checkNotBuilt(); + query.setWhere(where); + return this; + } - private void checkNotBuilt() { - if (buildCalled) throw new IllegalStateException(); - } - } + private void checkNotBuilt() { + if (buildCalled) throw new IllegalStateException(); + } + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropQuery.java index 6967baa49..021b0c5b3 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropQuery.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropQuery.java @@ -1,19 +1,24 @@ package org.mariotaku.querybuilder.query; -import org.mariotaku.querybuilder.SQLLang; +import org.mariotaku.querybuilder.SQLQuery; -public class SQLDropQuery implements SQLLang { +public class SQLDropQuery implements SQLQuery { - private final String table; + private final boolean dropIfExists; + private final String type; + private final String target; - public SQLDropQuery(final String table) { - if (table == null) throw new NullPointerException(); - this.table = table; - } + public SQLDropQuery(final boolean dropIfExists, final String type, final String target) { + if (target == null) throw new NullPointerException(); + this.dropIfExists = dropIfExists; + this.type = type; + this.target = target; + } - @Override - public String getSQL() { - return String.format("DROP TABLE %s", table); - } + @Override + public final String getSQL() { + if (dropIfExists) return String.format("DROP %s IF EXISTS %s", type, target); + return String.format("DROP %s %s", type, target); + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropTableQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropTableQuery.java index 9053abafd..bd8486acd 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropTableQuery.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropTableQuery.java @@ -1,22 +1,9 @@ package org.mariotaku.querybuilder.query; -import org.mariotaku.querybuilder.SQLLang; +public class SQLDropTableQuery extends SQLDropQuery { -public class SQLDropTableQuery implements SQLLang { - - private final boolean dropIfExists; - private final String table; - - public SQLDropTableQuery(final boolean dropIfExists, final String table) { - if (table == null) throw new NullPointerException(); - this.dropIfExists = dropIfExists; - this.table = table; - } - - @Override - public String getSQL() { - if (dropIfExists) return String.format("DROP TABLE IF EXISTS %s", table); - return String.format("DROP TABLE %s", table); - } + public SQLDropTableQuery(final boolean dropIfExists, final String table) { + super(dropIfExists, "TABLE", table); + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropTriggerQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropTriggerQuery.java new file mode 100644 index 000000000..b51e4f001 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropTriggerQuery.java @@ -0,0 +1,9 @@ +package org.mariotaku.querybuilder.query; + +public class SQLDropTriggerQuery extends SQLDropQuery { + + public SQLDropTriggerQuery(final boolean dropIfExists, final String table) { + super(dropIfExists, "TRIGGER", table); + } + +} diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropViewQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropViewQuery.java index d6c460932..42c167234 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropViewQuery.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLDropViewQuery.java @@ -1,22 +1,9 @@ package org.mariotaku.querybuilder.query; -import org.mariotaku.querybuilder.SQLLang; +public class SQLDropViewQuery extends SQLDropQuery { -public class SQLDropViewQuery implements SQLLang { - - private final boolean dropIfExists; - private final String table; - - public SQLDropViewQuery(final boolean dropIfExists, final String table) { - if (table == null) throw new NullPointerException(); - this.dropIfExists = dropIfExists; - this.table = table; - } - - @Override - public String getSQL() { - if (dropIfExists) return String.format("DROP VIEW IF EXISTS %s", table); - return String.format("DROP VIEW %s", table); - } + public SQLDropViewQuery(final boolean dropIfExists, final String table) { + super(dropIfExists, "VIEW", table); + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLInsertIntoQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLInsertIntoQuery.java deleted file mode 100644 index a83faacd5..000000000 --- a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLInsertIntoQuery.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.mariotaku.querybuilder.query; - -import org.mariotaku.querybuilder.SQLLang; -import org.mariotaku.querybuilder.Utils; - -public class SQLInsertIntoQuery implements SQLLang { - - private OnConflict onConflict; - private String table; - private String[] columns; - private SQLSelectQuery select; - - SQLInsertIntoQuery() { - - } - - @Override - public String getSQL() { - if (table == null) throw new NullPointerException("table must not be null!"); - final StringBuilder sb = new StringBuilder(); - sb.append("INSERT "); - if (onConflict != null) { - sb.append(String.format("OR %s ", onConflict.getAction())); - } - sb.append(String.format("INTO %s ", table)); - sb.append(String.format("(%s) ", Utils.toString(columns, ',', false))); - sb.append(String.format("%s ", select.getSQL())); - return sb.toString(); - } - - void setColumns(final String[] columns) { - this.columns = columns; - } - - void setOnConflict(final OnConflict onConflict) { - this.onConflict = onConflict; - } - - void setSelect(final SQLSelectQuery select) { - this.select = select; - } - - void setTable(final String table) { - this.table = table; - } - - public static final class Builder implements IBuilder { - - private final SQLInsertIntoQuery query = new SQLInsertIntoQuery(); - - private boolean buildCalled; - - @Override - public SQLInsertIntoQuery build() { - buildCalled = true; - return query; - } - - @Override - public String buildSQL() { - return build().getSQL(); - } - - public Builder columns(final String[] columns) { - checkNotBuilt(); - query.setColumns(columns); - return this; - } - - public Builder insertInto(final OnConflict onConflict, final String table) { - checkNotBuilt(); - query.setOnConflict(onConflict); - query.setTable(table); - return this; - } - - public Builder insertInto(final String table) { - return insertInto(null, table); - } - - public Builder select(final SQLSelectQuery select) { - checkNotBuilt(); - query.setSelect(select); - return this; - } - - private void checkNotBuilt() { - if (buildCalled) throw new IllegalStateException(); - } - - } - - public static enum OnConflict { - ROLLBACK("ROLLBACK"), ABORT("ABORT"), REPLACE("REPLACE"), FAIL("FAIL"), IGNORE("IGNORE"); - private final String action; - - private OnConflict(final String action) { - this.action = action; - } - - public String getAction() { - return action; - } - } - -} diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLInsertQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLInsertQuery.java new file mode 100644 index 000000000..86510acee --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLInsertQuery.java @@ -0,0 +1,94 @@ +package org.mariotaku.querybuilder.query; + +import org.mariotaku.querybuilder.OnConflict; +import org.mariotaku.querybuilder.SQLQuery; +import org.mariotaku.querybuilder.Utils; + +public class SQLInsertQuery implements SQLQuery { + + private OnConflict onConflict; + private String table; + private String[] columns; + private SQLSelectQuery select; + + SQLInsertQuery() { + + } + + @Override + public String getSQL() { + if (table == null) throw new NullPointerException("table must not be null!"); + final StringBuilder sb = new StringBuilder(); + sb.append("INSERT "); + if (onConflict != null) { + sb.append(String.format("OR %s ", onConflict.getAction())); + } + sb.append(String.format("INTO %s ", table)); + sb.append(String.format("(%s) ", Utils.toString(columns, ',', false))); + sb.append(String.format("%s ", select.getSQL())); + return sb.toString(); + } + + void setColumns(final String[] columns) { + this.columns = columns; + } + + void setOnConflict(final OnConflict onConflict) { + this.onConflict = onConflict; + } + + void setSelect(final SQLSelectQuery select) { + this.select = select; + } + + void setTable(final String table) { + this.table = table; + } + + public static final class Builder implements IBuilder { + + private final SQLInsertQuery query = new SQLInsertQuery(); + + private boolean buildCalled; + + @Override + public SQLInsertQuery build() { + buildCalled = true; + return query; + } + + @Override + public String buildSQL() { + return build().getSQL(); + } + + public Builder columns(final String[] columns) { + checkNotBuilt(); + query.setColumns(columns); + return this; + } + + public Builder insertInto(final OnConflict onConflict, final String table) { + checkNotBuilt(); + query.setOnConflict(onConflict); + query.setTable(table); + return this; + } + + public Builder insertInto(final String table) { + return insertInto(null, table); + } + + public Builder select(final SQLSelectQuery select) { + checkNotBuilt(); + query.setSelect(select); + return this; + } + + private void checkNotBuilt() { + if (buildCalled) throw new IllegalStateException(); + } + + } + +} diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLSelectQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLSelectQuery.java index 2bc26ee92..438355b77 100644 --- a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLSelectQuery.java +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLSelectQuery.java @@ -1,11 +1,11 @@ /** * This is free and unencumbered software released into the public domain. - * + * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. - * + * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit @@ -21,240 +21,241 @@ * 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. - * + * * For more information, please refer to */ package org.mariotaku.querybuilder.query; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.OrderBy; import org.mariotaku.querybuilder.SQLLang; +import org.mariotaku.querybuilder.SQLQuery; import org.mariotaku.querybuilder.SQLQueryException; import org.mariotaku.querybuilder.Selectable; -import org.mariotaku.querybuilder.Where; import java.util.ArrayList; import java.util.List; -public class SQLSelectQuery implements Selectable { +public class SQLSelectQuery implements SQLQuery, Selectable { - private final List internalQueries = new ArrayList(); + private final List internalQueries = new ArrayList(); - private InternalQuery currentInternalQuery; - private OrderBy orderBy; - private Integer limit = null, offset = null; + private InternalQuery currentInternalQuery; + private OrderBy orderBy; + private Integer limit = null, offset = null; - SQLSelectQuery() { - initCurrentQuery(); - } + SQLSelectQuery() { + initCurrentQuery(); + } - @Override - public String getSQL() { - final StringBuilder sb = new StringBuilder(); - final int size = internalQueries.size(); - for (int i = 0; i < size; i++) { - if (i != 0) { - sb.append("UNION "); - } - final InternalQuery query = internalQueries.get(i); - sb.append(query.getSQL()); + @Override + public String getSQL() { + final StringBuilder sb = new StringBuilder(); + final int size = internalQueries.size(); + for (int i = 0; i < size; i++) { + if (i != 0) { + sb.append("UNION "); + } + final InternalQuery query = internalQueries.get(i); + sb.append(query.getSQL()); - } - if (orderBy != null) { - sb.append(String.format("ORDER BY %s ", orderBy.getSQL())); - } - if (limit != null) { - sb.append(String.format("LIMIT %s ", limit)); - if (offset != null) { - sb.append(String.format("OFFSET %s ", offset)); - } - } - return sb.toString(); - } + } + if (orderBy != null) { + sb.append(String.format("ORDER BY %s ", orderBy.getSQL())); + } + if (limit != null) { + sb.append(String.format("LIMIT %s ", limit)); + if (offset != null) { + sb.append(String.format("OFFSET %s ", offset)); + } + } + return sb.toString(); + } - private void initCurrentQuery() { - currentInternalQuery = new InternalQuery(); - internalQueries.add(currentInternalQuery); - } + private void initCurrentQuery() { + currentInternalQuery = new InternalQuery(); + internalQueries.add(currentInternalQuery); + } - void setDistinct(final boolean distinct) { - currentInternalQuery.setDistinct(distinct); - } + void setDistinct(final boolean distinct) { + currentInternalQuery.setDistinct(distinct); + } - void setFrom(final Selectable from) { - currentInternalQuery.setFrom(from); - } + void setFrom(final Selectable from) { + currentInternalQuery.setFrom(from); + } - void setGroupBy(final Selectable groupBy) { - currentInternalQuery.setGroupBy(groupBy); - } + void setGroupBy(final Selectable groupBy) { + currentInternalQuery.setGroupBy(groupBy); + } - void setHaving(final Where having) { - currentInternalQuery.setHaving(having); - } + void setHaving(final Expression having) { + currentInternalQuery.setHaving(having); + } - void setLimit(final int limit) { - this.limit = limit; - } + void setLimit(final int limit) { + this.limit = limit; + } - void setOffset(final int offset) { - this.offset = offset; - } + void setOffset(final int offset) { + this.offset = offset; + } - void setOrderBy(final OrderBy orderBy) { - this.orderBy = orderBy; - } + void setOrderBy(final OrderBy orderBy) { + this.orderBy = orderBy; + } - void setSelect(final Selectable select) { - currentInternalQuery.setSelect(select); - } + void setSelect(final Selectable select) { + currentInternalQuery.setSelect(select); + } - void setWhere(final Where where) { - currentInternalQuery.setWhere(where); - } + void setWhere(final Expression where) { + currentInternalQuery.setWhere(where); + } - void union() { - initCurrentQuery(); - } + void union() { + initCurrentQuery(); + } - public static final class Builder implements IBuilder { - private boolean buildCalled; - private final SQLSelectQuery query = new SQLSelectQuery(); + public static final class Builder implements IBuilder { + private final SQLSelectQuery query = new SQLSelectQuery(); + private boolean buildCalled; - @Override - public SQLSelectQuery build() { - buildCalled = true; - return query; - } + @Override + public SQLSelectQuery build() { + buildCalled = true; + return query; + } - @Override - public String buildSQL() { - return build().getSQL(); - } + @Override + public String buildSQL() { + return build().getSQL(); + } - public Builder from(final Selectable from) { - checkNotBuilt(); - query.setFrom(from); - return this; - } + public Builder from(final Selectable from) { + checkNotBuilt(); + query.setFrom(from); + return this; + } - public Builder groupBy(final Selectable groupBy) { - checkNotBuilt(); - query.setGroupBy(groupBy); - return this; - } + public Builder groupBy(final Selectable groupBy) { + checkNotBuilt(); + query.setGroupBy(groupBy); + return this; + } - public Builder having(final Where having) { - checkNotBuilt(); - query.setHaving(having); - return this; - } + public Builder having(final Expression having) { + checkNotBuilt(); + query.setHaving(having); + return this; + } - public Builder limit(final int limit) { - checkNotBuilt(); - query.setLimit(limit); - return this; - } + public Builder limit(final int limit) { + checkNotBuilt(); + query.setLimit(limit); + return this; + } - public Builder offset(final int offset) { - query.setOffset(offset); - return this; - } + public Builder offset(final int offset) { + query.setOffset(offset); + return this; + } - public Builder orderBy(final OrderBy orderBy) { - checkNotBuilt(); - query.setOrderBy(orderBy); - return this; - } + public Builder orderBy(final OrderBy orderBy) { + checkNotBuilt(); + query.setOrderBy(orderBy); + return this; + } - public Builder select(final boolean distinct, final Selectable select) { - checkNotBuilt(); - query.setSelect(select); - query.setDistinct(distinct); - return this; - } + public Builder select(final boolean distinct, final Selectable select) { + checkNotBuilt(); + query.setSelect(select); + query.setDistinct(distinct); + return this; + } - public Builder select(final Selectable select) { - checkNotBuilt(); - select(false, select); - return this; - } + public Builder select(final Selectable select) { + checkNotBuilt(); + select(false, select); + return this; + } - public Builder union() { - checkNotBuilt(); - query.union(); - return this; - } + public Builder union() { + checkNotBuilt(); + query.union(); + return this; + } - public Builder where(final Where where) { - checkNotBuilt(); - query.setWhere(where); - return this; - } + public Builder where(final Expression where) { + checkNotBuilt(); + query.setWhere(where); + return this; + } - private void checkNotBuilt() { - if (buildCalled) throw new IllegalStateException(); - } + private void checkNotBuilt() { + if (buildCalled) throw new IllegalStateException(); + } - } + } - private static class InternalQuery implements SQLLang { + private static class InternalQuery implements SQLLang { - private boolean distinct; - private Selectable select, from, groupBy; - private Where where, having; + private boolean distinct; + private Selectable select, from, groupBy; + private Expression where, having; - @Override - public String getSQL() { - if (select == null) throw new SQLQueryException("selectable is null"); - final StringBuilder sb = new StringBuilder("SELECT "); - if (distinct) { - sb.append("DISTINCT "); - } - sb.append(String.format("%s ", select.getSQL())); - if (!(select instanceof SQLSelectQuery) && from == null) - throw new SQLQueryException("FROM not specified"); - else if (from != null) { - if (from instanceof SQLSelectQuery) { - sb.append(String.format("FROM (%s) ", from.getSQL())); - } else { - sb.append(String.format("FROM %s ", from.getSQL())); - } - } - if (where != null) { - sb.append(String.format("WHERE %s ", where.getSQL())); - } - if (groupBy != null) { - sb.append(String.format("GROUP BY %s ", groupBy.getSQL())); - if (having != null) { - sb.append(String.format("HAVING %s ", having.getSQL())); - } - } - return sb.toString(); - } + @Override + public String getSQL() { + if (select == null) throw new SQLQueryException("selectable is null"); + final StringBuilder sb = new StringBuilder("SELECT "); + if (distinct) { + sb.append("DISTINCT "); + } + sb.append(String.format("%s ", select.getSQL())); + if (!(select instanceof SQLSelectQuery) && from == null) + throw new SQLQueryException("FROM not specified"); + else if (from != null) { + if (from instanceof SQLSelectQuery) { + sb.append(String.format("FROM (%s) ", from.getSQL())); + } else { + sb.append(String.format("FROM %s ", from.getSQL())); + } + } + if (where != null) { + sb.append(String.format("WHERE %s ", where.getSQL())); + } + if (groupBy != null) { + sb.append(String.format("GROUP BY %s ", groupBy.getSQL())); + if (having != null) { + sb.append(String.format("HAVING %s ", having.getSQL())); + } + } + return sb.toString(); + } - void setDistinct(final boolean distinct) { - this.distinct = distinct; - } + void setDistinct(final boolean distinct) { + this.distinct = distinct; + } - void setFrom(final Selectable from) { - this.from = from; - } + void setFrom(final Selectable from) { + this.from = from; + } - void setGroupBy(final Selectable groupBy) { - this.groupBy = groupBy; - } + void setGroupBy(final Selectable groupBy) { + this.groupBy = groupBy; + } - void setHaving(final Where having) { - this.having = having; - } + void setHaving(final Expression having) { + this.having = having; + } - void setSelect(final Selectable select) { - this.select = select; - } + void setSelect(final Selectable select) { + this.select = select; + } - void setWhere(final Where where) { - this.where = where; - } - } + void setWhere(final Expression where) { + this.where = where; + } + } } diff --git a/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLUpdateQuery.java b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLUpdateQuery.java new file mode 100644 index 000000000..f93177ebe --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/querybuilder/query/SQLUpdateQuery.java @@ -0,0 +1,100 @@ +package org.mariotaku.querybuilder.query; + +import org.mariotaku.querybuilder.Expression; +import org.mariotaku.querybuilder.OnConflict; +import org.mariotaku.querybuilder.SQLQuery; +import org.mariotaku.querybuilder.SetValue; +import org.mariotaku.querybuilder.Utils; + +import java.util.Locale; + +public class SQLUpdateQuery implements SQLQuery { + + private OnConflict onConflict; + private String table; + private SetValue[] values; + private Expression where; + + SQLUpdateQuery() { + + } + + @Override + public String getSQL() { + if (table == null) throw new NullPointerException("table must not be null!"); + final StringBuilder sb = new StringBuilder(); + sb.append("UPDATE "); + if (onConflict != null) { + sb.append(String.format(Locale.ROOT, "OR %s ", onConflict.getAction())); + } + sb.append(String.format(Locale.ROOT, "%s ", table)); + sb.append(String.format(Locale.ROOT, "SET %s ", Utils.toString(values, ',', false))); + if (where != null) { + sb.append(String.format(Locale.ROOT, "WHERE %s ", where.getSQL())); + } + return sb.toString(); + } + + void setWhere(final Expression where) { + this.where = where; + } + + void setValues(final SetValue[] columns) { + this.values = columns; + } + + void setOnConflict(final OnConflict onConflict) { + this.onConflict = onConflict; + } + + void setTable(final String table) { + this.table = table; + } + + public static final class Builder implements IBuilder { + + private final SQLUpdateQuery query = new SQLUpdateQuery(); + + private boolean buildCalled; + + @Override + public SQLUpdateQuery build() { + buildCalled = true; + return query; + } + + @Override + public String buildSQL() { + return build().getSQL(); + } + + public Builder set(final SetValue... values) { + checkNotBuilt(); + query.setValues(values); + return this; + } + + public Builder where(final Expression where) { + checkNotBuilt(); + query.setWhere(where); + return this; + } + + public Builder update(final OnConflict onConflict, final String table) { + checkNotBuilt(); + query.setOnConflict(onConflict); + query.setTable(table); + return this; + } + + public Builder update(final String table) { + return update(null, table); + } + + private void checkNotBuilt() { + if (buildCalled) throw new IllegalStateException(); + } + + } + +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/TwidereConstants.java b/twidere/src/main/java/org/mariotaku/twidere/TwidereConstants.java index 604fd9845..b33c4b71b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/TwidereConstants.java +++ b/twidere/src/main/java/org/mariotaku/twidere/TwidereConstants.java @@ -175,7 +175,7 @@ public interface TwidereConstants extends SharedPreferenceConstants, IntentConst public static final int VIRTUAL_TABLE_ID_UNREAD_COUNTS_BY_TYPE = 109; public static final int NOTIFICATION_ID_HOME_TIMELINE = 1; - public static final int NOTIFICATION_ID_MENTIONS = 2; + public static final int NOTIFICATION_ID_MENTIONS_TIMELINE = 2; public static final int NOTIFICATION_ID_DIRECT_MESSAGES = 3; public static final int NOTIFICATION_ID_DRAFTS = 4; public static final int NOTIFICATION_ID_DATA_PROFILING = 5; diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/FiltersActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/FiltersActivity.java index 2397e4055..358718a1c 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/FiltersActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/FiltersActivity.java @@ -33,6 +33,7 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.SharedPreferences; +import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -45,7 +46,7 @@ import android.view.MenuItem; import android.view.View; import android.widget.AutoCompleteTextView; -import org.mariotaku.querybuilder.Where; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.support.BaseSupportActivity; import org.mariotaku.twidere.activity.support.UserListSelectorActivity; @@ -83,25 +84,19 @@ public class FiltersActivity extends BaseSupportActivity implements TabListener, } @Override - public void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE); - setContentView(R.layout.activity_filters); - mActionBar = getActionBar(); - mAdapter = new SupportTabsAdapter(this, getSupportFragmentManager(), null); - mActionBar.setDisplayHomeAsUpEnabled(true); - mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - addTab(FilteredUsersFragment.class, R.string.users, 0); - addTab(FilteredKeywordsFragment.class, R.string.keywords, 1); - addTab(FilteredSourcesFragment.class, R.string.sources, 2); - addTab(FilteredLinksFragment.class, R.string.links, 3); - mViewPager.setAdapter(mAdapter); - mViewPager.setOnPageChangeListener(this); + public boolean onCreateOptionsMenu(final Menu menu) { + getMenuInflater().inflate(R.menu.menu_filters, menu); + return true; } @Override - public boolean onCreateOptionsMenu(final Menu menu) { - getMenuInflater().inflate(R.menu.menu_filters, menu); + public boolean onPrepareOptionsMenu(final Menu menu) { + final boolean enable_in_home_timeline = mPreferences.getBoolean(KEY_FILTERS_IN_HOME_TIMELINE, true); + final boolean enable_in_mentions = mPreferences.getBoolean(KEY_FILTERS_IN_MENTIONS_TIMELINE, true); + final boolean enable_for_rts = mPreferences.getBoolean(KEY_FILTERS_FOR_RTS, true); + menu.findItem(R.id.enable_in_home_timeline).setChecked(enable_in_home_timeline); + menu.findItem(R.id.enable_in_mentions).setChecked(enable_in_mentions); + menu.findItem(R.id.enable_for_rts).setChecked(enable_for_rts); return true; } @@ -140,7 +135,7 @@ public class FiltersActivity extends BaseSupportActivity implements TabListener, } case R.id.enable_in_mentions: { final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putBoolean(KEY_FILTERS_IN_MENTIONS, !item.isChecked()); + editor.putBoolean(KEY_FILTERS_IN_MENTIONS_TIMELINE, !item.isChecked()); editor.apply(); break; } @@ -155,12 +150,29 @@ public class FiltersActivity extends BaseSupportActivity implements TabListener, } @Override - public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) { + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE); + setContentView(R.layout.activity_filters); + mActionBar = getActionBar(); + mAdapter = new SupportTabsAdapter(this, getSupportFragmentManager(), null); + mActionBar.setDisplayHomeAsUpEnabled(true); + mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + addTab(FilteredUsersFragment.class, R.string.users, 0); + addTab(FilteredKeywordsFragment.class, R.string.keywords, 1); + addTab(FilteredSourcesFragment.class, R.string.sources, 2); + addTab(FilteredLinksFragment.class, R.string.links, 3); + mViewPager.setAdapter(mAdapter); + mViewPager.setOnPageChangeListener(this); } @Override - public void onPageScrollStateChanged(final int state) { + public boolean getSystemWindowsInsets(Rect insets) { + return false; + } + @Override + public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) { } @Override @@ -170,18 +182,7 @@ public class FiltersActivity extends BaseSupportActivity implements TabListener, } @Override - public boolean onPrepareOptionsMenu(final Menu menu) { - final boolean enable_in_home_timeline = mPreferences.getBoolean(KEY_FILTERS_IN_HOME_TIMELINE, true); - final boolean enable_in_mentions = mPreferences.getBoolean(KEY_FILTERS_IN_MENTIONS, true); - final boolean enable_for_rts = mPreferences.getBoolean(KEY_FILTERS_FOR_RTS, true); - menu.findItem(R.id.enable_in_home_timeline).setChecked(enable_in_home_timeline); - menu.findItem(R.id.enable_in_mentions).setChecked(enable_in_mentions); - menu.findItem(R.id.enable_for_rts).setChecked(enable_for_rts); - return true; - } - - @Override - public void onTabReselected(final Tab tab, final FragmentTransaction ft) { + public void onPageScrollStateChanged(final int state) { } @@ -195,6 +196,11 @@ public class FiltersActivity extends BaseSupportActivity implements TabListener, } + @Override + public void onTabReselected(final Tab tab, final FragmentTransaction ft) { + + } + @Override protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) { switch (requestCode) { @@ -205,7 +211,7 @@ public class FiltersActivity extends BaseSupportActivity implements TabListener, final ParcelableUser user = data.getParcelableExtra(EXTRA_USER); final ContentValues values = ContentValuesCreator.makeFilteredUserContentValues(user); final ContentResolver resolver = getContentResolver(); - resolver.delete(Filters.Users.CONTENT_URI, Where.equals(Filters.Users.USER_ID, user.id).getSQL(), null); + resolver.delete(Filters.Users.CONTENT_URI, Expression.equals(Filters.Users.USER_ID, user.id).getSQL(), null); resolver.insert(Filters.Users.CONTENT_URI, values); break; } diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/SettingsActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/SettingsActivity.java index 2218210a1..5b1a4f7f6 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/SettingsActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/SettingsActivity.java @@ -41,6 +41,7 @@ import android.view.ViewGroup.MarginLayoutParams; import android.view.ViewParent; import android.widget.BaseAdapter; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.TextView; @@ -235,7 +236,6 @@ public class SettingsActivity extends BasePreferenceActivity { static final int HEADER_TYPE_CATEGORY = 1; static final int HEADER_TYPE_SPACE = 2; - private final Context mContext; private final Resources mResources; private final int mActionIconColor; private final ArrayList
mHeaders; @@ -244,7 +244,6 @@ public class SettingsActivity extends BasePreferenceActivity { private boolean mFirstItemIsCategory; public HeaderAdapter(final Context context) { - mContext = context; mInflater = LayoutInflater.from(context); mHeaders = new ArrayList<>(); mResources = context.getResources(); @@ -385,6 +384,13 @@ public class SettingsActivity extends BasePreferenceActivity { holder.icon.setImageDrawable(null); } holder.icon.setColorFilter(mActionIconColor, Mode.SRC_ATOP); + + if (position > 0 && position <= getCount() - 1) { + final boolean prevCategory = getItemViewType(position - 1) == HEADER_TYPE_CATEGORY; + holder.content.setShowDividers(prevCategory ? LinearLayout.SHOW_DIVIDER_NONE : LinearLayout.SHOW_DIVIDER_END); + } else { + holder.content.setShowDividers(LinearLayout.SHOW_DIVIDER_NONE); + } } private View inflateItemView(int viewType, ViewGroup parent) { @@ -424,12 +430,14 @@ public class SettingsActivity extends BasePreferenceActivity { private static class HeaderViewHolder extends ViewHolder { private final TextView title, summary; private final ImageView icon; + private final LinearLayout content; HeaderViewHolder(final View view) { super(view); title = (TextView) findViewById(android.R.id.title); summary = (TextView) findViewById(android.R.id.summary); icon = (ImageView) findViewById(android.R.id.icon); + content = (LinearLayout) findViewById(android.R.id.content); } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java index 4f294c019..9590ebba2 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java @@ -83,8 +83,8 @@ import org.mariotaku.twidere.R; import org.mariotaku.twidere.adapter.BaseArrayAdapter; import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.fragment.support.BaseSupportDialogFragment; -import org.mariotaku.twidere.model.Account; import org.mariotaku.twidere.model.DraftItem; +import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.ParcelableLocation; import org.mariotaku.twidere.model.ParcelableMedia; import org.mariotaku.twidere.model.ParcelableMediaUpdate; @@ -186,7 +186,7 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa private boolean mIsPossiblySensitive, mShouldSaveAccounts; - private long[] mAccountIds, mSendAccountIds; + private long[] mSendAccountIds; private Uri mTempPhotoUri; private boolean mImageUploaderUsed, mStatusShortenerUsed; @@ -537,7 +537,7 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa public void saveToDrafts() { final String text = mEditText != null ? ParseUtils.parseString(mEditText.getText()) : null; final ParcelableStatusUpdate.Builder builder = new ParcelableStatusUpdate.Builder(); - builder.accounts(Account.getAccounts(this, mSendAccountIds)); + builder.accounts(ParcelableAccount.getAccounts(this, mSendAccountIds)); builder.text(text); builder.inReplyToStatusId(mInReplyToStatusId); builder.location(mRecentLocation); @@ -561,8 +561,8 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa setContentView(R.layout.activity_compose); setProgressBarIndeterminateVisibility(false); setFinishOnTouchOutside(false); - mAccountIds = getAccountIds(this); - if (mAccountIds.length <= 0) { + final long[] defaultAccountIds = getAccountIds(this); + if (defaultAccountIds.length <= 0) { final Intent intent = new Intent(INTENT_ACTION_TWITTER_LOGIN); intent.setClass(this, SignInActivity.class); startActivity(intent); @@ -574,7 +574,7 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa mEditText.setOnEditorActionListener(mPreferences.getBoolean(KEY_QUICK_SEND, false) ? this : null); mEditText.addTextChangedListener(this); final AccountSelectorAdapter accountAdapter = new AccountSelectorAdapter(mMenuBar.getPopupContext()); - accountAdapter.addAll(Account.getAccountsList(this, false)); + accountAdapter.addAll(ParcelableAccount.getAccountsList(this, false)); mAccountSelectorPopup = IListPopupWindow.InstanceHelper.getInstance(mMenuBar.getPopupContext()); mAccountSelectorPopup.setInputMethodMode(IListPopupWindow.INPUT_METHOD_NOT_NEEDED); mAccountSelectorPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); @@ -621,8 +621,8 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa if (mSendAccountIds == null || mSendAccountIds.length == 0) { final long[] idsInPrefs = ArrayUtils.parseLongArray( mPreferences.getString(KEY_COMPOSE_ACCOUNTS, null), ','); - final long[] intersection = ArrayUtils.intersection(idsInPrefs, mAccountIds); - mSendAccountIds = intersection.length > 0 ? intersection : mAccountIds; + final long[] intersection = ArrayUtils.intersection(idsInPrefs, defaultAccountIds); + mSendAccountIds = intersection.length > 0 ? intersection : defaultAccountIds; } mOriginalText = ParseUtils.parseString(mEditText.getText()); } @@ -732,7 +732,18 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa private boolean handleDefaultIntent(final Intent intent) { if (intent == null) return false; final String action = intent.getAction(); - mShouldSaveAccounts = !Intent.ACTION_SEND.equals(action) && !Intent.ACTION_SEND_MULTIPLE.equals(action); + final boolean hasAccountIds; + if (intent.hasExtra(EXTRA_ACCOUNT_IDS)) { + mSendAccountIds = intent.getLongArrayExtra(EXTRA_ACCOUNT_IDS); + hasAccountIds = true; + } else if (intent.hasExtra(EXTRA_ACCOUNT_ID)) { + mSendAccountIds = new long[]{intent.getLongExtra(EXTRA_ACCOUNT_ID, -1)}; + hasAccountIds = true; + } else { + hasAccountIds = false; + } + mShouldSaveAccounts = !Intent.ACTION_SEND.equals(action) + && !Intent.ACTION_SEND_MULTIPLE.equals(action) && !hasAccountIds; final Uri data = intent.getData(); final CharSequence extraSubject = intent.getCharSequenceExtra(Intent.EXTRA_SUBJECT); final CharSequence extraText = intent.getCharSequenceExtra(Intent.EXTRA_TEXT); @@ -1265,7 +1276,7 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa } - private static class AccountSelectorAdapter extends BaseArrayAdapter { + private static class AccountSelectorAdapter extends BaseArrayAdapter { public AccountSelectorAdapter(final Context context) { super(context, android.R.layout.simple_list_item_multiple_choice); @@ -1284,7 +1295,7 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa @Override public View getView(int position, View convertView, ViewGroup parent) { final View view = super.getView(position, convertView, parent); - final Account account = getItem(position); + final ParcelableAccount account = getItem(position); final TextView text1 = (TextView) view.findViewById(android.R.id.text1); text1.setText(Utils.getAccountDisplayName(getContext(), account.account_id, isDisplayNameFirst())); return view; diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/CustomTabEditorActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/CustomTabEditorActivity.java index be35d00fb..59deedd67 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/CustomTabEditorActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/CustomTabEditorActivity.java @@ -48,7 +48,7 @@ import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter; import org.mariotaku.twidere.adapter.ArrayAdapter; import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.fragment.support.BaseSupportDialogFragment; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.CustomTabConfiguration; import org.mariotaku.twidere.model.CustomTabConfiguration.ExtraConfiguration; import org.mariotaku.twidere.model.ParcelableUser; @@ -303,10 +303,10 @@ public class CustomTabEditorActivity extends BaseSupportDialogActivity implement mSecondaryFieldContainer.setVisibility(has_secondary_field ? View.VISIBLE : View.GONE); final boolean accountIdRequired = conf.getAccountRequirement() == CustomTabConfiguration.ACCOUNT_REQUIRED; if (!accountIdRequired) { - mAccountsAdapter.add(Account.dummyInstance()); + mAccountsAdapter.add(ParcelableAccount.dummyInstance()); } final boolean officialKeyOnly = intent.getBooleanExtra(EXTRA_OFFICIAL_KEY_ONLY, false); - mAccountsAdapter.addAll(Account.getAccountsList(this, false, officialKeyOnly)); + mAccountsAdapter.addAll(ParcelableAccount.getAccountsList(this, false, officialKeyOnly)); switch (conf.getSecondaryFieldType()) { case CustomTabConfiguration.FIELD_TYPE_USER: { mSecondaryFieldLabel.setText(R.string.user); diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/DraftsActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/DraftsActivity.java index 74f33d20a..1ea5eb8d9 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/DraftsActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/DraftsActivity.java @@ -50,8 +50,8 @@ import android.widget.ListView; import org.mariotaku.menucomponent.widget.PopupMenu; import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.RawItemArray; -import org.mariotaku.querybuilder.Where; import org.mariotaku.twidere.R; import org.mariotaku.twidere.adapter.DraftsAdapter; import org.mariotaku.twidere.fragment.support.BaseSupportDialogFragment; @@ -106,7 +106,7 @@ public class DraftsActivity extends BaseSupportActivity implements LoaderCallbac } } if (sendDrafts(list)) { - final Where where = Where.in(new Column(Drafts._ID), + final Expression where = Expression.in(new Column(Drafts._ID), new RawItemArray(mListView.getCheckedItemIds())); mResolver.delete(Drafts.CONTENT_URI, where.getSQL(), null); } @@ -237,7 +237,7 @@ public class DraftsActivity extends BaseSupportActivity implements LoaderCallbac final Bundle bundle = new Bundle(); bundle.putParcelable(EXTRA_DRAFT, draft); intent.putExtras(bundle); - mResolver.delete(Drafts.CONTENT_URI, Where.equals(Drafts._ID, draft._id).getSQL(), null); + mResolver.delete(Drafts.CONTENT_URI, Expression.equals(Drafts._ID, draft._id).getSQL(), null); startActivityForResult(intent, REQUEST_COMPOSE); } @@ -307,7 +307,7 @@ public class DraftsActivity extends BaseSupportActivity implements LoaderCallbac @Override protected Integer doInBackground(final Void... params) { final ContentResolver resolver = mActivity.getContentResolver(); - final Where where = Where.in(new Column(Drafts._ID), new RawItemArray(mIds)); + final Expression where = Expression.in(new Column(Drafts._ID), new RawItemArray(mIds)); final String[] projection = {Drafts.MEDIA}; final Cursor c = resolver.query(Drafts.CONTENT_URI, projection, where.getSQL(), null, null); final int idxMedia = c.getColumnIndex(Drafts.MEDIA); diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java index c19a31ab7..58e7588c3 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java @@ -77,7 +77,7 @@ import org.mariotaku.twidere.fragment.support.AccountsDashboardFragment; import org.mariotaku.twidere.fragment.support.DirectMessagesFragment; import org.mariotaku.twidere.fragment.support.TrendsSuggectionsFragment; import org.mariotaku.twidere.graphic.EmptyDrawable; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.SupportTabSpec; import org.mariotaku.twidere.provider.TweetStore.Accounts; import org.mariotaku.twidere.task.AsyncTask; @@ -146,7 +146,7 @@ public class HomeActivity extends BaseSupportActivity implements OnClickListener private final ArrayList mCustomTabs = new ArrayList(); private final SparseArray mAttachedFragments = new SparseArray(); - private Account mSelectedAccountToSearch; + private ParcelableAccount mSelectedAccountToSearch; private SharedPreferences mPreferences; @@ -424,7 +424,7 @@ public class HomeActivity extends BaseSupportActivity implements OnClickListener } } - public void openSearchView(final Account account) { + public void openSearchView(final ParcelableAccount account) { mSelectedAccountToSearch = account; onSearchRequested(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/LinkHandlerActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/LinkHandlerActivity.java index 0b397dab0..e8f7dbb5b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/LinkHandlerActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/LinkHandlerActivity.java @@ -210,8 +210,7 @@ public class LinkHandlerActivity extends BaseSupportActivity implements OnClickL break; } case LINK_ID_STATUS: { -// transitionRes = R.transition.transition_status; - transitionRes = 0; + transitionRes = R.transition.transition_status; break; } default: { @@ -221,7 +220,7 @@ public class LinkHandlerActivity extends BaseSupportActivity implements OnClickL } if (transitionRes != 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !ThemeUtils.isTransparentBackground(this)) { - Utils.setSharedElementTransition(this, window, R.transition.transition_status); + Utils.setSharedElementTransition(this, window, transitionRes); } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsStatusesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsStatusesAdapter.java index f1ff8a54c..1d33afeb8 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsStatusesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsStatusesAdapter.java @@ -14,7 +14,6 @@ import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; import org.mariotaku.twidere.adapter.iface.IStatusesAdapter; import org.mariotaku.twidere.app.TwidereApplication; -import org.mariotaku.twidere.fragment.support.StatusFragment; import org.mariotaku.twidere.fragment.support.StatusMenuDialogFragment; import org.mariotaku.twidere.fragment.support.UserFragment; import org.mariotaku.twidere.model.ParcelableStatus; @@ -42,6 +41,8 @@ public abstract class AbsStatusesAdapter extends Adapter implemen private final int mCardLayoutResource; private boolean mLoadMoreIndicatorEnabled; + private EventListener mEventListener; + public AbsStatusesAdapter(Context context, boolean compact) { mContext = context; mInflater = LayoutInflater.from(context); @@ -59,7 +60,7 @@ public abstract class AbsStatusesAdapter extends Adapter implemen switch (viewType) { case ITEM_VIEW_TYPE_STATUS: { final View view = mInflater.inflate(mCardLayoutResource, parent, false); - return new StatusViewHolder<>(this, view); + return new StatusViewHolder(this, view); } case ITEM_VIEW_TYPE_GAP: { final View view = mInflater.inflate(R.layout.card_item_gap, parent, false); @@ -126,16 +127,10 @@ public abstract class AbsStatusesAdapter extends Adapter implemen } @Override - public void onStatusClick(StatusViewHolder holder, int position) { - final Context context = getContext(); -// final View cardView = holder.getCardView(); -// if (cardView != null && context instanceof FragmentActivity) { -// final Bundle options = Utils.makeSceneTransitionOption((FragmentActivity) context, -// new Pair<>(cardView, StatusFragment.TRANSITION_NAME_CARD)); -// Utils.openStatus(context, getStatus(position), options); -// } else { - Utils.openStatus(context, getStatus(position), null); -// } + public final void onStatusClick(StatusViewHolder holder, int position) { + if (mEventListener != null) { + mEventListener.onStatusClick(holder, position); + } } @Override @@ -153,18 +148,42 @@ public abstract class AbsStatusesAdapter extends Adapter implemen } @Override - public void onItemMenuClick(StatusViewHolder holder, int position) { - final Context context = getContext(); - if (!(context instanceof FragmentActivity)) return; - final Bundle args = new Bundle(); - args.putParcelable(EXTRA_STATUS, getStatus(position)); - final StatusMenuDialogFragment f = new StatusMenuDialogFragment(); - f.setArguments(args); - f.show(((FragmentActivity) context).getSupportFragmentManager(), "status_menu"); + public void onItemActionClick(ViewHolder holder, int id, int position) { + if (mEventListener != null) { + mEventListener.onStatusActionClick((StatusViewHolder) holder, id, position); + } + } + + @Override + public void onItemMenuClick(ViewHolder holder, int position) { + if (mEventListener != null) { + mEventListener.onStatusMenuClick((StatusViewHolder) holder, position); + } } public abstract void setData(D data); public abstract D getData(); + public void setEventListener(EventListener listener) { + mEventListener = listener; + } + + @Override + public final void onGapClick(ViewHolder holder, int position) { + if (mEventListener != null) { + mEventListener.onGapClick((GapViewHolder) holder, position); + } + } + + public static interface EventListener { + void onStatusActionClick(StatusViewHolder holder, int id, int position); + + void onStatusClick(StatusViewHolder holder, int position); + + void onGapClick(GapViewHolder holder, int position); + + void onStatusMenuClick(StatusViewHolder holder, int position); + } + } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/AccountsAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/AccountsAdapter.java index 8608312e1..c2f343ad7 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/AccountsAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/AccountsAdapter.java @@ -32,8 +32,8 @@ import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; import org.mariotaku.twidere.adapter.iface.IBaseAdapter; import org.mariotaku.twidere.app.TwidereApplication; -import org.mariotaku.twidere.model.Account; -import org.mariotaku.twidere.model.Account.Indices; +import org.mariotaku.twidere.model.ParcelableAccount; +import org.mariotaku.twidere.model.ParcelableAccount.Indices; import org.mariotaku.twidere.provider.TweetStore.Accounts; import org.mariotaku.twidere.util.ImageLoaderWrapper; import org.mariotaku.twidere.view.holder.AccountViewHolder; @@ -58,10 +58,10 @@ public class AccountsAdapter extends SimpleDragSortCursorAdapter implements Cons mPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); } - public Account getAccount(int position) { + public ParcelableAccount getAccount(int position) { final Cursor c = getCursor(); if (c == null || c.isClosed() || !c.moveToPosition(position)) return null; - return new Account(c, mIndices); + return new ParcelableAccount(c, mIndices); } @Override diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/AccountsSpinnerAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/AccountsSpinnerAdapter.java index 8985b6ecb..d60f1b197 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/AccountsSpinnerAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/AccountsSpinnerAdapter.java @@ -20,7 +20,6 @@ package org.mariotaku.twidere.adapter; import android.content.Context; -import android.graphics.PorterDuff.Mode; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -29,13 +28,12 @@ import android.widget.TextView; import org.mariotaku.twidere.R; import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.fragment.support.DirectMessagesConversationFragment; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.util.ImageLoaderWrapper; -import org.mariotaku.twidere.util.ThemeUtils; import java.util.Collection; -public class AccountsSpinnerAdapter extends ArrayAdapter { +public class AccountsSpinnerAdapter extends ArrayAdapter { private final ImageLoaderWrapper mImageLoader; private final boolean mDisplayProfileImage; @@ -48,7 +46,7 @@ public class AccountsSpinnerAdapter extends ArrayAdapter { Context.MODE_PRIVATE).getBoolean(DirectMessagesConversationFragment.KEY_DISPLAY_PROFILE_IMAGE, true); } - public AccountsSpinnerAdapter(final Context context, final Collection accounts) { + public AccountsSpinnerAdapter(final Context context, final Collection accounts) { this(context); addAll(accounts); } @@ -67,7 +65,7 @@ public class AccountsSpinnerAdapter extends ArrayAdapter { return view; } - private void bindView(final View view, final Account item) { + private void bindView(final View view, final ParcelableAccount item) { final TextView text1 = (TextView) view.findViewById(android.R.id.text1); final TextView text2 = (TextView) view.findViewById(android.R.id.text2); final ImageView icon = (ImageView) view.findViewById(android.R.id.icon); diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesAdapter.java index 044c0a301..7ac0c84c2 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesAdapter.java @@ -73,11 +73,6 @@ public class CursorStatusesAdapter extends AbsStatusesAdapter { notifyDataSetChanged(); } - @Override - public void onGapClick(StatusViewHolder holder, int position) { - - } - @Override public Cursor getData() { return mCursor; diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesListAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesListAdapter.java index ef0da562e..0afbd7253 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesListAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/CursorStatusesListAdapter.java @@ -25,6 +25,7 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.support.v4.util.Pair; +import android.support.v7.widget.RecyclerView.ViewHolder; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; @@ -121,7 +122,12 @@ public class CursorStatusesListAdapter extends BaseCursorAdapter implements ISta } @Override - public void onItemMenuClick(StatusViewHolder holder, int position) { + public void onItemActionClick(ViewHolder holder, int id, int position) { + + } + + @Override + public void onItemMenuClick(ViewHolder holder, int position) { } @@ -364,7 +370,7 @@ public class CursorStatusesListAdapter extends BaseCursorAdapter implements ISta } @Override - public void onGapClick(StatusViewHolder holder, int position) { + public void onGapClick(ViewHolder holder, int position) { } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/DirectMessageConversationEntriesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/DirectMessageConversationEntriesAdapter.java index c29146791..340a0e3b5 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/DirectMessageConversationEntriesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/DirectMessageConversationEntriesAdapter.java @@ -33,7 +33,6 @@ import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.provider.TweetStore.DirectMessages.ConversationEntries; import org.mariotaku.twidere.util.ImageLoaderWrapper; import org.mariotaku.twidere.util.MultiSelectManager; -import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.view.holder.DirectMessageEntryViewHolder; import static org.mariotaku.twidere.provider.TweetStore.DirectMessages.ConversationEntries.IDX_ACCOUNT_ID; @@ -55,15 +54,8 @@ public class DirectMessageConversationEntriesAdapter extends BaseCursorAdapter i private final ImageLoaderWrapper mImageLoader; private final MultiSelectManager mMultiSelectManager; - private boolean mAnimationEnabled; - private int mMaxAnimationPosition; - public DirectMessageConversationEntriesAdapter(final Context context) { - this(context, Utils.isCompactCards(context)); - } - - public DirectMessageConversationEntriesAdapter(final Context context, final boolean compactCards) { - super(context, getItemResource(compactCards), null, new String[0], new int[0], 0); + super(context, R.layout.list_item_message_entry, null, new String[0], new int[0], 0); final TwidereApplication app = TwidereApplication.getInstance(context); mMultiSelectManager = app.getMultiSelectManager(); mImageLoader = app.getImageLoaderWrapper(); @@ -96,8 +88,6 @@ public class DirectMessageConversationEntriesAdapter extends BaseCursorAdapter i final String nick = getUserNickname(context, conversationId); holder.name.setText(TextUtils.isEmpty(nick) ? name : isNicknameOnly() ? nick : context.getString( R.string.name_with_nickname, name, nick)); - holder.screen_name.setText("@" + screenName); - holder.screen_name.setVisibility(View.VISIBLE); holder.text.setText(toPlainText(cursor.getString(IDX_TEXT))); holder.time.setTime(timestamp); holder.setIsOutgoing(isOutgoing); @@ -110,12 +100,6 @@ public class DirectMessageConversationEntriesAdapter extends BaseCursorAdapter i } else { mImageLoader.cancelDisplayTask(holder.profile_image); } - if (position > mMaxAnimationPosition) { - if (mAnimationEnabled) { - view.startAnimation(holder.item_animation); - } - mMaxAnimationPosition = position; - } super.bindView(view, context, cursor); } @@ -175,20 +159,14 @@ public class DirectMessageConversationEntriesAdapter extends BaseCursorAdapter i @Override public void setAnimationEnabled(final boolean anim) { - if (mAnimationEnabled == anim) return; - mAnimationEnabled = anim; } @Override public void setMaxAnimationPosition(final int position) { - mMaxAnimationPosition = position; } @Override public void setMenuButtonClickListener(final MenuButtonClickListener listener) { } - private static int getItemResource(final boolean compactCards) { - return compactCards ? R.layout.card_item_message_entry_compact : R.layout.card_item_message_entry; - } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.java index ff1c5b711..e92caff26 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.java @@ -23,11 +23,6 @@ public class ParcelableStatusesAdapter extends AbsStatusesAdapter { - void onItemMenuClick(VH holder, int position); +public interface ICardSupportedAdapter { + void onItemActionClick(ViewHolder holder, int id, int position); + + void onItemMenuClick(ViewHolder holder, int position); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IGapSupportedAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IGapSupportedAdapter.java index c07675892..461144545 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IGapSupportedAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IGapSupportedAdapter.java @@ -24,10 +24,10 @@ import android.support.v7.widget.RecyclerView.ViewHolder; /** * Created by mariotaku on 14/12/3. */ -public interface IGapSupportedAdapter { +public interface IGapSupportedAdapter { boolean isGapItem(int position); - void onGapClick(VH holder, int position); + void onGapClick(ViewHolder holder, int position); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java index 31bde7ad7..7b5499ad5 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java @@ -10,8 +10,7 @@ import org.mariotaku.twidere.view.holder.StatusViewHolder; /** * Created by mariotaku on 14/11/18. */ -public interface IStatusesAdapter extends IGapSupportedAdapter, - IItemMenuSupportedAdapter { +public interface IStatusesAdapter extends IGapSupportedAdapter, ICardSupportedAdapter { ImageLoaderWrapper getImageLoader(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java b/twidere/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java index 84e4c8a9a..0b9504bd3 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java +++ b/twidere/src/main/java/org/mariotaku/twidere/constant/IntentConstants.java @@ -85,7 +85,7 @@ public interface IntentConstants { public static final String BROADCAST_FRIENDSHIP_DENIED = INTENT_PACKAGE_PREFIX + "FRIENDSHIP_DENIED"; public static final String BROADCAST_FAVORITE_CHANGED = INTENT_PACKAGE_PREFIX + "FAVORITE_CHANGED"; - public static final String BROADCAST_RETWEET_CHANGED = INTENT_PACKAGE_PREFIX + "RETWEET_CHANGED"; + public static final String BROADCAST_STATUS_RETWEETED = INTENT_PACKAGE_PREFIX + "STATUS_RETWEETED"; public static final String BROADCAST_STATUS_DESTROYED = INTENT_PACKAGE_PREFIX + "STATUS_DESTROYED"; public static final String BROADCAST_USER_LIST_MEMBERS_DELETED = INTENT_PACKAGE_PREFIX + "USER_LIST_MEMBER_DELETED"; public static final String BROADCAST_USER_LIST_MEMBERS_ADDED = INTENT_PACKAGE_PREFIX + "USER_LIST_MEMBER_ADDED"; diff --git a/twidere/src/main/java/org/mariotaku/twidere/constant/SharedPreferenceConstants.java b/twidere/src/main/java/org/mariotaku/twidere/constant/SharedPreferenceConstants.java index ee0077b17..8b1f355d8 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/constant/SharedPreferenceConstants.java +++ b/twidere/src/main/java/org/mariotaku/twidere/constant/SharedPreferenceConstants.java @@ -253,7 +253,7 @@ public interface SharedPreferenceConstants { @Preference(type = STRING, hasDefault = true, defaultString = TwidereConstants.TWITTER_CONSUMER_SECRET_3) public static final String KEY_CONSUMER_SECRET = "consumer_secret"; public static final String KEY_FILTERS_IN_HOME_TIMELINE = "filters_in_home_timeline"; - public static final String KEY_FILTERS_IN_MENTIONS = "filters_in_mentions"; + public static final String KEY_FILTERS_IN_MENTIONS_TIMELINE = "filters_in_mentions"; public static final String KEY_FILTERS_FOR_RTS = "filters_for_rts"; @Preference(type = BOOLEAN, hasDefault = true, defaultBoolean = false) public static final String KEY_NICKNAME_ONLY = "nickname_only"; diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/AccountNotificationSettingsFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/AccountNotificationSettingsFragment.java index 04cf15552..87fff3f26 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/AccountNotificationSettingsFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/AccountNotificationSettingsFragment.java @@ -23,7 +23,7 @@ import android.os.Bundle; import android.preference.Preference; import org.mariotaku.twidere.R; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; public class AccountNotificationSettingsFragment extends BaseAccountPreferenceFragment { @@ -31,7 +31,7 @@ public class AccountNotificationSettingsFragment extends BaseAccountPreferenceFr public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); final Preference preference = findPreference(KEY_NOTIFICATION_LIGHT_COLOR); - final Account account = getAccount(); + final ParcelableAccount account = getAccount(); if (preference != null && account != null) { preference.setDefaultValue(account.color); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseAccountPreferenceFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseAccountPreferenceFragment.java index 1a5cc76fe..8b5ca25e0 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseAccountPreferenceFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseAccountPreferenceFragment.java @@ -39,7 +39,7 @@ import android.widget.Switch; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; public abstract class BaseAccountPreferenceFragment extends PreferenceFragment implements Constants, OnCheckedChangeListener, OnSharedPreferenceChangeListener { @@ -49,7 +49,7 @@ public abstract class BaseAccountPreferenceFragment extends PreferenceFragment i super.onActivityCreated(savedInstanceState); setHasOptionsMenu(true); final PreferenceManager pm = getPreferenceManager(); - final Account account = getArguments().getParcelable(EXTRA_ACCOUNT); + final ParcelableAccount account = getArguments().getParcelable(EXTRA_ACCOUNT); final String preferenceName = ACCOUNT_PREFERENCES_NAME_PREFIX + (account != null ? account.account_id : "unknown"); pm.setSharedPreferencesName(preferenceName); @@ -93,7 +93,7 @@ public abstract class BaseAccountPreferenceFragment extends PreferenceFragment i } } - protected Account getAccount() { + protected ParcelableAccount getAccount() { final Bundle args = getArguments(); if (args == null) return null; return args.getParcelable(EXTRA_ACCOUNT); diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseFiltersFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseFiltersFragment.java index 9c2db588e..43169491b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseFiltersFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseFiltersFragment.java @@ -45,8 +45,8 @@ import android.widget.SimpleCursorAdapter; import android.widget.TextView; import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.RawItemArray; -import org.mariotaku.querybuilder.Where; import org.mariotaku.twidere.R; import org.mariotaku.twidere.fragment.support.BaseSupportListFragment; import org.mariotaku.twidere.provider.TweetStore.Filters; @@ -83,7 +83,7 @@ public abstract class BaseFiltersFragment extends BaseSupportListFragment implem public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) { switch (item.getItemId()) { case MENU_DELETE: { - final Where where = Where.in(new Column(Filters._ID), new RawItemArray(mListView.getCheckedItemIds())); + final Expression where = Expression.in(new Column(Filters._ID), new RawItemArray(mListView.getCheckedItemIds())); mResolver.delete(getContentUri(), where.getSQL(), null); break; } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/CustomTabsFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/CustomTabsFragment.java index e5a4dbb42..03ff1398a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/CustomTabsFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/CustomTabsFragment.java @@ -52,8 +52,8 @@ import com.mobeta.android.dslv.SimpleDragSortCursorAdapter; import org.mariotaku.menucomponent.widget.PopupMenu; import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.RawItemArray; -import org.mariotaku.querybuilder.Where; import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.support.CustomTabEditorActivity; import org.mariotaku.twidere.model.CustomTabConfiguration; @@ -101,7 +101,7 @@ public class CustomTabsFragment extends BaseListFragment implements LoaderCallba public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) { switch (item.getItemId()) { case MENU_DELETE: { - final Where where = Where.in(new Column(Tabs._ID), new RawItemArray(mListView.getCheckedItemIds())); + final Expression where = Expression.in(new Column(Tabs._ID), new RawItemArray(mListView.getCheckedItemIds())); mResolver.delete(Tabs.CONTENT_URI, where.getSQL(), null); break; } @@ -150,7 +150,7 @@ public class CustomTabsFragment extends BaseListFragment implements LoaderCallba values.put(Tabs.NAME, data.getStringExtra(EXTRA_NAME)); values.put(Tabs.ICON, data.getStringExtra(EXTRA_ICON)); values.put(Tabs.EXTRAS, data.getStringExtra(EXTRA_EXTRAS)); - final String where = Where.equals(Tabs._ID, data.getLongExtra(EXTRA_ID, -1)).getSQL(); + final String where = Expression.equals(Tabs._ID, data.getLongExtra(EXTRA_ID, -1)).getSQL(); mResolver.update(Tabs.CONTENT_URI, values, where, null); } break; diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java index 504103922..5c920e4a2 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java @@ -1,11 +1,13 @@ package org.mariotaku.twidere.fragment.support; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.graphics.Rect; import android.os.Bundle; import android.support.annotation.Nullable; -import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; import android.support.v4.widget.SwipeRefreshLayout; @@ -19,27 +21,77 @@ import android.view.ViewGroup; import org.mariotaku.twidere.R; import org.mariotaku.twidere.adapter.AbsStatusesAdapter; +import org.mariotaku.twidere.adapter.AbsStatusesAdapter.EventListener; import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration; +import org.mariotaku.twidere.constant.IntentConstants; +import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface; +import org.mariotaku.twidere.model.ParcelableStatus; +import org.mariotaku.twidere.util.AsyncTwitterWrapper; import org.mariotaku.twidere.util.SimpleDrawerCallback; import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.view.HeaderDrawerLayout.DrawerCallback; +import org.mariotaku.twidere.view.holder.GapViewHolder; +import org.mariotaku.twidere.view.holder.StatusViewHolder; /** * Created by mariotaku on 14/11/5. */ -public abstract class AbsStatusesFragment extends BaseSupportFragment - implements LoaderCallbacks, OnRefreshListener, DrawerCallback { +public abstract class AbsStatusesFragment extends BaseSupportFragment implements LoaderCallbacks, + OnRefreshListener, DrawerCallback, RefreshScrollTopInterface, EventListener { + + private final BroadcastReceiver mStateReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(final Context context, final Intent intent) { + if (getActivity() == null || !isAdded() || isDetached()) return; + onReceivedBroadcast(intent, intent.getAction()); + } + + }; private View mContentView; private SharedPreferences mPreferences; + private View mProgressContainer; + private SwipeRefreshLayout mSwipeRefreshLayout; + private RecyclerView mRecyclerView; + private AbsStatusesAdapter mAdapter; + private SimpleDrawerCallback mDrawerCallback; + private OnScrollListener mOnScrollListener = new OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + final LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); + if (isRefreshing()) return; + if (layoutManager.findLastVisibleItemPosition() == mAdapter.getItemCount() - 1) { + onLoadMoreStatuses(); + } + } + }; + + @Override + public boolean canScroll(float dy) { + return mDrawerCallback.canScroll(dy); + } + + @Override + public void cancelTouch() { + mDrawerCallback.cancelTouch(); + } @Override public void fling(float velocity) { mDrawerCallback.fling(velocity); } - public abstract int getStatuses(long[] accountIds, long[] maxIds, long[] sinceIds); + @Override + public boolean isScrollContent(float x, float y) { + return mDrawerCallback.isScrollContent(x, y); + } @Override public void scrollBy(float dy) { @@ -51,51 +103,34 @@ public abstract class AbsStatusesFragment extends BaseSupportFragment return mDrawerCallback.shouldLayoutHeaderBottom(); } - @Override - public boolean canScroll(float dy) { - return mDrawerCallback.canScroll(dy); - } - - @Override - public boolean isScrollContent(float x, float y) { - return mDrawerCallback.isScrollContent(x, y); - } - - @Override - public void cancelTouch() { - mDrawerCallback.cancelTouch(); - } - @Override public void topChanged(int offset) { mDrawerCallback.topChanged(offset); } - private View mProgressContainer; - private SwipeRefreshLayout mSwipeRefreshLayout; - private RecyclerView mRecyclerView; + public AbsStatusesAdapter getAdapter() { + return mAdapter; + } - private AbsStatusesAdapter mAdapter; + public SharedPreferences getSharedPreferences() { + if (mPreferences != null) return mPreferences; + return mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); + } - private SimpleDrawerCallback mDrawerCallback; + public abstract int getStatuses(long[] accountIds, long[] maxIds, long[] sinceIds); - private OnScrollListener mOnScrollListener = new OnScrollListener() { - @Override - public void onScrollStateChanged(RecyclerView recyclerView, int newState) { - super.onScrollStateChanged(recyclerView, newState); - } + public boolean isRefreshing() { + return mSwipeRefreshLayout.isRefreshing(); + } - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - final LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); - final LoaderManager lm = getLoaderManager(); - if (lm.hasRunningLoaders()) return; - if (layoutManager.findLastVisibleItemPosition() == mAdapter.getItemCount() - 1) { - final long[] maxIds = new long[]{mAdapter.getStatus(mAdapter.getStatusCount() - 1).id}; - getStatuses(null, maxIds, null); - } - } - }; + public void setRefreshing(boolean refreshing) { + mSwipeRefreshLayout.setRefreshing(refreshing); + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_recycler_view, container, false); + } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { @@ -116,17 +151,86 @@ public abstract class AbsStatusesFragment extends BaseSupportFragment } mRecyclerView.setAdapter(mAdapter); mRecyclerView.setOnScrollListener(mOnScrollListener); + mAdapter.setEventListener(this); getLoaderManager().initLoader(0, getArguments(), this); setListShown(false); } - protected abstract AbsStatusesAdapter onCreateAdapter(Context context, boolean compact); - - private void setListShown(boolean shown) { - mProgressContainer.setVisibility(shown ? View.GONE : View.VISIBLE); - mSwipeRefreshLayout.setVisibility(shown ? View.VISIBLE : View.GONE); + @Override + public void onStart() { + super.onStart(); + final IntentFilter filter = new IntentFilter(); + onSetIntentFilter(filter); + registerReceiver(mStateReceiver, filter); } + @Override + public void onStop() { + unregisterReceiver(mStateReceiver); + super.onStop(); + } + + @Override + public void onLoadFinished(Loader loader, Data data) { + setRefreshing(false); + mAdapter.setData(data); + setListShown(true); + } + + @Override + public void onLoaderReset(Loader loader) { + } + + @Override + public void onRefresh() { + triggerRefresh(); + } + + @Override + public void onStatusActionClick(StatusViewHolder holder, int id, int position) { + switch (id) { + case R.id.reply_count: { + final Context context = getActivity(); + final Intent intent = new Intent(IntentConstants.INTENT_ACTION_REPLY); + intent.setPackage(context.getPackageName()); + intent.putExtra(IntentConstants.EXTRA_STATUS, mAdapter.getStatus(position)); + context.startActivity(intent); + break; + } + } + } + + @Override + public void onStatusClick(StatusViewHolder holder, int position) { + Utils.openStatus(getActivity(), mAdapter.getStatus(position), null); +// final View cardView = holder.getCardView(); +// if (cardView != null && context instanceof FragmentActivity) { +// final Bundle options = Utils.makeSceneTransitionOption((FragmentActivity) context, +// new Pair<>(cardView, StatusFragment.TRANSITION_NAME_CARD)); +// Utils.openStatus(context, getStatus(position), options); +// } else { +// Utils.openStatus(context, getStatus(position), null); +// } + } + + @Override + public void onGapClick(GapViewHolder holder, int position) { + final ParcelableStatus status = mAdapter.getStatus(position); + final long sinceId = position + 1 < mAdapter.getStatusCount() ? mAdapter.getStatus(position + 1).id : -1; + final long[] accountIds = {status.account_id}; + final long[] maxIds = {status.id}; + final long[] sinceIds = {sinceId}; + getStatuses(accountIds, maxIds, sinceIds); + } + + @Override + public void onStatusMenuClick(StatusViewHolder holder, int position) { + final Bundle args = new Bundle(); + args.putParcelable(EXTRA_STATUS, mAdapter.getStatus(position)); + final StatusMenuDialogFragment f = new StatusMenuDialogFragment(); + f.setArguments(args); + f.show(getActivity().getSupportFragmentManager(), "status_menu"); + } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { @@ -144,44 +248,36 @@ public abstract class AbsStatusesFragment extends BaseSupportFragment } @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_recycler_view, container, false); + public boolean scrollToStart() { + final AsyncTwitterWrapper twitter = getTwitterWrapper(); + final int tabPosition = getTabPosition(); + if (twitter != null && tabPosition != -1) { + twitter.clearUnreadCountAsync(tabPosition); + } + mRecyclerView.smoothScrollToPosition(0); + return true; } + protected abstract long[] getAccountIds(); + protected Data getAdapterData() { return mAdapter.getData(); } - public void setRefreshing(boolean refreshing) { - mSwipeRefreshLayout.setRefreshing(refreshing); - } - - @Override - public void onLoadFinished(Loader loader, Data data) { - setRefreshing(false); + protected void setAdapterData(Data data) { mAdapter.setData(data); - setListShown(true); } - @Override - public void onLoaderReset(Loader loader) { + protected abstract AbsStatusesAdapter onCreateAdapter(Context context, boolean compact); + + protected abstract void onLoadMoreStatuses(); + + protected abstract void onReceivedBroadcast(Intent intent, String action); + + protected abstract void onSetIntentFilter(IntentFilter filter); + + private void setListShown(boolean shown) { + mProgressContainer.setVisibility(shown ? View.GONE : View.VISIBLE); + mSwipeRefreshLayout.setVisibility(shown ? View.VISIBLE : View.GONE); } - - - @Override - public void onRefresh() { - if (mAdapter.getStatusCount() > 0) { - final long[] sinceIds = new long[]{mAdapter.getStatus(0).id}; - getStatuses(null, null, sinceIds); - } else { - getStatuses(null, null, null); - } - } - - public SharedPreferences getSharedPreferences() { - if (mPreferences != null) return mPreferences; - return mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); - } - - } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsDashboardFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsDashboardFragment.java index c49552e48..130715dd1 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsDashboardFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsDashboardFragment.java @@ -28,6 +28,7 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Resources; import android.database.Cursor; +import android.graphics.Bitmap; import android.graphics.PorterDuff.Mode; import android.graphics.Rect; import android.os.Bundle; @@ -54,20 +55,24 @@ import android.widget.Switch; import android.widget.TextView; import com.commonsware.cwac.merge.MergeAdapter; +import com.nostra13.universalimageloader.core.assist.FailReason; +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.FiltersActivity; import org.mariotaku.twidere.activity.SettingsActivity; import org.mariotaku.twidere.activity.iface.IThemedActivity; import org.mariotaku.twidere.activity.support.AccountsManagerActivity; +import org.mariotaku.twidere.activity.support.ComposeActivity; import org.mariotaku.twidere.activity.support.DraftsActivity; import org.mariotaku.twidere.activity.support.HomeActivity; import org.mariotaku.twidere.activity.support.UserProfileEditorActivity; import org.mariotaku.twidere.adapter.ArrayAdapter; import org.mariotaku.twidere.app.TwidereApplication; -import org.mariotaku.twidere.model.Account; -import org.mariotaku.twidere.model.Account.Indices; +import org.mariotaku.twidere.model.ParcelableAccount; +import org.mariotaku.twidere.model.ParcelableAccount.Indices; import org.mariotaku.twidere.provider.TweetStore.Accounts; +import org.mariotaku.twidere.util.CompareUtils; import org.mariotaku.twidere.util.ImageLoaderWrapper; import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.Utils; @@ -76,13 +81,11 @@ import org.mariotaku.twidere.util.content.SupportFragmentReloadCursorObserver; import java.util.ArrayList; import static org.mariotaku.twidere.util.Utils.openUserFavorites; -import static org.mariotaku.twidere.util.Utils.openUserListMemberships; import static org.mariotaku.twidere.util.Utils.openUserLists; import static org.mariotaku.twidere.util.Utils.openUserProfile; -import static org.mariotaku.twidere.util.Utils.openUserTimeline; public class AccountsDashboardFragment extends BaseSupportListFragment implements LoaderCallbacks, - OnSharedPreferenceChangeListener, OnCheckedChangeListener { + OnSharedPreferenceChangeListener, OnCheckedChangeListener, ImageLoadingListener, OnClickListener { private final SupportFragmentReloadCursorObserver mReloadContentObserver = new SupportFragmentReloadCursorObserver( this, 0, this); @@ -106,48 +109,6 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement private Context mThemedContext; private ImageLoaderWrapper mImageLoader; - - @Override - public void onActivityCreated(final Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); - mResolver = getContentResolver(); - final Context context = getView().getContext(); - mImageLoader = TwidereApplication.getInstance(context).getImageLoaderWrapper(); - final LayoutInflater inflater = LayoutInflater.from(context); - final ListView listView = getListView(); - mAdapter = new MergeAdapter(); - mAccountsAdapter = new AccountSelectorAdapter(context, this); - mAccountOptionsAdapter = new AccountOptionsAdapter(context); - mAppMenuAdapter = new AppMenuAdapter(context); - mAppMenuSectionView = newSectionView(context, R.string.more); - mAccountSelectorView = inflater.inflate(R.layout.header_drawer_account_selector, listView, false); - mAccountsSelector = (RecyclerView) mAccountSelectorView.findViewById(R.id.other_accounts_list); - final LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false); - layoutManager.setStackFromEnd(true); - mAccountsSelector.setLayoutManager(layoutManager); - mAccountsSelector.setAdapter(mAccountsAdapter); - mAccountProfileContainer = mAccountSelectorView.findViewById(R.id.profile_container); - mAccountProfileImageView = (ImageView) mAccountSelectorView.findViewById(R.id.profile_image); - mAccountProfileBannerView = (ImageView) mAccountSelectorView.findViewById(R.id.account_profile_banner); - mAccountProfileNameView = (TextView) mAccountSelectorView.findViewById(R.id.name); - mAccountProfileScreenNameView = (TextView) mAccountSelectorView.findViewById(R.id.screen_name); - mAccountsToggle = (Switch) mAccountSelectorView.findViewById(R.id.toggle); - mAccountsToggle.setOnCheckedChangeListener(this); - mAdapter.addView(mAccountSelectorView, false); - mAdapter.addAdapter(mAccountOptionsAdapter); - mAdapter.addView(mAppMenuSectionView, false); - mAdapter.addAdapter(mAppMenuAdapter); - setListAdapter(mAdapter); - mPreferences.registerOnSharedPreferenceChangeListener(this); - getLoaderManager().initLoader(0, null, this); - } - - @Override - protected void fitSystemWindows(Rect insets) { - // No-op - } - @Override public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { switch (requestCode) { @@ -163,14 +124,57 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement super.onActivityResult(requestCode, resultCode, data); } + @Override + public void onResume() { + super.onResume(); + updateDefaultAccountState(); + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + final ParcelableAccount account = mAccountsAdapter.getSelectedAccount(); + if (account == null) return; + final ContentValues values = new ContentValues(); + values.put(Accounts.IS_ACTIVATED, isChecked); + final String where = Accounts.ACCOUNT_ID + " = " + account.account_id; + mResolver.update(Accounts.CONTENT_URI, values, where, null); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.profile_container: { + final ParcelableAccount account = mAccountsAdapter.getSelectedAccount(); + if (account == null) return; + final FragmentActivity activity = getActivity(); + final Bundle activityOption = Utils.makeSceneTransitionOption(activity, + new Pair(mAccountProfileImageView, UserFragment.TRANSITION_NAME_PROFILE_IMAGE)); + openUserProfile(activity, account.account_id, account.account_id, + account.screen_name, activityOption); + break; + } + } + } + @Override public Loader onCreateLoader(final int id, final Bundle args) { return new CursorLoader(getActivity(), Accounts.CONTENT_URI, Accounts.COLUMNS, null, null, Accounts.SORT_POSITION); } @Override - public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { - return LayoutInflater.from(getThemedContext()).inflate(R.layout.fragment_accounts_drawer, container, false); + public void onLoadFinished(final Loader loader, final Cursor data) { + if (data != null && data.getCount() > 0 && mAccountsAdapter.getSelectedAccountId() <= 0) { + data.moveToFirst(); + mAccountsAdapter.setSelectedAccountId(data.getLong(data.getColumnIndex(Accounts.ACCOUNT_ID))); + } + mAccountsAdapter.changeCursor(data); + updateAccountOptionsSeparatorLabel(); + updateDefaultAccountState(); + } + + @Override + public void onLoaderReset(final Loader loader) { + mAccountsAdapter.changeCursor(null); } @Override @@ -178,18 +182,10 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement final ListAdapter adapter = mAdapter.getAdapter(position); final Object item = mAdapter.getItem(position); if (adapter instanceof AccountOptionsAdapter) { - final Account account = mAccountsAdapter.getSelectedAccount(); + final ParcelableAccount account = mAccountsAdapter.getSelectedAccount(); if (account == null || !(item instanceof OptionItem)) return; final OptionItem option = (OptionItem) item; switch (option.id) { - case MENU_VIEW_PROFILE: { - final FragmentActivity activity = getActivity(); - final Bundle activityOption = Utils.makeSceneTransitionOption(activity, - new Pair(mAccountProfileImageView, UserFragment.TRANSITION_NAME_PROFILE_IMAGE)); - openUserProfile(activity, account.account_id, account.account_id, - account.screen_name, activityOption); - break; - } case MENU_SEARCH: { final FragmentActivity a = getActivity(); if (a instanceof HomeActivity) { @@ -200,8 +196,11 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement closeAccountsDrawer(); break; } - case MENU_STATUSES: { - openUserTimeline(getActivity(), account.account_id, account.account_id, account.screen_name); + case MENU_COMPOSE: { + final Intent composeIntent = new Intent(INTENT_ACTION_COMPOSE); + composeIntent.setClass(getActivity(), ComposeActivity.class); + composeIntent.putExtra(EXTRA_ACCOUNT_IDS, new long[]{account.account_id}); + startActivity(composeIntent); break; } case MENU_FAVORITES: { @@ -212,10 +211,6 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement openUserLists(getActivity(), account.account_id, account.account_id, account.screen_name); break; } - case MENU_LIST_MEMBERSHIPS: { - openUserListMemberships(getActivity(), account.account_id, account.account_id, account.screen_name); - break; - } case MENU_EDIT: { final Bundle bundle = new Bundle(); bundle.putLong(EXTRA_ACCOUNT_ID, account.account_id); @@ -259,25 +254,23 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement } @Override - public void onLoaderReset(final Loader loader) { - mAccountsAdapter.changeCursor(null); + public void onLoadingStarted(String imageUri, View view) { + } @Override - public void onLoadFinished(final Loader loader, final Cursor data) { - if (data != null && data.getCount() > 0 && mAccountsAdapter.getSelectedAccountId() <= 0) { - data.moveToFirst(); - mAccountsAdapter.setSelectedAccountId(data.getLong(data.getColumnIndex(Accounts.ACCOUNT_ID))); - } - mAccountsAdapter.changeCursor(data); - updateAccountOptionsSeparatorLabel(); - updateDefaultAccountState(); + public void onLoadingFailed(String imageUri, View view, FailReason failReason) { + } @Override - public void onResume() { - super.onResume(); - updateDefaultAccountState(); + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { + view.setTag(imageUri); + } + + @Override + public void onLoadingCancelled(String imageUri, View view) { + } @Override @@ -287,6 +280,59 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement } } + public void setStatusBarHeight(int height) { + mAccountProfileContainer.setPadding(0, height, 0, 0); + } + + @Override + protected void fitSystemWindows(Rect insets) { + // No-op + } + + @Override + public void onActivityCreated(final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); + mResolver = getContentResolver(); + final Context context = getView().getContext(); + mImageLoader = TwidereApplication.getInstance(context).getImageLoaderWrapper(); + final LayoutInflater inflater = LayoutInflater.from(context); + final ListView listView = getListView(); + mAdapter = new MergeAdapter(); + mAccountsAdapter = new AccountSelectorAdapter(context, this); + mAccountOptionsAdapter = new AccountOptionsAdapter(context); + mAppMenuAdapter = new AppMenuAdapter(context); + mAppMenuSectionView = newSectionView(context, R.string.more); + mAccountSelectorView = inflater.inflate(R.layout.header_drawer_account_selector, listView, false); + mAccountsSelector = (RecyclerView) mAccountSelectorView.findViewById(R.id.other_accounts_list); + final LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false); + layoutManager.setStackFromEnd(true); + mAccountsSelector.setLayoutManager(layoutManager); + mAccountsSelector.setAdapter(mAccountsAdapter); + mAccountProfileContainer = mAccountSelectorView.findViewById(R.id.profile_container); + mAccountProfileImageView = (ImageView) mAccountSelectorView.findViewById(R.id.profile_image); + mAccountProfileBannerView = (ImageView) mAccountSelectorView.findViewById(R.id.account_profile_banner); + mAccountProfileNameView = (TextView) mAccountSelectorView.findViewById(R.id.name); + mAccountProfileScreenNameView = (TextView) mAccountSelectorView.findViewById(R.id.screen_name); + mAccountsToggle = (Switch) mAccountSelectorView.findViewById(R.id.toggle); + + mAccountProfileContainer.setOnClickListener(this); + + mAccountsToggle.setOnCheckedChangeListener(this); + mAdapter.addView(mAccountSelectorView, false); + mAdapter.addAdapter(mAccountOptionsAdapter); + mAdapter.addView(mAppMenuSectionView, false); + mAdapter.addAdapter(mAppMenuAdapter); + setListAdapter(mAdapter); + mPreferences.registerOnSharedPreferenceChangeListener(this); + getLoaderManager().initLoader(0, null, this); + } + + @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { + return LayoutInflater.from(getThemedContext()).inflate(R.layout.fragment_accounts_drawer, container, false); + } + @Override public void onStart() { super.onStart(); @@ -316,8 +362,21 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement return mThemedContext = new ContextThemeWrapper(context, themeResource); } + private static TextView newSectionView(final Context context, final int titleRes) { + final TextView textView = new TextView(context, null, android.R.attr.listSeparatorTextViewStyle); + if (titleRes != 0) { + textView.setText(titleRes); + } + return textView; + } + + private void onAccountSelected(ParcelableAccount account) { + mAccountsAdapter.setSelectedAccountId(account.account_id); + updateAccountOptionsSeparatorLabel(); + } + private void updateAccountOptionsSeparatorLabel() { - final Account account = mAccountsAdapter.getSelectedAccount(); + final ParcelableAccount account = mAccountsAdapter.getSelectedAccount(); if (account == null) { return; } @@ -329,43 +388,23 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement final Resources res = getResources(); final int defWidth = res.getDisplayMetrics().widthPixels; final int width = bannerWidth > 0 ? bannerWidth : defWidth; - mImageLoader.displayProfileBanner(mAccountProfileBannerView, account.profile_banner_url, width); + final String bannerUrl = Utils.getBestBannerUrl(account.profile_banner_url, width); + final ImageView bannerView = mAccountProfileBannerView; + if (bannerView.getDrawable() == null || !CompareUtils.objectEquals(bannerUrl, bannerView.getTag())) { + mImageLoader.displayProfileBanner(mAccountProfileBannerView, bannerUrl, this); + } } private void updateDefaultAccountState() { } - private static TextView newSectionView(final Context context, final int titleRes) { - final TextView textView = new TextView(context, null, android.R.attr.listSeparatorTextViewStyle); - if (titleRes != 0) { - textView.setText(titleRes); - } - return textView; - } - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - final Account account = mAccountsAdapter.getSelectedAccount(); - if (account == null) return; - final ContentValues values = new ContentValues(); - values.put(Accounts.IS_ACTIVATED, isChecked); - final String where = Accounts.ACCOUNT_ID + " = " + account.account_id; - mResolver.update(Accounts.CONTENT_URI, values, where, null); - } - - public void setStatusBarHeight(int height) { - mAccountProfileContainer.setPadding(0, height, 0, 0); - } - - private static final class AccountOptionsAdapter extends OptionItemsAdapter { private static final ArrayList sOptions = new ArrayList<>(); static { - sOptions.add(new OptionItem(R.string.profile, R.drawable.ic_action_profile, MENU_VIEW_PROFILE)); sOptions.add(new OptionItem(android.R.string.search_go, R.drawable.ic_action_search, MENU_SEARCH)); - sOptions.add(new OptionItem(R.string.statuses, R.drawable.ic_action_quote, MENU_STATUSES)); + sOptions.add(new OptionItem(R.string.compose, R.drawable.ic_action_status_compose, MENU_COMPOSE)); sOptions.add(new OptionItem(R.string.favorites, R.drawable.ic_action_star, MENU_FAVORITES)); sOptions.add(new OptionItem(R.string.lists, R.drawable.ic_action_list, MENU_LISTS)); } @@ -423,6 +462,36 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement mFragment = fragment; } + public void changeCursor(Cursor cursor) { + mCursor = cursor; + if (cursor != null) { + mIndices = new Indices(cursor); + } + notifyDataSetChanged(); + } + + public ParcelableAccount getSelectedAccount() { + final Cursor c = mCursor; + final Indices i = mIndices; + if (c != null && i != null && c.moveToFirst()) { + while (!c.isAfterLast()) { + if (c.getLong(mIndices.account_id) == mSelectedAccountId) + return new ParcelableAccount(c, mIndices); + c.moveToNext(); + } + } + return null; + } + + public long getSelectedAccountId() { + return mSelectedAccountId; + } + + public void setSelectedAccountId(long accountId) { + mSelectedAccountId = accountId; + notifyDataSetChanged(); + } + @Override public AccountProfileImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { final View view = mInflater.inflate(R.layout.adapter_item_compose_account, parent, false); @@ -446,50 +515,15 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement return Math.max(mCursor.getCount() - 1, 0); } - public void changeCursor(Cursor cursor) { - mCursor = cursor; - if (cursor != null) { - mIndices = new Indices(cursor); - } - notifyDataSetChanged(); - } - - public void setSelectedAccountId(long accountId) { - mSelectedAccountId = accountId; - notifyDataSetChanged(); - } - - public Account getSelectedAccount() { - final Cursor c = mCursor; - final Indices i = mIndices; - if (c != null && i != null && c.moveToFirst()) { - while (!c.isAfterLast()) { - if (c.getLong(mIndices.account_id) == mSelectedAccountId) - return new Account(c, mIndices); - c.moveToNext(); - } - } - return null; - } - - public long getSelectedAccountId() { - return mSelectedAccountId; - } - private void dispatchItemSelected(int position) { final Cursor c = mCursor; c.moveToPosition(position); if (c.getLong(mIndices.account_id) != mSelectedAccountId || c.moveToNext()) { - mFragment.onAccountSelected(new Account(c, mIndices)); + mFragment.onAccountSelected(new ParcelableAccount(c, mIndices)); } } } - private void onAccountSelected(Account account) { - mAccountsAdapter.setSelectedAccountId(account.account_id); - updateAccountOptionsSeparatorLabel(); - } - private static class OptionItem { private final int name, icon, id; diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsManagerFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsManagerFragment.java index e6818b0cb..ff9b89140 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsManagerFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsManagerFragment.java @@ -35,12 +35,12 @@ import android.widget.ListView; import com.mobeta.android.dslv.DragSortListView; import com.mobeta.android.dslv.DragSortListView.DropListener; -import org.mariotaku.querybuilder.Where; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity; import org.mariotaku.twidere.activity.support.SignInActivity; import org.mariotaku.twidere.adapter.AccountsAdapter; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.provider.TweetStore.Accounts; import org.mariotaku.twidere.provider.TweetStore.DirectMessages; import org.mariotaku.twidere.provider.TweetStore.DirectMessages.Inbox; @@ -61,7 +61,7 @@ public class AccountsManagerFragment extends BaseSupportListFragment implements private AccountsAdapter mAdapter; private SharedPreferences mPreferences; - private Account mSelectedAccount; + private ParcelableAccount mSelectedAccount; @Override public boolean onOptionsItemSelected(MenuItem item) { @@ -178,7 +178,7 @@ public class AccountsManagerFragment extends BaseSupportListFragment implements public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { if (!(menuInfo instanceof AdapterContextMenuInfo)) return; final AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - final Account account = mAdapter.getAccount(info.position); + final ParcelableAccount account = mAdapter.getAccount(info.position); menu.setHeaderTitle(account.name); final MenuInflater inflater = new MenuInflater(v.getContext()); inflater.inflate(R.menu.action_manager_account, menu); @@ -258,7 +258,7 @@ public class AccountsManagerFragment extends BaseSupportListFragment implements final long id = c.getLong(idIdx); final ContentValues values = new ContentValues(); values.put(Accounts.SORT_POSITION, i); - final Where where = Where.equals(Accounts._ID, id); + final Expression where = Expression.equals(Accounts._ID, id); cr.update(Accounts.CONTENT_URI, values, where.getSQL(), null); } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BasePullToRefreshStaggeredGridFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BasePullToRefreshStaggeredGridFragment.java deleted file mode 100644 index ade095f4a..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BasePullToRefreshStaggeredGridFragment.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 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.fragment.support; - -import android.content.Context; -import android.os.Bundle; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.TextView; - -import org.mariotaku.refreshnow.widget.OnRefreshListener; -import org.mariotaku.refreshnow.widget.RefreshMode; -import org.mariotaku.refreshnow.widget.RefreshNowConfig; -import org.mariotaku.refreshnow.widget.RefreshNowProgressIndicator; -import org.mariotaku.refreshnow.widget.RefreshNowProgressIndicator.IndicatorConfig; -import org.mariotaku.twidere.R; -import org.mariotaku.twidere.fragment.iface.IBasePullToRefreshFragment; -import org.mariotaku.twidere.util.ThemeUtils; -import org.mariotaku.twidere.view.RefreshNowStaggeredGridView; - -public abstract class BasePullToRefreshStaggeredGridFragment extends BaseSupportStaggeredGridFragment implements - IBasePullToRefreshFragment, OnTouchListener { - - @Override - public RefreshNowStaggeredGridView getListView() { - return (RefreshNowStaggeredGridView) super.getListView(); - } - - @Override - public RefreshMode getRefreshMode() { - if (getView() == null) return RefreshMode.NONE; - return getListView().getRefreshMode(); - } - - @Override - public boolean isRefreshing() { - if (getView() == null) return false; - return getListView().isRefreshing(); - } - - /** - * Provide default implementation to return a simple list view. Subclasses - * can override to replace with their own layout. If doing so, the returned - * view hierarchy must have a ListView whose id is - * {@link android.R.id#list android.R.id.list} and can optionally have a - * sibling view id {@link android.R.id#empty android.R.id.empty} that is to - * be shown when the list is empty. - *

- * If you are overriding this method with your own custom content, consider - * including the standard layout {@link android.R.layout#list_content} in - * your layout file, so that you continue to retain all of the standard - * behavior of ListFragment. In particular, this is currently the only way - * to have the built-in indeterminant progress state be shown. - */ - @Override - public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { - final Context context = getActivity(); - - final FrameLayout root = new FrameLayout(context); - - // ------------------------------------------------------------------ - - final LinearLayout pframe = new LinearLayout(context); - pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID); - pframe.setOrientation(LinearLayout.VERTICAL); - pframe.setVisibility(View.GONE); - pframe.setGravity(Gravity.CENTER); - - final ProgressBar progress = new ProgressBar(context, null, android.R.attr.progressBarStyleLarge); - pframe.addView(progress, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - - root.addView(pframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - // ------------------------------------------------------------------ - - final FrameLayout lframe = new FrameLayout(context); - lframe.setId(INTERNAL_LIST_CONTAINER_ID); - - final TextView tv = new TextView(getActivity()); - tv.setId(INTERNAL_EMPTY_ID); - tv.setGravity(Gravity.CENTER); - lframe.addView(tv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - final RefreshNowStaggeredGridView lv = (RefreshNowStaggeredGridView) inflater.inflate( - R.layout.refreshnow_staggered_gridview, lframe, false); - lv.setId(android.R.id.list); - lv.setDrawSelectorOnTop(false); - lv.setOnRefreshListener(this); - lv.setOnTouchListener(this); - lframe.addView(lv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - final RefreshNowProgressIndicator indicator = new RefreshNowProgressIndicator(context); - final IndicatorConfig config = ThemeUtils.buildRefreshIndicatorConfig(context); - indicator.setConfig(config); - final int indicatorHeight = Math.round(3 * getResources().getDisplayMetrics().density); - lframe.addView(indicator, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, indicatorHeight, - Gravity.TOP)); - - lv.setRefreshIndicatorView(indicator); - - root.addView(lframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - // ------------------------------------------------------------------ - - root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - return root; - } - - @Override - public void onRefreshComplete() { - - } - - @Override - public final void onRefreshStart(final RefreshMode mode) { - if (mode.hasStart()) { - onRefreshFromStart(); - } else if (mode.hasEnd()) { - onRefreshFromEnd(); - } - } - - @Override - public final boolean onTouch(final View v, final MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: { - onListTouched(); - break; - } - } - return false; - } - - @Override - public void setConfig(final RefreshNowConfig config) { - if (getView() == null) return; - getListView().setConfig(config); - } - - @Override - public void setOnRefreshListener(final OnRefreshListener listener) { - - } - - @Override - public void setRefreshComplete() { - if (getView() == null) return; - getListView().setRefreshComplete(); - } - - @Override - public void setRefreshIndicatorView(final View view) { - if (getView() == null) return; - getListView().setRefreshIndicatorView(view); - } - - @Override - public void setRefreshing(final boolean refresh) { - if (getView() == null) return; - getListView().setRefreshing(refresh); - } - - @Override - public void setRefreshMode(final RefreshMode mode) { - if (getView() == null) return; - getListView().setRefreshMode(mode); - } - - @Override - public boolean triggerRefresh() { - onRefreshFromStart(); - setRefreshing(true); - return true; - } - - protected void onListTouched() { - - } - -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesListFragment.java index 24f846995..a893cbe41 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesListFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesListFragment.java @@ -40,8 +40,8 @@ import android.widget.ListView; import org.mariotaku.twidere.R; import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter.MenuButtonClickListener; import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter; -import org.mariotaku.twidere.model.Account; -import org.mariotaku.twidere.model.Account.AccountWithCredentials; +import org.mariotaku.twidere.model.ParcelableAccount; +import org.mariotaku.twidere.model.ParcelableAccount.ParcelableAccountWithCredentials; import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.task.AsyncTask; import org.mariotaku.twidere.util.AsyncTaskManager; @@ -345,9 +345,9 @@ abstract class BaseStatusesListFragment extends BasePullToRefreshListFragm break; } case MENU_TRANSLATE: { - final AccountWithCredentials account = Account.getAccountWithCredentials(getActivity(), + final ParcelableAccountWithCredentials account = ParcelableAccount.getAccountWithCredentials(getActivity(), status.account_id); - if (AccountWithCredentials.isOfficialCredentials(getActivity(), account)) { + if (ParcelableAccountWithCredentials.isOfficialCredentials(getActivity(), account)) { StatusTranslateDialogFragment.show(getFragmentManager(), status); } else { diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesStaggeredGridFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesStaggeredGridFragment.java deleted file mode 100644 index 5e9fba080..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseStatusesStaggeredGridFragment.java +++ /dev/null @@ -1,578 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 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.fragment.support; - -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.support.v4.app.LoaderManager.LoaderCallbacks; -import android.support.v4.content.Loader; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MenuItem.OnMenuItemClickListener; -import android.view.View; -import android.widget.AbsListView; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemLongClickListener; -import android.widget.ListView; - -import com.etsy.android.grid.StaggeredGridView; - -import org.mariotaku.menucomponent.widget.PopupMenu; -import org.mariotaku.twidere.R; -import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter.MenuButtonClickListener; -import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter; -import org.mariotaku.twidere.model.ParcelableStatus; -import org.mariotaku.twidere.task.AsyncTask; -import org.mariotaku.twidere.util.AsyncTaskManager; -import org.mariotaku.twidere.util.AsyncTwitterWrapper; -import org.mariotaku.twidere.util.ClipboardUtils; -import org.mariotaku.twidere.util.MultiSelectManager; -import org.mariotaku.twidere.util.PositionManager; -import org.mariotaku.twidere.util.TwitterWrapper; -import org.mariotaku.twidere.util.Utils; -import org.mariotaku.twidere.util.collection.NoDuplicatesCopyOnWriteArrayList; -import org.mariotaku.twidere.view.holder.StatusListViewHolder; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.mariotaku.twidere.util.Utils.cancelRetweet; -import static org.mariotaku.twidere.util.Utils.clearListViewChoices; -import static org.mariotaku.twidere.util.Utils.configBaseCardAdapter; -import static org.mariotaku.twidere.util.Utils.isMyRetweet; -import static org.mariotaku.twidere.util.Utils.openStatus; -import static org.mariotaku.twidere.util.Utils.setMenuForStatus; -import static org.mariotaku.twidere.util.Utils.showOkMessage; -import static org.mariotaku.twidere.util.Utils.startStatusShareChooser; - -abstract class BaseStatusesStaggeredGridFragment extends BasePullToRefreshStaggeredGridFragment implements - LoaderCallbacks, OnItemLongClickListener, OnMenuItemClickListener, - MultiSelectManager.Callback, MenuButtonClickListener { - - private AsyncTaskManager mAsyncTaskManager; - private SharedPreferences mPreferences; - - private StaggeredGridView mListView; - private IStatusesListAdapter mAdapter; - private PopupMenu mPopupMenu; - - private Data mData; - private ParcelableStatus mSelectedStatus; - - private boolean mLoadMoreAutomatically; - private int mListScrollOffset; - - private MultiSelectManager mMultiSelectManager; - private PositionManager mPositionManager; - - private int mFirstVisibleItem; - private int mSelectedPosition; - - private final Map> mUnreadCountsToRemove = Collections - .synchronizedMap(new HashMap>()); - private final List mReadPositions = new NoDuplicatesCopyOnWriteArrayList(); - - private RemoveUnreadCountsTask mRemoveUnreadCountsTask; - - public AsyncTaskManager getAsyncTaskManager() { - return mAsyncTaskManager; - } - - public final Data getData() { - return mData; - } - - @Override - public IStatusesListAdapter getListAdapter() { - return mAdapter; - } - - public ParcelableStatus getSelectedStatus() { - return mSelectedStatus; - } - - public SharedPreferences getSharedPreferences() { - return mPreferences; - } - - public abstract int getStatuses(long[] account_ids, long[] max_ids, long[] since_ids); - - public final Map> getUnreadCountsToRemove() { - return mUnreadCountsToRemove; - } - - @Override - public void onActivityCreated(final Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - mAsyncTaskManager = getAsyncTaskManager(); - mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); - mPositionManager = new PositionManager(getActivity()); - mMultiSelectManager = getMultiSelectManager(); - mListView = getListView(); - mAdapter = newAdapterInstance(); - mAdapter.setMenuButtonClickListener(this); - setListAdapter(null); - setListHeaderFooters(mListView); - setListAdapter(mAdapter); - mListView.setSelector(android.R.color.transparent); - mListView.setOnItemLongClickListener(this); - setListShown(false); - getLoaderManager().initLoader(0, getArguments(), this); - } - - @Override - public abstract Loader onCreateLoader(int id, Bundle args); - - @Override - public boolean onItemLongClick(final AdapterView parent, final View view, final int position, final long id) { - final Object tag = view.getTag(); - if (tag instanceof StatusListViewHolder) { - final StatusListViewHolder holder = (StatusListViewHolder) tag; - final ParcelableStatus status = mAdapter.getStatus(position - mListView.getHeaderViewsCount()); - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (twitter != null) { - TwitterWrapper.removeUnreadCounts(getActivity(), getTabPosition(), status.account_id, status.id); - } - if (holder.show_as_gap) return false; - if (mPreferences.getBoolean(KEY_LONG_CLICK_TO_OPEN_MENU, false)) { - openMenu(holder.content.getFakeOverflowButton(), status, position); - } else { - setItemSelected(status, position, !mMultiSelectManager.isSelected(status)); - } - return true; - } - return false; - } - - @Override - public void onItemsCleared() { - clearListViewChoices(mListView); - } - - @Override - public void onItemSelected(final Object item) { - mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - } - - @Override - public void onItemUnselected(final Object item) { - } - - @Override - public void onListItemClick(final StaggeredGridView l, final View v, final int position, final long id) { - final Object tag = v.getTag(); - if (tag instanceof StatusListViewHolder) { - final int pos = position - l.getHeaderViewsCount(); - final ParcelableStatus status = mAdapter.getStatus(pos); - if (status == null) return; - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (twitter != null) { - TwitterWrapper.removeUnreadCounts(getActivity(), getTabPosition(), status.account_id, status.id); - } - if (((StatusListViewHolder) tag).show_as_gap) { - final long since_id = position + 1 < mAdapter.getStatusCount() ? mAdapter.getStatus(pos + 1).id : -1; - getStatuses(new long[]{status.account_id}, new long[]{status.id}, new long[]{since_id}); - mListView.setItemChecked(position, false); - } else { - if (mMultiSelectManager.isActive()) { - setItemSelected(status, position, !mMultiSelectManager.isSelected(status)); - return; - } - openStatus(getActivity(), status, null); - } - } - } - - @Override - public final void onLoaderReset(final Loader loader) { - mAdapter.setData(mData = null); - } - - @Override - public final void onLoadFinished(final Loader loader, final Data data) { - if (getActivity() == null || getView() == null) return; - setListShown(true); - setRefreshComplete(); - setProgressBarIndeterminateVisibility(false); - setData(data); - mFirstVisibleItem = -1; - mReadPositions.clear(); - final int listVisiblePosition, savedChildIndex; - final boolean rememberPosition = mPreferences.getBoolean(KEY_REMEMBER_POSITION, true); - if (rememberPosition) { - listVisiblePosition = mListView.getLastVisiblePosition(); - final int childCount = mListView.getChildCount(); - savedChildIndex = childCount - 1; - if (childCount > 0) { - final View lastChild = mListView.getChildAt(savedChildIndex); - mListScrollOffset = lastChild != null ? lastChild.getTop() : 0; - } - } else { - listVisiblePosition = mListView.getFirstVisiblePosition(); - savedChildIndex = 0; - if (mListView.getChildCount() > 0) { - final View firstChild = mListView.getChildAt(savedChildIndex); - mListScrollOffset = firstChild != null ? firstChild.getTop() : 0; - } - } - final long lastViewedId = mAdapter.getStatusId(listVisiblePosition); - mAdapter.setData(data); - mAdapter.setShowAccountColor(shouldShowAccountColor()); - final int currFirstVisiblePosition = mListView.getFirstVisiblePosition(); - final long currViewedId = mAdapter.getStatusId(currFirstVisiblePosition); - final long statusId; - if (lastViewedId <= 0) { - if (!rememberPosition) return; - statusId = mPositionManager.getPosition(getPositionKey()); - } else if ((listVisiblePosition > 0 || rememberPosition) && currViewedId > 0 && lastViewedId != currViewedId) { - statusId = lastViewedId; - } else { - if (listVisiblePosition == 0 && mAdapter.getStatusId(0) != lastViewedId) { - mAdapter.setMaxAnimationPosition(mListView.getLastVisiblePosition()); - } - return; - } - final int position = mAdapter.findPositionByStatusId(statusId); - if (position > -1 && position < mListView.getCount()) { - mAdapter.setMaxAnimationPosition(mListView.getLastVisiblePosition()); - // mListView.setSelectionFromTop(position, mListScrollOffset); - mListView.setSelection(position); - mListScrollOffset = 0; - } - } - - @Override - public void onMenuButtonClick(final View button, final int position, final long id) { - if (mMultiSelectManager.isActive()) return; - final ParcelableStatus status = mAdapter.getStatus(position); - if (status == null) return; - openMenu(button, status, position); - } - - @Override - public final boolean onMenuItemClick(final MenuItem item) { - final ParcelableStatus status = mSelectedStatus; - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (status == null || twitter == null) return false; - switch (item.getItemId()) { - case MENU_VIEW: { - openStatus(getActivity(), status, null); - break; - } - case MENU_SHARE: { - startStatusShareChooser(getActivity(), status); - break; - } - case MENU_COPY: { - if (ClipboardUtils.setText(getActivity(), status.text_plain)) { - showOkMessage(getActivity(), R.string.text_copied, false); - } - break; - } - case MENU_RETWEET: { - if (isMyRetweet(status)) { - cancelRetweet(twitter, status); - } else { - final long id_to_retweet = status.retweet_id > 0 ? status.retweet_id : status.id; - twitter.retweetStatus(status.account_id, id_to_retweet); - } - break; - } - case MENU_QUOTE: { - final Intent intent = new Intent(INTENT_ACTION_QUOTE); - final Bundle bundle = new Bundle(); - bundle.putParcelable(EXTRA_STATUS, status); - intent.putExtras(bundle); - startActivity(intent); - break; - } - case MENU_REPLY: { - final Intent intent = new Intent(INTENT_ACTION_REPLY); - final Bundle bundle = new Bundle(); - bundle.putParcelable(EXTRA_STATUS, status); - intent.putExtras(bundle); - startActivity(intent); - break; - } - case MENU_FAVORITE: { - if (status.is_favorite) { - twitter.destroyFavoriteAsync(status.account_id, status.id); - } else { - twitter.createFavoriteAsync(status.account_id, status.id); - } - break; - } - case MENU_DELETE: { - twitter.destroyStatusAsync(status.account_id, status.id); - break; - } - case MENU_ADD_TO_FILTER: { - AddStatusFilterDialogFragment.show(getFragmentManager(), status); - break; - } - case MENU_MULTI_SELECT: { - final boolean isSelected = !mMultiSelectManager.isSelected(status); - setItemSelected(status, mSelectedPosition, isSelected); - break; - } - default: { - if (item.getIntent() != null) { - try { - startActivity(item.getIntent()); - } catch (final ActivityNotFoundException e) { - Log.w(LOGTAG, e); - return false; - } - } - break; - } - } - return true; - } - - @Override - public void onRefreshFromEnd() { - if (mLoadMoreAutomatically) return; - loadMoreStatuses(); - } - - @Override - public void onResume() { - super.onResume(); - mListView.setFastScrollEnabled(mPreferences.getBoolean(KEY_FAST_SCROLL_THUMB, false)); - configBaseCardAdapter(getActivity(), mAdapter); - final boolean display_image_preview = mPreferences.getBoolean(KEY_DISPLAY_IMAGE_PREVIEW, false); - final boolean display_sensitive_contents = mPreferences.getBoolean(KEY_DISPLAY_SENSITIVE_CONTENTS, false); - final boolean indicate_my_status = mPreferences.getBoolean(KEY_INDICATE_MY_STATUS, true); - mAdapter.setDisplayImagePreview(display_image_preview); - mAdapter.setDisplaySensitiveContents(display_sensitive_contents); - mAdapter.setIndicateMyStatusDisabled(isMyTimeline() || !indicate_my_status); - mLoadMoreAutomatically = mPreferences.getBoolean(KEY_LOAD_MORE_AUTOMATICALLY, false); - } - - @Override - public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, - final int totalItemCount) { - super.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); - addReadPosition(firstVisibleItem); - } - - @Override - public void onScrollStateChanged(final AbsListView view, final int scrollState) { - super.onScrollStateChanged(view, scrollState); - switch (scrollState) { - case SCROLL_STATE_IDLE: - for (int i = mListView.getFirstVisiblePosition(), j = mListView.getLastVisiblePosition(); i < j; i++) { - mReadPositions.add(i); - } - removeUnreadCounts(); - break; - default: - break; - } - } - - @Override - public void onStart() { - super.onStart(); - mMultiSelectManager.registerCallback(this); - final int choiceMode = mListView.getChoiceMode(); - if (mMultiSelectManager.isActive()) { - if (choiceMode != ListView.CHOICE_MODE_MULTIPLE) { - mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - } - } else { - if (choiceMode != ListView.CHOICE_MODE_NONE) { - Utils.clearListViewChoices(mListView); - } - } - } - - @Override - public void onStop() { - savePosition(); - mMultiSelectManager.unregisterCallback(this); - if (mPopupMenu != null) { - mPopupMenu.dismiss(); - } - super.onStop(); - } - - @Override - public boolean scrollToStart() { - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - final int tab_position = getTabPosition(); - if (twitter != null && tab_position >= 0) { - twitter.clearUnreadCountAsync(tab_position); - } - return super.scrollToStart(); - } - - @Override - public void setUserVisibleHint(final boolean isVisibleToUser) { - super.setUserVisibleHint(isVisibleToUser); - updateRefreshState(); - } - - protected final int getListScrollOffset() { - return mListScrollOffset; - } - - protected abstract long[] getNewestStatusIds(); - - protected abstract long[] getOldestStatusIds(); - - protected abstract String getPositionKey(); - - protected boolean isMyTimeline() { - return false; - } - - protected abstract void loadMoreStatuses(); - - protected abstract IStatusesListAdapter newAdapterInstance(); - - @Override - protected void onReachedBottom() { - if (!mLoadMoreAutomatically) return; - loadMoreStatuses(); - } - - protected void savePosition() { - final int first_visible_position = mListView.getFirstVisiblePosition(); - if (mListView.getChildCount() > 0) { - final View first_child = mListView.getChildAt(0); - mListScrollOffset = first_child != null ? first_child.getTop() : 0; - } - final long status_id = mAdapter.getStatusId(first_visible_position); - mPositionManager.setPosition(getPositionKey(), status_id); - } - - protected final void setData(final Data data) { - mData = data; - } - - protected void setItemSelected(final ParcelableStatus status, final int position, final boolean selected) { - if (selected) { - mMultiSelectManager.selectItem(status); - } else { - mMultiSelectManager.unselectItem(status); - } - if (position >= 0) { - mListView.setItemChecked(position, selected); - } - } - - protected void setListHeaderFooters(final StaggeredGridView list) { - - } - - protected boolean shouldEnablePullToRefresh() { - return true; - } - - protected abstract boolean shouldShowAccountColor(); - - protected abstract void updateRefreshState(); - - private void addReadPosition(final int firstVisibleItem) { - if (mFirstVisibleItem != firstVisibleItem) { - mReadPositions.add(firstVisibleItem); - } - mFirstVisibleItem = firstVisibleItem; - } - - private void addUnreadCountsToRemove(final long account_id, final long id) { - if (mUnreadCountsToRemove.containsKey(account_id)) { - final Set counts = mUnreadCountsToRemove.get(account_id); - counts.add(id); - } else { - final Set counts = new HashSet(); - counts.add(id); - mUnreadCountsToRemove.put(account_id, counts); - } - } - - private void openMenu(final View view, final ParcelableStatus status, final int position) { - mSelectedStatus = status; - mSelectedPosition = position; - if (view == null || status == null) return; - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (twitter != null) { - TwitterWrapper.removeUnreadCounts(getActivity(), getTabPosition(), status.account_id, status.id); - } - if (mPopupMenu != null && mPopupMenu.isShowing()) { - mPopupMenu.dismiss(); - } - mPopupMenu = PopupMenu.getInstance(getActivity(), view); - mPopupMenu.inflate(R.menu.action_status); - final boolean longclickToOpenMenu = mPreferences.getBoolean(KEY_LONG_CLICK_TO_OPEN_MENU, false); - final Menu menu = mPopupMenu.getMenu(); - setMenuForStatus(getActivity(), menu, status); - Utils.setMenuItemAvailability(menu, MENU_MULTI_SELECT, longclickToOpenMenu); - mPopupMenu.setOnMenuItemClickListener(this); - mPopupMenu.show(); - } - - private void removeUnreadCounts() { - if (mRemoveUnreadCountsTask != null && mRemoveUnreadCountsTask.getStatus() == AsyncTask.Status.RUNNING) - return; - mRemoveUnreadCountsTask = new RemoveUnreadCountsTask(mReadPositions, this); - mRemoveUnreadCountsTask.execute(); - } - - static class RemoveUnreadCountsTask extends AsyncTask { - private final List read_positions; - private final IStatusesListAdapter adapter; - private final BaseStatusesStaggeredGridFragment fragment; - - RemoveUnreadCountsTask(final List read_positions, final BaseStatusesStaggeredGridFragment fragment) { - this.read_positions = read_positions; - this.fragment = fragment; - this.adapter = fragment.getListAdapter(); - } - - @Override - protected Void doInBackground(final Void... params) { - for (final int pos : read_positions) { - final long id = adapter.getStatusId(pos), account_id = adapter.getAccountId(pos); - fragment.addUnreadCountsToRemove(account_id, id); - } - return null; - } - - @Override - protected void onPostExecute(final Void result) { - final AsyncTwitterWrapper twitter = fragment.getTwitterWrapper(); - if (twitter != null) { - twitter.removeUnreadCountsAsync(fragment.getTabPosition(), fragment.getUnreadCountsToRemove()); - } - } - - } -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportFragment.java index 7d39013fd..e5f05fb2b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportFragment.java @@ -114,7 +114,8 @@ public class BaseSupportFragment extends Fragment implements IBaseFragment, Cons @Override public int getTabPosition() { - return 0; + final Bundle args = getArguments(); + return args != null ? args.getInt(EXTRA_TAB_POSITION, -1) : -1; } @Override diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportStaggeredGridFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportStaggeredGridFragment.java deleted file mode 100644 index 31bdaebeb..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportStaggeredGridFragment.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 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.fragment.support; - -import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.graphics.Rect; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; - -import com.etsy.android.grid.StaggeredGridView; - -import org.mariotaku.twidere.Constants; -import org.mariotaku.twidere.activity.support.HomeActivity; -import org.mariotaku.twidere.app.TwidereApplication; -import org.mariotaku.twidere.fragment.iface.IBaseFragment; -import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface; -import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback; -import org.mariotaku.twidere.util.AsyncTwitterWrapper; -import org.mariotaku.twidere.util.MultiSelectManager; -import org.mariotaku.twidere.util.Utils; - -public class BaseSupportStaggeredGridFragment extends StaggeredGridFragment implements IBaseFragment, Constants, - OnScrollListener, RefreshScrollTopInterface { - - private boolean mActivityFirstCreated; - private boolean mIsInstanceStateSaved; - private boolean mReachedBottom, mNotReachedBottomBefore = true; - - @Override - public void requestFitSystemWindows() { - final Activity activity = getActivity(); - final Fragment parentFragment = getParentFragment(); - final SystemWindowsInsetsCallback callback; - if (parentFragment instanceof SystemWindowsInsetsCallback) { - callback = (SystemWindowsInsetsCallback) parentFragment; - } else if (activity instanceof SystemWindowsInsetsCallback) { - callback = (SystemWindowsInsetsCallback) activity; - } else { - return; - } - final Rect insets = new Rect(); - if (callback.getSystemWindowsInsets(insets)) { - fitSystemWindows(insets); - } - } - - protected void fitSystemWindows(Rect insets) { - - } - - public final TwidereApplication getApplication() { - return TwidereApplication.getInstance(getActivity()); - } - - public final ContentResolver getContentResolver() { - final Activity activity = getActivity(); - if (activity != null) return activity.getContentResolver(); - return null; - } - - @Override - public Bundle getExtraConfiguration() { - final Bundle args = getArguments(); - final Bundle extras = new Bundle(); - if (args != null && args.containsKey(EXTRA_EXTRAS)) { - extras.putAll(args.getBundle(EXTRA_EXTRAS)); - } - return extras; - } - - public final MultiSelectManager getMultiSelectManager() { - return getApplication() != null ? getApplication().getMultiSelectManager() : null; - } - - public final SharedPreferences getSharedPreferences(final String name, final int mode) { - final Activity activity = getActivity(); - if (activity != null) return activity.getSharedPreferences(name, mode); - return null; - } - - public final Object getSystemService(final String name) { - final Activity activity = getActivity(); - if (activity != null) return activity.getSystemService(name); - return null; - } - - @Override - public final int getTabPosition() { - final Bundle args = getArguments(); - return args != null ? args.getInt(EXTRA_TAB_POSITION, -1) : -1; - } - - public AsyncTwitterWrapper getTwitterWrapper() { - return getApplication() != null ? getApplication().getTwitterWrapper() : null; - } - - public void invalidateOptionsMenu() { - final Activity activity = getActivity(); - if (activity == null) return; - activity.invalidateOptionsMenu(); - } - - public boolean isActivityFirstCreated() { - return mActivityFirstCreated; - } - - public boolean isInstanceStateSaved() { - return mIsInstanceStateSaved; - } - - public boolean isReachedBottom() { - return mReachedBottom; - } - - @Override - public void onActivityCreated(final Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - mIsInstanceStateSaved = savedInstanceState != null; - final StaggeredGridView lv = getListView(); - lv.setOnScrollListener(this); - } - - @Override - public void onAttach(final Activity activity) { - super.onAttach(activity); - } - - @Override - public void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mActivityFirstCreated = true; - } - - @Override - public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { - return super.onCreateView(inflater, container, savedInstanceState); - } - - @Override - public void onDestroy() { - super.onDestroy(); - mActivityFirstCreated = true; - } - - @Override - public void onDetach() { - super.onDetach(); - final Fragment fragment = getParentFragment(); - if (fragment instanceof SupportFragmentCallback) { - ((SupportFragmentCallback) fragment).onDetachFragment(this); - } - final Activity activity = getActivity(); - if (activity instanceof SupportFragmentCallback) { - ((SupportFragmentCallback) activity).onDetachFragment(this); - } - } - - public void onPostStart() { - } - - @Override - public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, - final int totalItemCount) { - final boolean reached = firstVisibleItem + visibleItemCount >= totalItemCount - && totalItemCount >= visibleItemCount; - - if (mReachedBottom != reached) { - mReachedBottom = reached; - if (mReachedBottom && mNotReachedBottomBefore) { - mNotReachedBottomBefore = false; - return; - } - if (mReachedBottom && getListAdapter().getCount() > visibleItemCount) { - onReachedBottom(); - } - } - - } - - @Override - public void onScrollStateChanged(final AbsListView view, final int scrollState) { - - } - - @Override - public void onStart() { - super.onStart(); - onPostStart(); - } - - @Override - public void onStop() { - mActivityFirstCreated = false; - super.onStop(); - } - - public void registerReceiver(final BroadcastReceiver receiver, final IntentFilter filter) { - final Activity activity = getActivity(); - if (activity == null) return; - activity.registerReceiver(receiver, filter); - } - - @Override - public boolean scrollToStart() { - if (!isAdded() || getActivity() == null) return false; - Utils.scrollListToTop(getListView()); - return true; - } - - public void setProgressBarIndeterminateVisibility(final boolean visible) { - final Activity activity = getActivity(); - if (activity == null) return; - activity.setProgressBarIndeterminateVisibility(visible); - if (activity instanceof HomeActivity) { - ((HomeActivity) activity).setHomeProgressBarIndeterminateVisibility(visible); - } - } - - @Override - public void setSelection(final int position) { - Utils.scrollListToPosition(getListView(), position); - } - - @Override - public void setUserVisibleHint(final boolean isVisibleToUser) { - super.setUserVisibleHint(isVisibleToUser); - final Activity activity = getActivity(); - final Fragment fragment = getParentFragment(); - if (fragment instanceof SupportFragmentCallback) { - ((SupportFragmentCallback) fragment).onSetUserVisibleHint(this, isVisibleToUser); - } - if (activity instanceof SupportFragmentCallback) { - ((SupportFragmentCallback) activity).onSetUserVisibleHint(this, isVisibleToUser); - } - } - - @Override - public boolean triggerRefresh() { - return false; - } - - public void unregisterReceiver(final BroadcastReceiver receiver) { - final Activity activity = getActivity(); - if (activity == null) return; - activity.unregisterReceiver(receiver); - } - - protected void onReachedBottom() { - - } -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesFragment.java index bbfb7fad6..36bd80190 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesFragment.java @@ -20,18 +20,134 @@ package org.mariotaku.twidere.fragment.support; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Expression; +import org.mariotaku.querybuilder.RawItemArray; import org.mariotaku.twidere.adapter.CursorStatusesAdapter; +import org.mariotaku.twidere.provider.TweetStore.Statuses; +import org.mariotaku.twidere.task.AsyncTask; +import org.mariotaku.twidere.util.Utils; + +import static org.mariotaku.twidere.util.Utils.buildStatusFilterWhereClause; +import static org.mariotaku.twidere.util.Utils.getNewestStatusIdsFromDatabase; +import static org.mariotaku.twidere.util.Utils.getOldestStatusIdsFromDatabase; +import static org.mariotaku.twidere.util.Utils.getTableNameByUri; +import static org.mariotaku.twidere.util.Utils.shouldEnableFiltersForRTs; /** * Created by mariotaku on 14/12/3. */ public abstract class CursorStatusesFragment extends AbsStatusesFragment { + + public abstract Uri getContentUri(); + + @Override + public Loader onCreateLoader(int id, Bundle args) { + final Context context = getActivity(); + final Uri uri = getContentUri(); + final String table = getTableNameByUri(uri); + final String sortOrder = getSortOrder(); + final long[] accountIds = getAccountIds(); + final Expression accountWhere = Expression.in(new Column(Statuses.ACCOUNT_ID), new RawItemArray(accountIds)); + final Expression filterWhere = getFiltersWhere(table), where; + if (filterWhere != null) { + where = Expression.and(accountWhere, filterWhere); + } else { + where = accountWhere; + } + final String selection = processWhere(where).getSQL(); + return new CursorLoader(context, uri, Statuses.COLUMNS, selection, null, sortOrder); + } + + @Override + public boolean triggerRefresh() { + new AsyncTask() { + + @Override + protected long[][] doInBackground(final Void... params) { + final long[][] result = new long[3][]; + result[0] = getAccountIds(); + result[2] = getNewestStatusIds(result[0]); + return result; + } + + @Override + protected void onPostExecute(final long[][] result) { + getStatuses(result[0], result[1], result[2]); + } + + }.execute(); + return true; + } + + @Override + protected long[] getAccountIds() { + final Bundle args = getArguments(); + if (args != null && args.getLong(EXTRA_ACCOUNT_ID) > 0) { + return new long[]{args.getLong(EXTRA_ACCOUNT_ID)}; + } + return Utils.getActivatedAccountIds(getActivity()); + } + + protected abstract int getNotificationType(); + @Override protected CursorStatusesAdapter onCreateAdapter(final Context context, final boolean compact) { return new CursorStatusesAdapter(context, compact); } + @Override + protected void onLoadMoreStatuses() { + new AsyncTask() { + + @Override + protected long[][] doInBackground(final Void... params) { + final long[][] result = new long[3][]; + result[0] = getAccountIds(); + result[1] = getOldestStatusIds(result[0]); + return result; + } + + @Override + protected void onPostExecute(final long[][] result) { + getStatuses(result[0], result[1], result[2]); + } + + }.execute(); + } + + protected Expression getFiltersWhere(String table) { + if (!isFilterEnabled()) return null; + return buildStatusFilterWhereClause(table, null, shouldEnableFiltersForRTs(getActivity())); + } + + protected abstract boolean isFilterEnabled(); + + protected long[] getNewestStatusIds(long[] accountIds) { + return getNewestStatusIdsFromDatabase(getActivity(), getContentUri(), accountIds); + } + + protected long[] getOldestStatusIds(long[] accountIds) { + return getOldestStatusIdsFromDatabase(getActivity(), getContentUri(), accountIds); + } + + protected Expression processWhere(final Expression where) { + return where; + } + + private String getSortOrder() { + final SharedPreferences preferences = getSharedPreferences(); + final boolean sortById = preferences.getBoolean(KEY_SORT_TIMELINE_BY_ID, false); + return sortById ? Statuses.SORT_ORDER_STATUS_ID_DESC : Statuses.SORT_ORDER_TIMESTAMP_DESC; + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesListFragment.java deleted file mode 100644 index 3a033d600..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesListFragment.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 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.fragment.support; - -import android.app.Activity; -import android.content.ContentResolver; -import android.content.Context; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.widget.AbsListView; - -import org.mariotaku.querybuilder.Columns.Column; -import org.mariotaku.querybuilder.RawItemArray; -import org.mariotaku.querybuilder.Where; -import org.mariotaku.twidere.R; -import org.mariotaku.twidere.activity.support.HomeActivity; -import org.mariotaku.twidere.adapter.CursorStatusesListAdapter; -import org.mariotaku.twidere.provider.TweetStore.Accounts; -import org.mariotaku.twidere.provider.TweetStore.Filters; -import org.mariotaku.twidere.provider.TweetStore.Statuses; -import org.mariotaku.twidere.task.AsyncTask; -import org.mariotaku.twidere.util.AsyncTwitterWrapper; -import org.mariotaku.twidere.util.content.SupportFragmentReloadCursorObserver; - -import static org.mariotaku.twidere.util.Utils.buildStatusFilterWhereClause; -import static org.mariotaku.twidere.util.Utils.getActivatedAccountIds; -import static org.mariotaku.twidere.util.Utils.getNewestStatusIdsFromDatabase; -import static org.mariotaku.twidere.util.Utils.getOldestStatusIdsFromDatabase; -import static org.mariotaku.twidere.util.Utils.getTableNameByUri; -import static org.mariotaku.twidere.util.Utils.shouldEnableFiltersForRTs; - -public abstract class CursorStatusesListFragment extends BaseStatusesListFragment { - - private final SupportFragmentReloadCursorObserver mReloadContentObserver = new SupportFragmentReloadCursorObserver( - this, 0, this); - - public HomeActivity getHomeActivity() { - final Activity activity = getActivity(); - if (activity instanceof HomeActivity) return (HomeActivity) activity; - return null; - } - - @Override - public void onActivityCreated(final Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - getListAdapter().setFiltersEnabled(isFiltersEnabled()); - } - - @Override - public Loader onCreateLoader(final int id, final Bundle args) { - final Context context = getActivity(); - final SharedPreferences preferences = getSharedPreferences(); - final boolean sortById = preferences.getBoolean(KEY_SORT_TIMELINE_BY_ID, false); - final Uri uri = getContentUri(); - final String table = getTableNameByUri(uri); - final String sortOrder = sortById ? Statuses.SORT_ORDER_STATUS_ID_DESC : Statuses.SORT_ORDER_TIMESTAMP_DESC; - final long accountId = getAccountId(); - final long[] accountIds = accountId > 0 ? new long[]{accountId} : getActivatedAccountIds(context); - final boolean noAccountSelected = accountIds.length == 0; - setEmptyText(noAccountSelected ? getString(R.string.no_account_selected) : null); - if (!noAccountSelected) { - getListView().setEmptyView(null); - } - final Where accountWhere = Where.in(new Column(Statuses.ACCOUNT_ID), new RawItemArray(accountIds)); - final Where where; - if (isFiltersEnabled()) { - final Where filterWhere = new Where(buildStatusFilterWhereClause(table, null, - shouldEnableFiltersForRTs(context))); - where = Where.and(accountWhere, filterWhere); - } else { - where = accountWhere; - } - final String selection = processWhere(where).getSQL(); - return new CursorLoader(context, uri, CursorStatusesListAdapter.CURSOR_COLS, selection, null, sortOrder); - } - - @Override - public void onRefreshFromStart() { - if (isRefreshing()) return; - savePosition(); - new AsyncTask() { - - @Override - protected long[][] doInBackground(final Void... params) { - final long[][] result = new long[3][]; - final long account_id = getAccountId(); - result[0] = account_id > 0 ? new long[]{account_id} : getActivatedAccountIds(getActivity()); - result[2] = getNewestStatusIds(); - return result; - } - - @Override - protected void onPostExecute(final long[][] result) { - getStatuses(result[0], result[1], result[2]); - } - - }.execute(); - } - - @Override - public void onRestart() { - super.onRestart(); - getLoaderManager().restartLoader(0, getArguments(), this); - } - - @Override - public void onScrollStateChanged(final AbsListView view, final int scrollState) { - super.onScrollStateChanged(view, scrollState); - switch (scrollState) { - case SCROLL_STATE_FLING: - case SCROLL_STATE_TOUCH_SCROLL: { - break; - } - case SCROLL_STATE_IDLE: { - savePosition(); - break; - } - } - } - - @Override - public void onStart() { - super.onStart(); - final ContentResolver resolver = getContentResolver(); - resolver.registerContentObserver(Filters.CONTENT_URI, true, mReloadContentObserver); - if (getAccountId() <= 0) { - resolver.registerContentObserver(Accounts.CONTENT_URI, true, mReloadContentObserver); - } - } - - @Override - public void onStop() { - savePosition(); - final ContentResolver resolver = getContentResolver(); - resolver.unregisterContentObserver(mReloadContentObserver); - super.onStop(); - } - - protected long getAccountId() { - final Bundle args = getArguments(); - return args != null ? args.getLong(EXTRA_ACCOUNT_ID, -1) : -1; - } - - protected abstract Uri getContentUri(); - - @Override - protected long[] getNewestStatusIds() { - final long account_id = getAccountId(); - final long[] account_ids = account_id > 0 ? new long[]{account_id} : getActivatedAccountIds(getActivity()); - return getNewestStatusIdsFromDatabase(getActivity(), getContentUri(), account_ids); - } - - protected abstract int getNotificationType(); - - @Override - protected long[] getOldestStatusIds() { - final long account_id = getAccountId(); - final long[] account_ids = account_id > 0 ? new long[]{account_id} : getActivatedAccountIds(getActivity()); - return getOldestStatusIdsFromDatabase(getActivity(), getContentUri(), account_ids); - } - - protected abstract boolean isFiltersEnabled(); - - @Override - protected void loadMoreStatuses() { - if (isRefreshing()) return; - savePosition(); - new AsyncTask() { - - @Override - protected long[][] doInBackground(final Void... params) { - final long[][] result = new long[3][]; - final long account_id = getAccountId(); - result[0] = account_id > 0 ? new long[]{account_id} : getActivatedAccountIds(getActivity()); - result[1] = getOldestStatusIds(); - return result; - } - - @Override - protected void onPostExecute(final long[][] result) { - getStatuses(result[0], result[1], result[2]); - } - - }.execute(); - } - - @Override - protected CursorStatusesListAdapter newAdapterInstance(final boolean compact) { - return new CursorStatusesListAdapter(getActivity(), compact); - } - - @Override - protected void onListTouched() { - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (twitter != null) { - twitter.clearNotificationAsync(getNotificationType(), getAccountId()); - } - } - - protected Where processWhere(final Where where) { - return where; - } - - @Override - protected boolean shouldShowAccountColor() { - return getAccountId() <= 0 && getActivatedAccountIds(getActivity()).length > 1; - } -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesStaggeredGridFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesStaggeredGridFragment.java deleted file mode 100644 index 990c887f3..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesStaggeredGridFragment.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 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.fragment.support; - -import static org.mariotaku.twidere.util.Utils.buildStatusFilterWhereClause; -import static org.mariotaku.twidere.util.Utils.getActivatedAccountIds; -import static org.mariotaku.twidere.util.Utils.getNewestStatusIdsFromDatabase; -import static org.mariotaku.twidere.util.Utils.getOldestStatusIdsFromDatabase; -import static org.mariotaku.twidere.util.Utils.getTableNameByUri; -import static org.mariotaku.twidere.util.Utils.shouldEnableFiltersForRTs; - -import android.app.Activity; -import android.content.ContentResolver; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.widget.AbsListView; - -import org.mariotaku.querybuilder.Columns.Column; -import org.mariotaku.querybuilder.RawItemArray; -import org.mariotaku.querybuilder.Where; -import org.mariotaku.twidere.R; -import org.mariotaku.twidere.activity.support.HomeActivity; -import org.mariotaku.twidere.adapter.CursorStatusesListAdapter; -import org.mariotaku.twidere.provider.TweetStore.Accounts; -import org.mariotaku.twidere.provider.TweetStore.Filters; -import org.mariotaku.twidere.provider.TweetStore.Statuses; -import org.mariotaku.twidere.task.AsyncTask; -import org.mariotaku.twidere.util.AsyncTwitterWrapper; -import org.mariotaku.twidere.util.content.SupportFragmentReloadCursorObserver; - -public abstract class CursorStatusesStaggeredGridFragment extends BaseStatusesStaggeredGridFragment { - - private final SupportFragmentReloadCursorObserver mReloadContentObserver = new SupportFragmentReloadCursorObserver( - this, 0, this); - - public HomeActivity getHomeActivity() { - final Activity activity = getActivity(); - if (activity instanceof HomeActivity) return (HomeActivity) activity; - return null; - } - - @Override - public void onActivityCreated(final Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - getListAdapter().setFiltersEnabled(isFiltersEnabled()); - } - - @Override - public Loader onCreateLoader(final int id, final Bundle args) { - final Uri uri = getContentUri(); - final String table = getTableNameByUri(uri); - final String sort_by = Statuses.DEFAULT_SORT_ORDER; - final long account_id = getAccountId(); - final long[] account_ids = account_id > 0 ? new long[] { account_id } : getActivatedAccountIds(getActivity()); - final boolean no_account_selected = account_ids.length == 0; - setEmptyText(no_account_selected ? getString(R.string.no_account_selected) : null); - if (!no_account_selected) { - getListView().setEmptyView(null); - } - final Where accountWhere = Where.in(new Column(Statuses.ACCOUNT_ID), new RawItemArray(account_ids)); - final Where where; - if (isFiltersEnabled()) { - final Where filterWhere = new Where(buildStatusFilterWhereClause(table, null, - shouldEnableFiltersForRTs(getActivity()))); - where = Where.and(accountWhere, filterWhere); - } else { - where = accountWhere; - } - return new CursorLoader(getActivity(), uri, CursorStatusesListAdapter.CURSOR_COLS, where.getSQL(), null, sort_by); - } - - @Override - public void onRefreshFromStart() { - if (isRefreshing()) return; - savePosition(); - new AsyncTask() { - - @Override - protected long[][] doInBackground(final Void... params) { - final long[][] result = new long[3][]; - final long account_id = getAccountId(); - result[0] = account_id > 0 ? new long[] { account_id } : getActivatedAccountIds(getActivity()); - result[2] = getNewestStatusIds(); - return result; - } - - @Override - protected void onPostExecute(final long[][] result) { - getStatuses(result[0], result[1], result[2]); - } - - }.execute(); - } - - @Override - public void onScrollStateChanged(final AbsListView view, final int scrollState) { - super.onScrollStateChanged(view, scrollState); - switch (scrollState) { - case SCROLL_STATE_FLING: - case SCROLL_STATE_TOUCH_SCROLL: { - break; - } - case SCROLL_STATE_IDLE: { - savePosition(); - break; - } - } - } - - @Override - public void onStart() { - super.onStart(); - final ContentResolver resolver = getContentResolver(); - resolver.registerContentObserver(Filters.CONTENT_URI, true, mReloadContentObserver); - if (getAccountId() <= 0) { - resolver.registerContentObserver(Accounts.CONTENT_URI, true, mReloadContentObserver); - } - } - - @Override - public void onStop() { - savePosition(); - final ContentResolver resolver = getContentResolver(); - resolver.unregisterContentObserver(mReloadContentObserver); - super.onStop(); - } - - protected long getAccountId() { - final Bundle args = getArguments(); - return args != null ? args.getLong(EXTRA_ACCOUNT_ID, -1) : -1; - } - - protected abstract Uri getContentUri(); - - @Override - protected long[] getNewestStatusIds() { - final long account_id = getAccountId(); - final long[] account_ids = account_id > 0 ? new long[] { account_id } : getActivatedAccountIds(getActivity()); - return getNewestStatusIdsFromDatabase(getActivity(), getContentUri(), account_ids); - } - - protected abstract int getNotificationType(); - - @Override - protected long[] getOldestStatusIds() { - final long account_id = getAccountId(); - final long[] account_ids = account_id > 0 ? new long[] { account_id } : getActivatedAccountIds(getActivity()); - return getOldestStatusIdsFromDatabase(getActivity(), getContentUri(), account_ids); - } - - protected abstract boolean isFiltersEnabled(); - - @Override - protected void loadMoreStatuses() { - if (isRefreshing()) return; - savePosition(); - new AsyncTask() { - - @Override - protected long[][] doInBackground(final Void... params) { - final long[][] result = new long[3][]; - final long account_id = getAccountId(); - result[0] = account_id > 0 ? new long[] { account_id } : getActivatedAccountIds(getActivity()); - result[1] = getOldestStatusIds(); - return result; - } - - @Override - protected void onPostExecute(final long[][] result) { - getStatuses(result[0], result[1], result[2]); - } - - }.execute(); - } - - @Override - protected CursorStatusesListAdapter newAdapterInstance() { - return new CursorStatusesListAdapter(getActivity()); - } - - @Override - protected void onListTouched() { - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (twitter != null) { - twitter.clearNotificationAsync(getNotificationType(), getAccountId()); - } - } - - @Override - protected boolean shouldShowAccountColor() { - return getAccountId() <= 0 && getActivatedAccountIds(getActivity()).length > 1; - } -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesConversationFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesConversationFragment.java index c58a68713..0ed3fa44e 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesConversationFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesConversationFragment.java @@ -64,7 +64,7 @@ import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter; import org.mariotaku.twidere.adapter.DirectMessagesConversationAdapter; import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter.MenuButtonClickListener; import org.mariotaku.twidere.app.TwidereApplication; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.ParcelableDirectMessage; import org.mariotaku.twidere.model.ParcelableUser; import org.mariotaku.twidere.provider.TweetStore; @@ -134,7 +134,7 @@ public class DirectMessagesConversationFragment extends BasePullToRefreshListFra } }; - private Account mSender; + private ParcelableAccount mSender; private ParcelableUser mRecipient; private ImageLoaderWrapper mImageLoader; private IColorLabelView mProfileImageContainer; @@ -174,7 +174,7 @@ public class DirectMessagesConversationFragment extends BasePullToRefreshListFra } mEditText.addTextChangedListener(this); - final List accounts = Account.getAccountsList(getActivity(), false); + final List accounts = ParcelableAccount.getAccountsList(getActivity(), false); mAccountSpinner.setAdapter(new AccountsSpinnerAdapter(getActivity(), accounts)); mAccountSpinner.setOnItemSelectedListener(this); @@ -331,7 +331,7 @@ public class DirectMessagesConversationFragment extends BasePullToRefreshListFra @Override public void onItemSelected(final AdapterView parent, final View view, final int pos, final long id) { - final Account account = (Account) mAccountSpinner.getSelectedItem(); + final ParcelableAccount account = (ParcelableAccount) mAccountSpinner.getSelectedItem(); if (account != null) { mAccountId = account.account_id; mSender = account; @@ -487,7 +487,7 @@ public class DirectMessagesConversationFragment extends BasePullToRefreshListFra mAccountId = accountId; mRecipientId = recipientId; final Context context = getActivity(); - mSender = Account.getAccount(context, accountId); + mSender = ParcelableAccount.getAccount(context, accountId); mRecipient = Utils.getUserForConversation(context, accountId, recipientId); final LoaderManager lm = getLoaderManager(); final Bundle args = new Bundle(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesFragment.java index 9c5e829cc..a9b099fca 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesFragment.java @@ -37,8 +37,8 @@ import android.widget.AbsListView; import android.widget.ListView; import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.RawItemArray; -import org.mariotaku.querybuilder.Where; import org.mariotaku.twidere.R; import org.mariotaku.twidere.adapter.DirectMessageConversationEntriesAdapter; import org.mariotaku.twidere.provider.TweetStore.Accounts; @@ -112,10 +112,7 @@ public class DirectMessagesFragment extends BasePullToRefreshListFragment implem mAdapter = new DirectMessageConversationEntriesAdapter(getActivity()); setListAdapter(mAdapter); mListView = getListView(); - if (!mPreferences.getBoolean(KEY_COMPACT_CARDS, false)) { - mListView.setDivider(null); - } - mListView.setSelector(android.R.color.transparent); + mListView.setDivider(null); getLoaderManager().initLoader(0, null, this); setListShown(false); } @@ -130,7 +127,7 @@ public class DirectMessagesFragment extends BasePullToRefreshListFragment implem if (!no_account_selected) { getListView().setEmptyView(null); } - final Where account_where = Where.in(new Column(Statuses.ACCOUNT_ID), new RawItemArray(account_ids)); + final Expression account_where = Expression.in(new Column(Statuses.ACCOUNT_ID), new RawItemArray(account_ids)); return new CursorLoader(getActivity(), uri, null, account_where.getSQL(), null, null); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/HomeTimelineFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/HomeTimelineFragment.java index 7049822fd..5c05b7122 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/HomeTimelineFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/HomeTimelineFragment.java @@ -19,33 +19,61 @@ package org.mariotaku.twidere.fragment.support; -import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; -import android.database.Cursor; import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import org.mariotaku.twidere.adapter.CursorStatusesListAdapter; import org.mariotaku.twidere.provider.TweetStore.Statuses; +import org.mariotaku.twidere.util.AsyncTwitterWrapper; /** * Created by mariotaku on 14/12/3. */ public class HomeTimelineFragment extends CursorStatusesFragment { + @Override - public int getStatuses(long[] accountIds, long[] maxIds, long[] sinceIds) { - return 0; + public Uri getContentUri() { + return Statuses.CONTENT_URI; } @Override - public Loader onCreateLoader(int id, Bundle args) { - final Context context = getActivity(); - final Uri uri = Statuses.CONTENT_URI; - final SharedPreferences preferences = getSharedPreferences(); - final boolean sortById = preferences.getBoolean(KEY_SORT_TIMELINE_BY_ID, false); - final String sortOrder = sortById ? Statuses.SORT_ORDER_STATUS_ID_DESC : Statuses.SORT_ORDER_TIMESTAMP_DESC; - return new CursorLoader(context, uri, CursorStatusesListAdapter.CURSOR_COLS, null, null, sortOrder); + protected int getNotificationType() { + return NOTIFICATION_ID_HOME_TIMELINE; } + + @Override + protected boolean isFilterEnabled() { + final SharedPreferences pref = getSharedPreferences(); + return pref != null && pref.getBoolean(KEY_FILTERS_IN_HOME_TIMELINE, true); + } + + @Override + public int getStatuses(long[] accountIds, long[] maxIds, long[] sinceIds) { + final AsyncTwitterWrapper twitter = getTwitterWrapper(); + if (twitter == null) return -1; + return twitter.getHomeTimelineAsync(accountIds, maxIds, sinceIds); + } + + @Override + protected void onReceivedBroadcast(Intent intent, String action) { + switch (action) { + case BROADCAST_TASK_STATE_CHANGED: { + updateRefreshState(); + break; + } + } + } + + @Override + protected void onSetIntentFilter(IntentFilter filter) { + filter.addAction(BROADCAST_TASK_STATE_CHANGED); + } + + private void updateRefreshState() { + final AsyncTwitterWrapper twitter = getTwitterWrapper(); + if (twitter == null) return; + setRefreshing(twitter.isHomeTimelineRefreshing()); + } + } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/HomeTimelineListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/HomeTimelineListFragment.java deleted file mode 100644 index 363c586c7..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/HomeTimelineListFragment.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 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.fragment.support; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.net.Uri; - -import org.mariotaku.twidere.provider.TweetStore.Statuses; -import org.mariotaku.twidere.util.AsyncTwitterWrapper; - -public class HomeTimelineListFragment extends CursorStatusesListFragment { - - private final BroadcastReceiver mStatusReceiver = new BroadcastReceiver() { - - @Override - public void onReceive(final Context context, final Intent intent) { - if (getActivity() == null || !isAdded() || isDetached()) return; - final String action = intent.getAction(); - if (BROADCAST_HOME_TIMELINE_REFRESHED.equals(action)) { - setRefreshComplete(); - } else if (BROADCAST_TASK_STATE_CHANGED.equals(action)) { - updateRefreshState(); - } - } - }; - - @Override - public int getStatuses(final long[] accountIds, final long[] maxIds, final long[] sinceIds) { - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (twitter == null) return 0; - if (maxIds == null) return twitter.refreshAll(accountIds); - return twitter.getHomeTimelineAsync(accountIds, maxIds, sinceIds); - } - - @Override - public void onStart() { - super.onStart(); - final IntentFilter filter = new IntentFilter(BROADCAST_HOME_TIMELINE_REFRESHED); - filter.addAction(BROADCAST_TASK_STATE_CHANGED); - registerReceiver(mStatusReceiver, filter); - } - - @Override - public void onStop() { - unregisterReceiver(mStatusReceiver); - super.onStop(); - } - - @Override - protected Uri getContentUri() { - return Statuses.CONTENT_URI; - } - - @Override - protected int getNotificationType() { - return NOTIFICATION_ID_HOME_TIMELINE; - } - - @Override - protected String getPositionKey() { - return "home_timeline" + getTabPosition(); - } - - @Override - protected boolean isFiltersEnabled() { - final SharedPreferences pref = getSharedPreferences(); - return pref != null && pref.getBoolean(KEY_FILTERS_IN_HOME_TIMELINE, true); - } - - @Override - protected void updateRefreshState() { - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (twitter == null || !getUserVisibleHint() || getActivity() == null) return; - setRefreshing(twitter.isHomeTimelineRefreshing()); - } - -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/IncomingFriendshipsMenuDialogFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/IncomingFriendshipsMenuDialogFragment.java index a37ab1180..a0895d954 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/IncomingFriendshipsMenuDialogFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/IncomingFriendshipsMenuDialogFragment.java @@ -5,8 +5,8 @@ import android.view.Menu; import android.view.MenuInflater; import org.mariotaku.twidere.R; -import org.mariotaku.twidere.model.Account; -import org.mariotaku.twidere.model.Account.AccountWithCredentials; +import org.mariotaku.twidere.model.ParcelableAccount; +import org.mariotaku.twidere.model.ParcelableAccount.ParcelableAccountWithCredentials; import org.mariotaku.twidere.model.ParcelableUser; public class IncomingFriendshipsMenuDialogFragment extends UserMenuDialogFragment { @@ -14,8 +14,8 @@ public class IncomingFriendshipsMenuDialogFragment extends UserMenuDialogFragmen @Override protected void onPrepareItemMenu(final Menu menu, final ParcelableUser user) { final Context context = getThemedContext(); - final AccountWithCredentials account = Account.getAccountWithCredentials(context, user.account_id); - if (AccountWithCredentials.isOfficialCredentials(context, account)) { + final ParcelableAccountWithCredentials account = ParcelableAccount.getAccountWithCredentials(context, user.account_id); + if (ParcelableAccountWithCredentials.isOfficialCredentials(context, account)) { final MenuInflater inflater = new MenuInflater(context); inflater.inflate(R.menu.action_incoming_friendship, menu); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/MentionsTimelineFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/MentionsTimelineFragment.java index cdff2cf92..18b037f8b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/MentionsTimelineFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/MentionsTimelineFragment.java @@ -1,18 +1,18 @@ /* - * Twidere - Twitter client for Android - * + * Twidere - Twitter client for Android + * * Copyright (C) 2012-2014 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 . */ @@ -25,89 +25,57 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.net.Uri; -import android.os.Bundle; -import org.mariotaku.querybuilder.Where; import org.mariotaku.twidere.provider.TweetStore.Mentions; import org.mariotaku.twidere.util.AsyncTwitterWrapper; -public class MentionsTimelineFragment extends CursorStatusesListFragment { +/** + * Created by mariotaku on 14/12/3. + */ +public class MentionsTimelineFragment extends CursorStatusesFragment { - private final BroadcastReceiver mStatusReceiver = new BroadcastReceiver() { + @Override + public Uri getContentUri() { + return Mentions.CONTENT_URI; + } - @Override - public void onReceive(final Context context, final Intent intent) { - if (getActivity() == null || !isAdded() || isDetached()) return; - final String action = intent.getAction(); - if (BROADCAST_MENTIONS_REFRESHED.equals(action)) { - setRefreshComplete(); - } else if (BROADCAST_TASK_STATE_CHANGED.equals(action)) { - updateRefreshState(); - } - } - }; + @Override + protected int getNotificationType() { + return NOTIFICATION_ID_MENTIONS_TIMELINE; + } - @Override - public int getStatuses(final long[] account_ids, final long[] max_ids, final long[] since_ids) { - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (twitter == null) return -1; - return twitter.getMentionsAsync(account_ids, max_ids, since_ids); - } + @Override + protected boolean isFilterEnabled() { + final SharedPreferences pref = getSharedPreferences(); + return pref != null && pref.getBoolean(KEY_FILTERS_IN_MENTIONS_TIMELINE, true); + } - @Override - public void onActivityCreated(final Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - getListAdapter().setMentionsHightlightDisabled(true); - } + @Override + public int getStatuses(long[] accountIds, long[] maxIds, long[] sinceIds) { + final AsyncTwitterWrapper twitter = getTwitterWrapper(); + if (twitter == null) return -1; + return twitter.getMentionsTimelineAsync(accountIds, maxIds, sinceIds); + } - @Override - public void onStart() { - super.onStart(); - final IntentFilter filter = new IntentFilter(BROADCAST_MENTIONS_REFRESHED); - filter.addAction(BROADCAST_TASK_STATE_CHANGED); - registerReceiver(mStatusReceiver, filter); - } + @Override + protected void onReceivedBroadcast(Intent intent, String action) { + switch (action) { + case BROADCAST_TASK_STATE_CHANGED: { + updateRefreshState(); + break; + } + } + } - @Override - public void onStop() { - unregisterReceiver(mStatusReceiver); - super.onStop(); - } + @Override + protected void onSetIntentFilter(IntentFilter filter) { + filter.addAction(BROADCAST_TASK_STATE_CHANGED); + } - @Override - protected Uri getContentUri() { - return Mentions.CONTENT_URI; - } - - @Override - protected int getNotificationType() { - return NOTIFICATION_ID_MENTIONS; - } - - @Override - protected String getPositionKey() { - return "mentions_timeline" + getTabPosition(); - } - - @Override - protected boolean isFiltersEnabled() { - final SharedPreferences pref = getSharedPreferences(); - return pref != null && pref.getBoolean(KEY_FILTERS_IN_MENTIONS, true); - } - - @Override - protected Where processWhere(final Where where) { - final Bundle extras = getExtraConfiguration(); - if (extras.getBoolean(EXTRA_MY_FOLLOWING_ONLY)) - return Where.and(where, Where.equals(Mentions.IS_FOLLOWING, 1)); - return where; - } - - @Override - protected void updateRefreshState() { - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (twitter == null || !getUserVisibleHint()) return; - setRefreshing(twitter.isMentionsRefreshing()); - } + private void updateRefreshState() { + final AsyncTwitterWrapper twitter = getTwitterWrapper(); + if (twitter == null) return; + setRefreshing(twitter.isMentionsTimelineRefreshing()); + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesFragment.java index 912b89864..64f97a0cb 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesFragment.java @@ -20,11 +20,15 @@ package org.mariotaku.twidere.fragment.support; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.Bundle; import org.mariotaku.twidere.adapter.ParcelableStatusesAdapter; +import org.mariotaku.twidere.adapter.iface.IStatusesAdapter; import org.mariotaku.twidere.model.ParcelableStatus; +import java.util.ArrayList; import java.util.List; /** @@ -33,8 +37,31 @@ import java.util.List; public abstract class ParcelableStatusesFragment extends AbsStatusesFragment> { @Override - protected ParcelableStatusesAdapter onCreateAdapter(final Context context, final boolean compact) { - return new ParcelableStatusesAdapter(context, compact); + protected void onSetIntentFilter(IntentFilter filter) { + filter.addAction(BROADCAST_STATUS_DESTROYED); + } + + @Override + protected void onReceivedBroadcast(Intent intent, String action) { + switch (action) { + case BROADCAST_STATUS_DESTROYED: { + deleteStatus(intent.getLongExtra(EXTRA_STATUS_ID, -1)); + break; + } + } + } + + public final void deleteStatus(final long statusId) { + final List data = getAdapterData(); + if (statusId <= 0 || data == null) return; + final ArrayList dataToRemove = new ArrayList<>(); + for (final ParcelableStatus status : data) { + if (status.id == statusId || status.retweet_id > 0 && status.retweet_id == statusId) { + dataToRemove.add(status); + } + } + data.removeAll(dataToRemove); + setAdapterData(data); } @Override @@ -50,4 +77,39 @@ public abstract class ParcelableStatusesFragment extends AbsStatusesFragment> adapter = getAdapter(); + final long[] maxIds = new long[]{adapter.getStatus(adapter.getStatusCount() - 1).id}; + getStatuses(null, maxIds, null); + } + + @Override + public boolean triggerRefresh() { + final IStatusesAdapter> adapter = getAdapter(); + final long[] accountIds = getAccountIds(); + if (adapter.getStatusCount() > 0) { + final long[] sinceIds = new long[]{adapter.getStatus(0).id}; + getStatuses(accountIds, null, sinceIds); + } else { + getStatuses(accountIds, null, null); + } + return true; + } + + protected long getAccountId() { + final Bundle args = getArguments(); + return args != null ? args.getLong(EXTRA_ACCOUNT_ID, -1) : -1; + } + } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesListFragment.java index be00e9690..954fbfd29 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesListFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesListFragment.java @@ -58,7 +58,7 @@ public abstract class ParcelableStatusesListFragment extends BaseStatusesListFra if (statusId > 0) { deleteStatus(statusId); } - } else if (BROADCAST_RETWEET_CHANGED.equals(action)) { + } else if (BROADCAST_STATUS_RETWEETED.equals(action)) { final long status_id = intent.getLongExtra(EXTRA_STATUS_ID, -1); final boolean retweeted = intent.getBooleanExtra(EXTRA_RETWEETED, false); if (status_id > 0 && !retweeted) { @@ -158,7 +158,7 @@ public abstract class ParcelableStatusesListFragment extends BaseStatusesListFra public void onStart() { super.onStart(); final IntentFilter filter = new IntentFilter(BROADCAST_STATUS_DESTROYED); - filter.addAction(BROADCAST_RETWEET_CHANGED); + filter.addAction(BROADCAST_STATUS_RETWEETED); registerReceiver(mStateReceiver, filter); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/SearchFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/SearchFragment.java index 2e213df77..ae9a56c5d 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/SearchFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/SearchFragment.java @@ -54,6 +54,15 @@ public class SearchFragment extends BaseSupportFragment implements RefreshScroll private Fragment mCurrentVisibleFragment; + @Override + protected void fitSystemWindows(Rect insets) { + super.fitSystemWindows(insets); + final View view = getView(); + if (view != null) { + view.setPadding(insets.left, insets.top, insets.right, insets.bottom); + } + } + @Override public Fragment getCurrentVisibleFragment() { return mCurrentVisibleFragment; diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StaggeredGridFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StaggeredGridFragment.java deleted file mode 100644 index 26095fae7..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StaggeredGridFragment.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.mariotaku.twidere.fragment.support; - -import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.support.v4.app.Fragment; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AnimationUtils; -import android.widget.AdapterView; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.ListAdapter; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.etsy.android.grid.StaggeredGridView; - -import org.mariotaku.twidere.R; - -/** - * Static library support version of the framework's - * {@link android.app.ListFragment}. Used to write apps that run on platforms - * prior to Android 3.0. When running on Android 3.0 or above, this - * implementation is still used; it does not try to switch to the framework's - * implementation. See the framework SDK documentation for a class overview. - */ -public class StaggeredGridFragment extends Fragment { - static final int INTERNAL_EMPTY_ID = 0x00ff0001; - static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002; - static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003; - - final private Handler mHandler = new Handler(); - - final private Runnable mRequestFocus = new Runnable() { - @Override - public void run() { - mList.focusableViewAvailable(mList); - } - }; - - final private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(final AdapterView parent, final View v, final int position, final long id) { - onListItemClick((StaggeredGridView) parent, v, position, id); - } - }; - - ListAdapter mAdapter; - StaggeredGridView mList; - View mEmptyView; - TextView mStandardEmptyView; - View mProgressContainer; - View mListContainer; - CharSequence mEmptyText; - boolean mListShown; - - public StaggeredGridFragment() { - } - - /** - * Get the ListAdapter associated with this activity's ListView. - */ - public ListAdapter getListAdapter() { - return mAdapter; - } - - /** - * Get the activity's list view widget. - */ - public StaggeredGridView getListView() { - ensureList(); - return mList; - } - - /** - * Get the cursor row ID of the currently selected list item. - */ - public long getSelectedItemId() { - ensureList(); - return mList.getSelectedItemId(); - } - - /** - * Get the position of the currently selected list item. - */ - public int getSelectedItemPosition() { - ensureList(); - return mList.getSelectedItemPosition(); - } - - /** - * Provide default implementation to return a simple list view. Subclasses - * can override to replace with their own layout. If doing so, the returned - * view hierarchy must have a ListView whose id is - * {@link android.R.id#list android.R.id.list} and can optionally have a - * sibling view id {@link android.R.id#empty android.R.id.empty} that is to - * be shown when the list is empty. - * - *

- * If you are overriding this method with your own custom content, consider - * including the standard layout {@link android.R.layout#list_content} in - * your layout file, so that you continue to retain all of the standard - * behavior of ListFragment. In particular, this is currently the only way - * to have the built-in indeterminant progress state be shown. - */ - @Override - public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { - final Context context = getActivity(); - - final FrameLayout root = new FrameLayout(context); - - // ------------------------------------------------------------------ - - final LinearLayout pframe = new LinearLayout(context); - pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID); - pframe.setOrientation(LinearLayout.VERTICAL); - pframe.setVisibility(View.GONE); - pframe.setGravity(Gravity.CENTER); - - final ProgressBar progress = new ProgressBar(context, null, android.R.attr.progressBarStyleLarge); - pframe.addView(progress, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - - root.addView(pframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - // ------------------------------------------------------------------ - - final FrameLayout lframe = new FrameLayout(context); - lframe.setId(INTERNAL_LIST_CONTAINER_ID); - - final TextView tv = new TextView(getActivity()); - tv.setId(INTERNAL_EMPTY_ID); - tv.setGravity(Gravity.CENTER); - lframe.addView(tv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - final StaggeredGridView lv = (StaggeredGridView) inflater.inflate(R.layout.staggered_gridview, lframe, false); - lv.setId(android.R.id.list); - lv.setDrawSelectorOnTop(false); - lframe.addView(lv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - root.addView(lframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - // ------------------------------------------------------------------ - - root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - return root; - } - - /** - * Detach from list view. - */ - @Override - public void onDestroyView() { - mHandler.removeCallbacks(mRequestFocus); - mList = null; - mListShown = false; - mEmptyView = mProgressContainer = mListContainer = null; - mStandardEmptyView = null; - super.onDestroyView(); - } - - /** - * This method will be called when an item in the list is selected. - * Subclasses should override. Subclasses can call - * getListView().getItemAtPosition(position) if they need to access the data - * associated with the selected item. - * - * @param l The ListView where the click happened - * @param v The view that was clicked within the ListView - * @param position The position of the view in the list - * @param id The row id of the item that was clicked - */ - public void onListItemClick(final StaggeredGridView l, final View v, final int position, final long id) { - } - - /** - * Attach to list view once the view hierarchy has been created. - */ - @Override - public void onViewCreated(final View view, final Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - ensureList(); - } - - /** - * The default content for a ListFragment has a TextView that can be shown - * when the list is empty. If you would like to have it shown, call this - * method to supply the text it should use. - */ - public void setEmptyText(final CharSequence text) { - ensureList(); - if (mStandardEmptyView == null) throw new IllegalStateException("Can't be used with a custom content view"); - mStandardEmptyView.setText(text); - if (mEmptyText == null) { - mList.setEmptyView(mStandardEmptyView); - } - mEmptyText = text; - } - - /** - * Provide the cursor for the list view. - */ - public void setListAdapter(final ListAdapter adapter) { - final boolean hadAdapter = mAdapter != null; - mAdapter = adapter; - if (mList != null) { - mList.setAdapter(adapter); - if (!mListShown && !hadAdapter) { - // The list was hidden, and previously didn't have an - // adapter. It is now time to show it. - setListShown(true, getView().getWindowToken() != null); - } - } - } - - /** - * Control whether the list is being displayed. You can make it not - * displayed if you are waiting for the initial data to show in it. During - * this time an indeterminant progress indicator will be shown instead. - * - *

- * Applications do not normally need to use this themselves. The default - * behavior of ListFragment is to start with the list not being shown, only - * showing it once an adapter is given with - * {@link #setListAdapter(ListAdapter)}. If the list at that point had not - * been shown, when it does get shown it will be do without the user ever - * seeing the hidden state. - * - * @param shown If true, the list view is shown; if false, the progress - * indicator. The initial value is true. - */ - public void setListShown(final boolean shown) { - setListShown(shown, true); - } - - /** - * Like {@link #setListShown(boolean)}, but no animation is used when - * transitioning from the previous state. - */ - public void setListShownNoAnimation(final boolean shown) { - setListShown(shown, false); - } - - /** - * Set the currently selected list item to the specified position with the - * adapter's data - * - * @param position - */ - public void setSelection(final int position) { - ensureList(); - mList.setSelection(position); - } - - private void ensureList() { - if (mList != null) return; - final View root = getView(); - if (root == null) throw new IllegalStateException("Content view not yet created"); - if (root instanceof StaggeredGridView) { - mList = (StaggeredGridView) root; - } else { - mStandardEmptyView = (TextView) root.findViewById(INTERNAL_EMPTY_ID); - if (mStandardEmptyView == null) { - mEmptyView = root.findViewById(android.R.id.empty); - } else { - mStandardEmptyView.setVisibility(View.GONE); - } - mProgressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID); - mListContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID); - final View rawListView = root.findViewById(android.R.id.list); - if (!(rawListView instanceof StaggeredGridView)) { - if (rawListView == null) - throw new RuntimeException("Your content must have a StaggeredGridView whose id attribute is " - + "'android.R.id.list'"); - throw new RuntimeException("Content has view with id attribute 'android.R.id.list' " - + "that is not a StaggeredGridView class"); - } - mList = (StaggeredGridView) rawListView; - if (mEmptyView != null) { - mList.setEmptyView(mEmptyView); - } else if (mEmptyText != null) { - mStandardEmptyView.setText(mEmptyText); - mList.setEmptyView(mStandardEmptyView); - } - } - mListShown = true; - mList.setOnItemClickListener(mOnClickListener); - if (mAdapter != null) { - final ListAdapter adapter = mAdapter; - mAdapter = null; - setListAdapter(adapter); - } else { - // We are starting without an adapter, so assume we won't - // have our data right away and start with the progress indicator. - if (mProgressContainer != null) { - setListShown(false, false); - } - } - mHandler.post(mRequestFocus); - } - - /** - * Control whether the list is being displayed. You can make it not - * displayed if you are waiting for the initial data to show in it. During - * this time an indeterminant progress indicator will be shown instead. - * - * @param shown If true, the list view is shown; if false, the progress - * indicator. The initial value is true. - * @param animate If true, an animation will be used to transition to the - * new state. - */ - private void setListShown(final boolean shown, final boolean animate) { - ensureList(); - if (mProgressContainer == null) throw new IllegalStateException("Can't be used with a custom content view"); - if (mListShown == shown) return; - mListShown = shown; - if (shown) { - if (animate) { - mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out)); - mListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in)); - } else { - mProgressContainer.clearAnimation(); - mListContainer.clearAnimation(); - } - mProgressContainer.setVisibility(View.GONE); - mListContainer.setVisibility(View.VISIBLE); - } else { - if (animate) { - mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in)); - mListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out)); - } else { - mProgressContainer.clearAnimation(); - mListContainer.clearAnimation(); - } - mProgressContainer.setVisibility(View.VISIBLE); - mListContainer.setVisibility(View.GONE); - } - } -} \ No newline at end of file diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StaggeredHomeTimelineFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StaggeredHomeTimelineFragment.java deleted file mode 100644 index 1ddbbea74..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StaggeredHomeTimelineFragment.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 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.fragment.support; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.net.Uri; -import android.view.View; - -import org.mariotaku.twidere.provider.TweetStore.Statuses; -import org.mariotaku.twidere.util.AsyncTwitterWrapper; - -public class StaggeredHomeTimelineFragment extends CursorStatusesStaggeredGridFragment { - - private final BroadcastReceiver mStatusReceiver = new BroadcastReceiver() { - - @Override - public void onReceive(final Context context, final Intent intent) { - if (getActivity() == null || !isAdded() || isDetached()) return; - final String action = intent.getAction(); - if (BROADCAST_HOME_TIMELINE_REFRESHED.equals(action)) { - setRefreshComplete(); - } else if (BROADCAST_TASK_STATE_CHANGED.equals(action)) { - updateRefreshState(); - } - } - }; - - @Override - public int getStatuses(final long[] accountIds, final long[] maxIds, final long[] sinceIds) { - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (twitter == null) return 0; - if (maxIds == null) return twitter.refreshAll(accountIds); - return twitter.getHomeTimelineAsync(accountIds, maxIds, sinceIds); - } - - @Override - public void onStart() { - super.onStart(); - final IntentFilter filter = new IntentFilter(BROADCAST_HOME_TIMELINE_REFRESHED); - filter.addAction(BROADCAST_TASK_STATE_CHANGED); - registerReceiver(mStatusReceiver, filter); - } - - @Override - public void onStop() { - unregisterReceiver(mStatusReceiver); - super.onStop(); - } - - @Override - protected Uri getContentUri() { - return Statuses.CONTENT_URI; - } - - @Override - protected int getNotificationType() { - return NOTIFICATION_ID_HOME_TIMELINE; - } - - @Override - protected String getPositionKey() { - return "home_timeline" + getTabPosition(); - } - - @Override - protected boolean isFiltersEnabled() { - final SharedPreferences pref = getSharedPreferences(); - return pref != null && pref.getBoolean(KEY_FILTERS_IN_HOME_TIMELINE, true); - } - - @Override - protected void updateRefreshState() { - final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (twitter == null || !getUserVisibleHint() || getActivity() == null) return; - setRefreshing(twitter.isHomeTimelineRefreshing()); - } - - @Override - public View getRefreshIndicatorView() { - return getListView().getRefreshIndicatorView(); - } -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java index dbab3f284..7eaf31e99 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java @@ -74,8 +74,8 @@ import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity; import org.mariotaku.twidere.adapter.ParcelableStatusesListAdapter; import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter; import org.mariotaku.twidere.app.TwidereApplication; -import org.mariotaku.twidere.model.Account; -import org.mariotaku.twidere.model.Account.AccountWithCredentials; +import org.mariotaku.twidere.model.ParcelableAccount; +import org.mariotaku.twidere.model.ParcelableAccount.ParcelableAccountWithCredentials; import org.mariotaku.twidere.model.ParcelableLocation; import org.mariotaku.twidere.model.ParcelableMedia; import org.mariotaku.twidere.model.ParcelableStatus; @@ -203,7 +203,7 @@ public class StatusFragment extends ParcelableStatusesListFragment implements On } break; } - case BROADCAST_RETWEET_CHANGED: { + case BROADCAST_STATUS_RETWEETED: { final long status_id = intent.getLongExtra(EXTRA_STATUS_ID, -1); if (status_id > 0 && status_id == getStatusId()) { getStatus(true); @@ -561,9 +561,9 @@ public class StatusFragment extends ParcelableStatusesListFragment implements On } case R.id.favorites_container: { // TODO - final AccountWithCredentials account = Account.getAccountWithCredentials(getActivity(), + final ParcelableAccountWithCredentials account = ParcelableAccount.getAccountWithCredentials(getActivity(), status.account_id); - if (AccountWithCredentials.isOfficialCredentials(getActivity(), account)) { + if (ParcelableAccountWithCredentials.isOfficialCredentials(getActivity(), account)) { openStatusFavoriters(getActivity(), status.account_id, status.retweet_id > 0 ? status.retweet_id : status.id); } @@ -689,7 +689,7 @@ public class StatusFragment extends ParcelableStatusesListFragment implements On final IntentFilter filter = new IntentFilter(); filter.addAction(BROADCAST_FRIENDSHIP_CHANGED); filter.addAction(BROADCAST_FAVORITE_CHANGED); - filter.addAction(BROADCAST_RETWEET_CHANGED); + filter.addAction(BROADCAST_STATUS_RETWEETED); registerReceiver(mStatusReceiver, filter); updateUserColor(); final int text_size = mPreferences.getInt(KEY_TEXT_SIZE, getDefaultTextSize(getActivity())); @@ -814,9 +814,9 @@ public class StatusFragment extends ParcelableStatusesListFragment implements On break; } case MENU_TRANSLATE: { - final AccountWithCredentials account = Account.getAccountWithCredentials(getActivity(), + final ParcelableAccountWithCredentials account = ParcelableAccount.getAccountWithCredentials(getActivity(), status.account_id); - if (AccountWithCredentials.isOfficialCredentials(getActivity(), account)) { + if (ParcelableAccountWithCredentials.isOfficialCredentials(getActivity(), account)) { StatusTranslateDialogFragment.show(getFragmentManager(), status); } else { diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFavoritesFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFavoritesFragment.java index 73d660eba..356ba2b5b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFavoritesFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFavoritesFragment.java @@ -36,7 +36,6 @@ public class UserFavoritesFragment extends ParcelableStatusesFragment { @Override public Loader> onCreateLoader(int id, Bundle args) { setRefreshing(true); - final List data = getAdapterData(); final Context context = getActivity(); final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1); final long maxId = args.getLong(EXTRA_MAX_ID, -1); @@ -44,8 +43,8 @@ public class UserFavoritesFragment extends ParcelableStatusesFragment { final long userId = args.getLong(EXTRA_USER_ID, -1); final String screenName = args.getString(EXTRA_SCREEN_NAME); final int tabPosition = args.getInt(EXTRA_TAB_POSITION, -1); - return new UserFavoritesLoader(context, accountId, userId, screenName, maxId, sinceId, data, - null, tabPosition); + return new UserFavoritesLoader(context, accountId, userId, screenName, maxId, sinceId, + getAdapterData(), null, tabPosition); } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java index 5bf013ea8..6f9d3237b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java @@ -80,7 +80,7 @@ import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import org.mariotaku.menucomponent.internal.menu.MenuUtils; -import org.mariotaku.querybuilder.Where; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.support.AccountSelectorActivity; import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity; @@ -332,7 +332,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener mFollowButton.setText(R.string.follow); } final ContentResolver resolver = getContentResolver(); - final String where = Where.equals(CachedUsers.USER_ID, user.id).getSQL(); + final String where = Expression.equals(CachedUsers.USER_ID, user.id).getSQL(); resolver.delete(CachedUsers.CONTENT_URI, where, null); // I bet you don't want to see blocked user in your auto // complete list. @@ -926,7 +926,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener final boolean filtering = Utils.isFilteringUser(getActivity(), user.id); final ContentResolver cr = getContentResolver(); if (filtering) { - final Where where = Where.equals(Filters.Users.USER_ID, user.id); + final Expression where = Expression.equals(Filters.Users.USER_ID, user.id); cr.delete(Filters.Users.CONTENT_URI, where.getSQL(), null); showInfoMessage(getActivity(), R.string.message_user_unmuted, false); } else { @@ -1115,7 +1115,8 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener final Drawable shadow = activity.getResources().getDrawable(R.drawable.shadow_user_banner_action_bar); final Drawable background = ThemeUtils.getActionBarBackground(activity, themeResId); mActionBarBackground = new ActionBarDrawable(getResources(), shadow, background, ThemeUtils.isDarkTheme(themeResId)); - mActionBarBackground.setAlpha(ThemeUtils.getThemeAlpha(activity)); + mActionBarBackground.setAlpha(linkHandler.getCurrentThemeBackgroundAlpha()); + mProfileBannerView.setAlpha(linkHandler.getCurrentThemeBackgroundAlpha() / 255f); actionBar.setBackgroundDrawable(mActionBarBackground); } @@ -1161,8 +1162,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener final View profileBannerContainer = mProfileBannerContainer; final int spaceHeight = space.getHeight(); final float factor = MathUtils.clamp(offset / (float) spaceHeight, 0, 1); - profileBannerView.setAlpha(1.0f - factor / 8f); - profileBannerContainer.setTranslationY(-offset); + profileBannerContainer.setTranslationY(Math.max(-offset, -spaceHeight)); profileBannerView.setTranslationY(Math.min(offset, spaceHeight) / 2); if (mActionBarBackground != null && mTintedStatusContent != null) { diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java index a8f7838af..29b69ed4e 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java @@ -32,12 +32,14 @@ import android.graphics.Rect; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; +import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.support.v4.view.ViewPager; +import android.support.v7.widget.CardView; import android.text.method.LinkMovementMethod; import android.util.Log; import android.view.LayoutInflater; @@ -59,6 +61,7 @@ import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.support.UserListSelectorActivity; import org.mariotaku.twidere.adapter.support.SupportTabsAdapter; import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback; +import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback; import org.mariotaku.twidere.model.ParcelableUser; import org.mariotaku.twidere.model.ParcelableUserList; import org.mariotaku.twidere.model.SingleResponse; @@ -86,7 +89,7 @@ import static org.mariotaku.twidere.util.Utils.setMenuItemAvailability; public class UserListFragment extends BaseSupportFragment implements OnClickListener, LoaderCallbacks>, DrawerCallback, - SystemWindowsInsetsCallback { + SystemWindowsInsetsCallback, SupportFragmentCallback { private ImageLoaderWrapper mProfileImageLoader; private AsyncTwitterWrapper mTwitterWrapper; @@ -100,6 +103,7 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList private HeaderDrawerLayout mHeaderDrawerLayout; private ViewPager mViewPager; private PagerSlidingTabStrip mPagerIndicator; + private CardView mCardView; private SupportTabsAdapter mPagerAdapter; @@ -125,6 +129,59 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList } }; private boolean mUserListLoaderInitialized; + private Fragment mCurrentVisibleFragment; + + @Override + public boolean canScroll(float dy) { + final Fragment fragment = mCurrentVisibleFragment; + return fragment instanceof DrawerCallback && ((DrawerCallback) fragment).canScroll(dy); + } + + @Override + public void cancelTouch() { + final Fragment fragment = mCurrentVisibleFragment; + if (fragment instanceof DrawerCallback) { + ((DrawerCallback) fragment).cancelTouch(); + } + } + + @Override + public void fling(float velocity) { + final Fragment fragment = mCurrentVisibleFragment; + if (fragment instanceof DrawerCallback) { + ((DrawerCallback) fragment).fling(velocity); + } + } + + @Override + public boolean isScrollContent(float x, float y) { + final ViewPager v = mViewPager; + final int[] location = new int[2]; + v.getLocationOnScreen(location); + return x >= location[0] && x <= location[0] + v.getWidth() + && y >= location[1] && y <= location[1] + v.getHeight(); + } + + @Override + public void scrollBy(float dy) { + final Fragment fragment = mCurrentVisibleFragment; + if (fragment instanceof DrawerCallback) { + ((DrawerCallback) fragment).scrollBy(dy); + } + } + + @Override + public boolean shouldLayoutHeaderBottom() { + final HeaderDrawerLayout drawer = mHeaderDrawerLayout; + final CardView card = mCardView; + if (drawer == null || card == null) return false; + return card.getTop() + drawer.getHeaderTop() - drawer.getPaddingTop() <= 0; + } + + @Override + public void topChanged(int offset) { + + } public void displayUserList(final ParcelableUserList userList) { if (userList == null || getActivity() == null) return; @@ -149,6 +206,31 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList invalidateOptionsMenu(); } + @Override + public Fragment getCurrentVisibleFragment() { + return mCurrentVisibleFragment; + } + + @Override + public void onDetachFragment(Fragment fragment) { + + } + + @Override + public void onSetUserVisibleHint(Fragment fragment, boolean isVisibleToUser) { + mCurrentVisibleFragment = isVisibleToUser ? fragment : null; + } + + @Override + public boolean triggerRefresh(int position) { + return false; + } + + @Override + public boolean getSystemWindowsInsets(Rect insets) { + return false; + } + public void getUserListInfo(final boolean omit_intent_extra) { final LoaderManager lm = getLoaderManager(); lm.destroyLoader(0); @@ -320,29 +402,6 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList return true; } - private void setupUserPages() { - final Context context = getActivity(); - final Bundle args = getArguments(), tabArgs = new Bundle(); - if (args.containsKey(EXTRA_USER)) { - final ParcelableUserList userList = args.getParcelable(EXTRA_USER_LIST); - tabArgs.putLong(EXTRA_ACCOUNT_ID, userList.account_id); - tabArgs.putLong(EXTRA_USER_ID, userList.user_id); - tabArgs.putString(EXTRA_SCREEN_NAME, userList.user_screen_name); - tabArgs.putInt(EXTRA_LIST_ID, (int) userList.id); - tabArgs.putString(EXTRA_LIST_NAME, userList.name); - } else { - tabArgs.putLong(EXTRA_ACCOUNT_ID, args.getLong(EXTRA_ACCOUNT_ID, -1)); - tabArgs.putLong(EXTRA_USER_ID, args.getLong(EXTRA_USER_ID, -1)); - tabArgs.putString(EXTRA_SCREEN_NAME, args.getString(EXTRA_SCREEN_NAME)); - tabArgs.putInt(EXTRA_LIST_ID, args.getInt(EXTRA_LIST_ID, -1)); - tabArgs.putString(EXTRA_LIST_NAME, args.getString(EXTRA_LIST_NAME)); - } - mPagerAdapter.addTab(UserListTimelineFragment.class, tabArgs, getString(R.string.statuses), null, 0); - mPagerAdapter.addTab(UserListMembersFragment.class, tabArgs, getString(R.string.list_members), null, 1); - mPagerAdapter.addTab(UserListSubscribersFragment.class, tabArgs, getString(R.string.list_subscribers), null, 2); - mPagerIndicator.notifyDataSetChanged(); - } - @Override public void onClick(final View view) { switch (view.getId()) { @@ -415,6 +474,7 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList final View headerView = mHeaderDrawerLayout.getHeader(); final View contentView = mHeaderDrawerLayout.getContent(); + mCardView = (CardView) headerView.findViewById(R.id.card); mProfileContainer = (ColorLabelRelativeLayout) headerView.findViewById(R.id.profile); mListNameView = (TextView) headerView.findViewById(R.id.list_name); mCreatedByView = (TextView) headerView.findViewById(R.id.created_by); @@ -441,44 +501,27 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList content.setClipToPadding(false); } - @Override - public void fling(float velocity) { - - } - - @Override - public void scrollBy(float dy) { - - } - - @Override - public boolean shouldLayoutHeaderBottom() { - return false; - } - - @Override - public boolean canScroll(float dy) { - return false; - } - - @Override - public boolean isScrollContent(float x, float y) { - return false; - } - - @Override - public void cancelTouch() { - - } - - @Override - public void topChanged(int offset) { - - } - - @Override - public boolean getSystemWindowsInsets(Rect insets) { - return false; + private void setupUserPages() { + final Context context = getActivity(); + final Bundle args = getArguments(), tabArgs = new Bundle(); + if (args.containsKey(EXTRA_USER)) { + final ParcelableUserList userList = args.getParcelable(EXTRA_USER_LIST); + tabArgs.putLong(EXTRA_ACCOUNT_ID, userList.account_id); + tabArgs.putLong(EXTRA_USER_ID, userList.user_id); + tabArgs.putString(EXTRA_SCREEN_NAME, userList.user_screen_name); + tabArgs.putInt(EXTRA_LIST_ID, (int) userList.id); + tabArgs.putString(EXTRA_LIST_NAME, userList.name); + } else { + tabArgs.putLong(EXTRA_ACCOUNT_ID, args.getLong(EXTRA_ACCOUNT_ID, -1)); + tabArgs.putLong(EXTRA_USER_ID, args.getLong(EXTRA_USER_ID, -1)); + tabArgs.putString(EXTRA_SCREEN_NAME, args.getString(EXTRA_SCREEN_NAME)); + tabArgs.putInt(EXTRA_LIST_ID, args.getInt(EXTRA_LIST_ID, -1)); + tabArgs.putString(EXTRA_LIST_NAME, args.getString(EXTRA_LIST_NAME)); + } + mPagerAdapter.addTab(UserListTimelineFragment.class, tabArgs, getString(R.string.statuses), null, 0); + mPagerAdapter.addTab(UserListMembersFragment.class, tabArgs, getString(R.string.list_members), null, 1); + mPagerAdapter.addTab(UserListSubscribersFragment.class, tabArgs, getString(R.string.list_subscribers), null, 2); + mPagerIndicator.notifyDataSetChanged(); } public static class EditUserListDialogFragment extends BaseSupportDialogFragment implements diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListTimelineFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListTimelineFragment.java index 0d35dcd54..0c9a82860 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListTimelineFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListTimelineFragment.java @@ -1,75 +1,51 @@ /* - * Twidere - Twitter client for Android - * + * Twidere - Twitter client for Android + * * Copyright (C) 2012-2014 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.fragment.support; -import android.content.Context; import android.os.Bundle; import android.support.v4.content.Loader; -import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter; import org.mariotaku.twidere.loader.support.UserListTimelineLoader; import org.mariotaku.twidere.model.ParcelableStatus; import java.util.List; -public class UserListTimelineFragment extends ParcelableStatusesListFragment { +/** + * Created by mariotaku on 14/12/2. + */ +public class UserListTimelineFragment extends ParcelableStatusesFragment { - @Override - public Loader> newLoaderInstance(final Context context, final Bundle args) { - if (args == null) return null; - final int list_id = args.getInt(EXTRA_LIST_ID, -1); - final long account_id = args.getLong(EXTRA_ACCOUNT_ID, -1); - final long max_id = args.getLong(EXTRA_MAX_ID, -1); - final long since_id = args.getLong(EXTRA_SINCE_ID, -1); - final long user_id = args.getLong(EXTRA_USER_ID, -1); - final String screen_name = args.getString(EXTRA_SCREEN_NAME); - final String list_name = args.getString(EXTRA_LIST_NAME); - final int tab_position = args.getInt(EXTRA_TAB_POSITION, -1); - return new UserListTimelineLoader(getActivity(), account_id, list_id, user_id, screen_name, list_name, max_id, - since_id, getData(), getSavedStatusesFileArgs(), tab_position); - } - - @Override - public void onActivityCreated(final Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - final IStatusesListAdapter> adapter = getListAdapter(); - adapter.setFiltersEnabled(true); - adapter.setIgnoredFilterFields(false, false, false, false, false); - } - - @Override - protected String[] getSavedStatusesFileArgs() { - final Bundle args = getArguments(); - if (args == null) return null; - final int list_id = args.getInt(EXTRA_LIST_ID, -1); - final long account_id = args.getLong(EXTRA_ACCOUNT_ID, -1); - final long user_id = args.getLong(EXTRA_USER_ID, -1); - final String screen_name = args.getString(EXTRA_SCREEN_NAME); - final String list_name = args.getString(EXTRA_LIST_NAME); - return new String[] { AUTHORITY_USER_LIST_TIMELINE, "account" + account_id, "list_id" + list_id, - "list_name" + list_name, "user" + user_id, "screen_name" + screen_name }; - } - - @Override - protected boolean shouldShowAccountColor() { - return false; - } + @Override + public Loader> onCreateLoader(int id, Bundle args) { + setRefreshing(true); + if (args == null) return null; + final int listId = args.getInt(EXTRA_LIST_ID, -1); + final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1); + final long maxId = args.getLong(EXTRA_MAX_ID, -1); + final long sinceId = args.getLong(EXTRA_SINCE_ID, -1); + final long userId = args.getLong(EXTRA_USER_ID, -1); + final String screenName = args.getString(EXTRA_SCREEN_NAME); + final String listName = args.getString(EXTRA_LIST_NAME); + final int tabPosition = args.getInt(EXTRA_TAB_POSITION, -1); + return new UserListTimelineLoader(getActivity(), accountId, listId, userId, screenName, + listName, maxId, sinceId, getAdapterData(), null, tabPosition); + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListTimelineListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListTimelineListFragment.java new file mode 100644 index 000000000..1ffde1236 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListTimelineListFragment.java @@ -0,0 +1,75 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2014 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.fragment.support; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.content.Loader; + +import org.mariotaku.twidere.adapter.iface.IStatusesListAdapter; +import org.mariotaku.twidere.loader.support.UserListTimelineLoader; +import org.mariotaku.twidere.model.ParcelableStatus; + +import java.util.List; + +public class UserListTimelineListFragment extends ParcelableStatusesListFragment { + + @Override + public Loader> newLoaderInstance(final Context context, final Bundle args) { + if (args == null) return null; + final int list_id = args.getInt(EXTRA_LIST_ID, -1); + final long account_id = args.getLong(EXTRA_ACCOUNT_ID, -1); + final long max_id = args.getLong(EXTRA_MAX_ID, -1); + final long since_id = args.getLong(EXTRA_SINCE_ID, -1); + final long user_id = args.getLong(EXTRA_USER_ID, -1); + final String screen_name = args.getString(EXTRA_SCREEN_NAME); + final String list_name = args.getString(EXTRA_LIST_NAME); + final int tab_position = args.getInt(EXTRA_TAB_POSITION, -1); + return new UserListTimelineLoader(getActivity(), account_id, list_id, user_id, screen_name, list_name, max_id, + since_id, getData(), getSavedStatusesFileArgs(), tab_position); + } + + @Override + public void onActivityCreated(final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + final IStatusesListAdapter> adapter = getListAdapter(); + adapter.setFiltersEnabled(true); + adapter.setIgnoredFilterFields(false, false, false, false, false); + } + + @Override + protected String[] getSavedStatusesFileArgs() { + final Bundle args = getArguments(); + if (args == null) return null; + final int list_id = args.getInt(EXTRA_LIST_ID, -1); + final long account_id = args.getLong(EXTRA_ACCOUNT_ID, -1); + final long user_id = args.getLong(EXTRA_USER_ID, -1); + final String screen_name = args.getString(EXTRA_SCREEN_NAME); + final String list_name = args.getString(EXTRA_LIST_NAME); + return new String[] { AUTHORITY_USER_LIST_TIMELINE, "account" + account_id, "list_id" + list_id, + "list_name" + list_name, "user" + user_id, "screen_name" + screen_name }; + } + + @Override + protected boolean shouldShowAccountColor() { + return false; + } + +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/menu/AccountActionProvider.java b/twidere/src/main/java/org/mariotaku/twidere/menu/AccountActionProvider.java index fe818267e..daef766da 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/menu/AccountActionProvider.java +++ b/twidere/src/main/java/org/mariotaku/twidere/menu/AccountActionProvider.java @@ -9,19 +9,19 @@ import android.view.SubMenu; import android.view.View; import org.mariotaku.twidere.TwidereConstants; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; public class AccountActionProvider extends ActionProvider implements TwidereConstants { public static final int MENU_GROUP = 201; - private final Account[] mAccounts; + private final ParcelableAccount[] mAccounts; private long mAccountId; public AccountActionProvider(final Context context) { super(context); - mAccounts = Account.getAccounts(context, false, false); + mAccounts = ParcelableAccount.getAccounts(context, false, false); } @Override @@ -37,7 +37,7 @@ public class AccountActionProvider extends ActionProvider implements TwidereCons @Override public void onPrepareSubMenu(final SubMenu subMenu) { subMenu.removeGroup(MENU_GROUP); - for (final Account account : mAccounts) { + for (final ParcelableAccount account : mAccounts) { final MenuItem item = subMenu.add(MENU_GROUP, Menu.NONE, 0, account.name); final Intent intent = new Intent(); intent.putExtra(EXTRA_ACCOUNT, account); @@ -47,7 +47,7 @@ public class AccountActionProvider extends ActionProvider implements TwidereCons for (int i = 0, j = subMenu.size(); i < j; i++) { final MenuItem item = subMenu.getItem(i); final Intent intent = item.getIntent(); - final Account account = intent.getParcelableExtra(EXTRA_ACCOUNT); + final ParcelableAccount account = intent.getParcelableExtra(EXTRA_ACCOUNT); if (account.account_id == mAccountId) { item.setChecked(true); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/AccountPreferences.java b/twidere/src/main/java/org/mariotaku/twidere/model/AccountPreferences.java index 3f815da55..9d8dbbb8a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/model/AccountPreferences.java +++ b/twidere/src/main/java/org/mariotaku/twidere/model/AccountPreferences.java @@ -46,7 +46,7 @@ public class AccountPreferences implements Constants { } public int getDefaultNotificationLightColor() { - final Account a = Account.getAccount(mContext, mAccountId); + final ParcelableAccount a = ParcelableAccount.getAccount(mContext, mAccountId); return a != null ? a.color : mContext.getResources().getColor(R.color.material_light_blue); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/CustomTabConfiguration2.java b/twidere/src/main/java/org/mariotaku/twidere/model/CustomTabConfiguration2.java deleted file mode 100644 index 11be8cc1d..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/model/CustomTabConfiguration2.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 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.model; - -import android.support.v4.app.Fragment; - -import org.mariotaku.twidere.Constants; -import org.mariotaku.twidere.R; -import org.mariotaku.twidere.fragment.support.ActivitiesAboutMeFragment; -import org.mariotaku.twidere.fragment.support.ActivitiesByFriendsFragment; -import org.mariotaku.twidere.fragment.support.DirectMessagesFragment; -import org.mariotaku.twidere.fragment.support.HomeTimelineFragment; -import org.mariotaku.twidere.fragment.support.HomeTimelineListFragment; -import org.mariotaku.twidere.fragment.support.MentionsTimelineFragment; -import org.mariotaku.twidere.fragment.support.SearchStatusesFragment; -import org.mariotaku.twidere.fragment.support.TrendsSuggectionsFragment; -import org.mariotaku.twidere.fragment.support.UserFavoritesFragment; -import org.mariotaku.twidere.fragment.support.UserListTimelineFragment; -import org.mariotaku.twidere.fragment.support.UserTimelineFragment; - -import java.util.Comparator; -import java.util.Map.Entry; - -public enum CustomTabConfiguration2 implements Constants { - - HOME_TIMELINE(HomeTimelineFragment.class, R.string.home, R.drawable.ic_action_home, - CustomTabConfiguration.ACCOUNT_OPTIONAL, CustomTabConfiguration.FIELD_TYPE_NONE, 0, false), - - MENTIONS_TIMELINE(MentionsTimelineFragment.class, R.string.mentions, R.drawable.ic_action_at, - CustomTabConfiguration.ACCOUNT_OPTIONAL, CustomTabConfiguration.FIELD_TYPE_NONE, 1, false), - - DIRECT_MESSAGES(DirectMessagesFragment.class, R.string.direct_messages, R.drawable.ic_action_message, - CustomTabConfiguration.ACCOUNT_OPTIONAL, CustomTabConfiguration.FIELD_TYPE_NONE, 2, false), - - TRENDS_SUGGESTIONS(TrendsSuggectionsFragment.class, R.string.trends, R.drawable.ic_action_hashtag, - CustomTabConfiguration.ACCOUNT_NONE, CustomTabConfiguration.FIELD_TYPE_NONE, 3, true), - - FAVORITES(UserFavoritesFragment.class, R.string.favorites, R.drawable.ic_action_star, - CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_USER, 4), - - USER_TIMELINE(UserTimelineFragment.class, R.string.users_statuses, R.drawable.ic_action_quote, - CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_USER, 5), - - SEARCH_STATUSES(SearchStatusesFragment.class, R.string.search_statuses, R.drawable.ic_action_search, - CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_TEXT, R.string.query, - EXTRA_QUERY, 6), - - LIST_TIMELINE(UserListTimelineFragment.class, R.string.list_timeline, R.drawable.ic_action_list, - CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_USER_LIST, 7), - - ACTIVITIES_ABOUT_ME(ActivitiesAboutMeFragment.class, R.string.activities_about_me, - R.drawable.ic_action_user, CustomTabConfiguration.ACCOUNT_OPTIONAL, - CustomTabConfiguration.FIELD_TYPE_NONE, 8), - - ACTIVITIES_BY_FRIENDS(ActivitiesByFriendsFragment.class, R.string.activities_by_friends, - R.drawable.ic_action_accounts, CustomTabConfiguration.ACCOUNT_REQUIRED, - CustomTabConfiguration.FIELD_TYPE_NONE, 9); - - public static final int FIELD_TYPE_NONE = 0; - public static final int FIELD_TYPE_USER = 1; - public static final int FIELD_TYPE_USER_LIST = 2; - public static final int FIELD_TYPE_TEXT = 3; - - public static final int ACCOUNT_NONE = 0; - public static final int ACCOUNT_REQUIRED = 1; - public static final int ACCOUNT_OPTIONAL = 2; - - private final int title, icon, secondaryFieldType, secondaryFieldTitle, sortPosition, accountRequirement; - private final Class cls; - private final String secondaryFieldTextKey; - private final boolean singleTab; - - CustomTabConfiguration2(final Class cls, final int title, final int icon, - final int accountRequirement, final int secondaryFieldType, final int sortPosition) { - this(cls, title, icon, accountRequirement, secondaryFieldType, 0, EXTRA_TEXT, sortPosition, false); - } - - CustomTabConfiguration2(final Class cls, final int title, final int icon, - final int accountRequirement, final int secondaryFieldType, final int sortPosition, final boolean singleTab) { - this(cls, title, icon, accountRequirement, secondaryFieldType, 0, EXTRA_TEXT, sortPosition, singleTab); - } - - CustomTabConfiguration2(final Class cls, final int title, final int icon, - final int accountRequirement, final int secondaryFieldType, final int secondaryFieldTitle, - final String secondaryFieldTextKey, final int sortPosition) { - this(cls, title, icon, accountRequirement, secondaryFieldType, 0, secondaryFieldTextKey, sortPosition, false); - } - - CustomTabConfiguration2(final Class cls, final int title, final int icon, - final int accountRequirement, final int secondaryFieldType, final int secondaryFieldTitle, - final String secondaryFieldTextKey, final int sortPosition, final boolean singleTab) { - this.cls = cls; - this.title = title; - this.icon = icon; - this.sortPosition = sortPosition; - this.accountRequirement = accountRequirement; - this.secondaryFieldType = secondaryFieldType; - this.secondaryFieldTitle = secondaryFieldTitle; - this.secondaryFieldTextKey = secondaryFieldTextKey; - this.singleTab = singleTab; - } - - public int getAccountRequirement() { - return accountRequirement; - } - - public int getDefaultIcon() { - return icon; - } - - public int getDefaultTitle() { - return title; - } - - public Class getFragmentClass() { - return cls; - } - - public String getSecondaryFieldTextKey() { - return secondaryFieldTextKey; - } - - public int getSecondaryFieldTitle() { - return secondaryFieldTitle; - } - - public int getSecondaryFieldType() { - return secondaryFieldType; - } - - public int getSortPosition() { - return sortPosition; - } - - public boolean isSingleTab() { - return singleTab; - } - - @Override - public String toString() { - return "CustomTabConfiguration{title=" + title + ", icon=" + icon + ", secondaryFieldType=" - + secondaryFieldType + ", secondaryFieldTitle=" + secondaryFieldTitle + ", sortPosition=" - + sortPosition + ", accountRequirement=" + accountRequirement + ", cls=" + cls - + ", secondaryFieldTextKey=" + secondaryFieldTextKey + ", singleTab=" + singleTab + "}"; - } - - public static class CustomTabConfigurationComparator implements Comparator> { - - public static final CustomTabConfigurationComparator SINGLETON = new CustomTabConfigurationComparator(); - - @Override - public int compare(final Entry lhs, - final Entry rhs) { - return lhs.getValue().getSortPosition() - rhs.getValue().getSortPosition(); - } - - } - -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/DraftItem.java b/twidere/src/main/java/org/mariotaku/twidere/model/DraftItem.java index 14349b616..3a0f86e29 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/model/DraftItem.java +++ b/twidere/src/main/java/org/mariotaku/twidere/model/DraftItem.java @@ -80,7 +80,7 @@ public class DraftItem implements Parcelable { public DraftItem(final ParcelableStatusUpdate status) { _id = 0; - account_ids = Account.getAccountIds(status.accounts); + account_ids = ParcelableAccount.getAccountIds(status.accounts); in_reply_to_status_id = status.in_reply_to_status_id; text = status.text; media = status.media; diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/Account.java b/twidere/src/main/java/org/mariotaku/twidere/model/ParcelableAccount.java similarity index 81% rename from twidere/src/main/java/org/mariotaku/twidere/model/Account.java rename to twidere/src/main/java/org/mariotaku/twidere/model/ParcelableAccount.java index c85a4baee..14e4edb50 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/model/Account.java +++ b/twidere/src/main/java/org/mariotaku/twidere/model/ParcelableAccount.java @@ -27,8 +27,8 @@ import android.os.Parcelable; import android.support.annotation.NonNull; import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.RawItemArray; -import org.mariotaku.querybuilder.Where; import org.mariotaku.twidere.provider.TweetStore.Accounts; import org.mariotaku.twidere.util.content.ContentResolverUtils; @@ -39,18 +39,18 @@ import java.util.List; import static org.mariotaku.twidere.util.Utils.isOfficialConsumerKeySecret; import static org.mariotaku.twidere.util.Utils.shouldForceUsingPrivateAPIs; -public class Account implements Parcelable { +public class ParcelableAccount implements Parcelable { - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override - public Account createFromParcel(final Parcel in) { - return new Account(in); + public ParcelableAccount createFromParcel(final Parcel in) { + return new ParcelableAccount(in); } @Override - public Account[] newArray(final int size) { - return new Account[size]; + public ParcelableAccount[] newArray(final int size) { + return new ParcelableAccount[size]; } }; @@ -60,7 +60,7 @@ public class Account implements Parcelable { public final boolean is_activated; public final boolean is_dummy; - public Account(final Cursor cursor, final Indices indices) { + public ParcelableAccount(final Cursor cursor, final Indices indices) { is_dummy = false; screen_name = indices.screen_name != -1 ? cursor.getString(indices.screen_name) : null; name = indices.name != -1 ? cursor.getString(indices.name) : null; @@ -71,7 +71,7 @@ public class Account implements Parcelable { is_activated = indices.is_activated != -1 && cursor.getInt(indices.is_activated) == 1; } - public Account(final Parcel source) { + public ParcelableAccount(final Parcel source) { is_dummy = source.readInt() == 1; is_activated = source.readInt() == 1; account_id = source.readLong(); @@ -82,7 +82,7 @@ public class Account implements Parcelable { color = source.readInt(); } - private Account() { + private ParcelableAccount() { is_dummy = true; screen_name = null; name = null; @@ -117,11 +117,11 @@ public class Account implements Parcelable { out.writeInt(color); } - public static Account dummyInstance() { - return new Account(); + public static ParcelableAccount dummyInstance() { + return new ParcelableAccount(); } - public static Account getAccount(final Context context, final long account_id) { + public static ParcelableAccount getAccount(final Context context, final long account_id) { if (context == null) return null; final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, Accounts.COLUMNS, Accounts.ACCOUNT_ID + " = " + account_id, null, null); @@ -130,7 +130,7 @@ public class Account implements Parcelable { if (cur.getCount() > 0 && cur.moveToFirst()) { final Indices indices = new Indices(cur); cur.moveToFirst(); - return new Account(cur, indices); + return new ParcelableAccount(cur, indices); } } finally { cur.close(); @@ -139,7 +139,7 @@ public class Account implements Parcelable { return null; } - public static long[] getAccountIds(final Account[] accounts) { + public static long[] getAccountIds(final ParcelableAccount[] accounts) { final long[] ids = new long[accounts.length]; for (int i = 0, j = accounts.length; i < j; i++) { ids[i] = accounts[i].account_id; @@ -147,25 +147,25 @@ public class Account implements Parcelable { return ids; } - public static Account[] getAccounts(final Context context, final boolean activatedOnly, + public static ParcelableAccount[] getAccounts(final Context context, final boolean activatedOnly, final boolean officialKeyOnly) { - final List list = getAccountsList(context, activatedOnly, officialKeyOnly); - return list.toArray(new Account[list.size()]); + final List list = getAccountsList(context, activatedOnly, officialKeyOnly); + return list.toArray(new ParcelableAccount[list.size()]); } - public static Account[] getAccounts(final Context context, final long[] accountIds) { - if (context == null) return new Account[0]; - final String where = accountIds != null ? Where.in(new Column(Accounts.ACCOUNT_ID), + public static ParcelableAccount[] getAccounts(final Context context, final long[] accountIds) { + if (context == null) return new ParcelableAccount[0]; + final String where = accountIds != null ? Expression.in(new Column(Accounts.ACCOUNT_ID), new RawItemArray(accountIds)).getSQL() : null; final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, Accounts.COLUMNS_NO_CREDENTIALS, where, null, null); - if (cur == null) return new Account[0]; + if (cur == null) return new ParcelableAccount[0]; try { final Indices idx = new Indices(cur); cur.moveToFirst(); - final Account[] names = new Account[cur.getCount()]; + final ParcelableAccount[] names = new ParcelableAccount[cur.getCount()]; while (!cur.isAfterLast()) { - names[cur.getPosition()] = new Account(cur, idx); + names[cur.getPosition()] = new ParcelableAccount(cur, idx); cur.moveToNext(); } return names; @@ -174,14 +174,14 @@ public class Account implements Parcelable { } } - public static List getAccountsList(final Context context, final boolean activatedOnly) { + public static List getAccountsList(final Context context, final boolean activatedOnly) { return getAccountsList(context, activatedOnly, false); } - public static List getAccountsList(final Context context, final boolean activatedOnly, + public static List getAccountsList(final Context context, final boolean activatedOnly, final boolean officialKeyOnly) { if (context == null) return Collections.emptyList(); - final ArrayList accounts = new ArrayList<>(); + final ArrayList accounts = new ArrayList<>(); final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, Accounts.COLUMNS_NO_CREDENTIALS, activatedOnly ? Accounts.IS_ACTIVATED + " = 1" : null, null, Accounts.SORT_POSITION); @@ -190,13 +190,13 @@ public class Account implements Parcelable { cur.moveToFirst(); while (!cur.isAfterLast()) { if (!officialKeyOnly) { - accounts.add(new Account(cur, indices)); + accounts.add(new ParcelableAccount(cur, indices)); } else { final String consumerKey = cur.getString(indices.consumer_key); final String consumerSecret = cur.getString(indices.consumer_secret); if (shouldForceUsingPrivateAPIs(context) || isOfficialConsumerKeySecret(context, consumerKey, consumerSecret)) { - accounts.add(new Account(cur, indices)); + accounts.add(new ParcelableAccount(cur, indices)); } } cur.moveToNext(); @@ -205,7 +205,7 @@ public class Account implements Parcelable { return accounts; } - public static AccountWithCredentials getAccountWithCredentials(final Context context, final long account_id) { + public static ParcelableAccountWithCredentials getAccountWithCredentials(final Context context, final long account_id) { if (context == null) return null; final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, Accounts.COLUMNS, Accounts.ACCOUNT_ID + " = " + account_id, null, null); @@ -214,7 +214,7 @@ public class Account implements Parcelable { if (cur.getCount() > 0 && cur.moveToFirst()) { final Indices indices = new Indices(cur); cur.moveToFirst(); - return new AccountWithCredentials(cur, indices); + return new ParcelableAccountWithCredentials(cur, indices); } } finally { cur.close(); @@ -223,7 +223,7 @@ public class Account implements Parcelable { return null; } - public static class AccountWithCredentials extends Account { + public static class ParcelableAccountWithCredentials extends ParcelableAccount { public final int auth_type; public final String consumer_key, consumer_secret; @@ -232,7 +232,7 @@ public class Account implements Parcelable { public final String api_url_format; public final boolean same_oauth_signing_url, no_version_suffix; - public AccountWithCredentials(final Cursor cursor, final Indices indices) { + public ParcelableAccountWithCredentials(final Cursor cursor, final Indices indices) { super(cursor, indices); auth_type = cursor.getInt(indices.auth_type); consumer_key = cursor.getString(indices.consumer_key); @@ -254,7 +254,7 @@ public class Account implements Parcelable { + ", api_url_format=" + api_url_format + ", same_oauth_signing_url=" + same_oauth_signing_url + "}"; } - public static boolean isOfficialCredentials(final Context context, final AccountWithCredentials account) { + public static boolean isOfficialCredentials(final Context context, final ParcelableAccountWithCredentials account) { if (account == null) return false; final boolean isOAuth = account.auth_type == Accounts.AUTH_TYPE_OAUTH || account.auth_type == Accounts.AUTH_TYPE_XAUTH; diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/ParcelableStatusUpdate.java b/twidere/src/main/java/org/mariotaku/twidere/model/ParcelableStatusUpdate.java index 353036c35..2deac7c2e 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/model/ParcelableStatusUpdate.java +++ b/twidere/src/main/java/org/mariotaku/twidere/model/ParcelableStatusUpdate.java @@ -39,7 +39,7 @@ public class ParcelableStatusUpdate implements Parcelable { } }; - public final Account[] accounts; + public final ParcelableAccount[] accounts; public final ParcelableMediaUpdate[] media; public final String text; public final ParcelableLocation location; @@ -51,7 +51,7 @@ public class ParcelableStatusUpdate implements Parcelable { * ParcelableStatusUpdate.Builder instead. */ @Deprecated - public ParcelableStatusUpdate(final Account[] accounts, final String text, final ParcelableLocation location, + public ParcelableStatusUpdate(final ParcelableAccount[] accounts, final String text, final ParcelableLocation location, final ParcelableMediaUpdate[] media, final long in_reply_to_status_id, final boolean is_possibly_sensitive) { this.accounts = accounts; this.text = text; @@ -62,7 +62,7 @@ public class ParcelableStatusUpdate implements Parcelable { } public ParcelableStatusUpdate(final Context context, final DraftItem draft) { - accounts = Account.getAccounts(context, draft.account_ids); + accounts = ParcelableAccount.getAccounts(context, draft.account_ids); text = draft.text; location = draft.location; media = draft.media; @@ -71,7 +71,7 @@ public class ParcelableStatusUpdate implements Parcelable { } public ParcelableStatusUpdate(final Parcel in) { - accounts = in.createTypedArray(Account.CREATOR); + accounts = in.createTypedArray(ParcelableAccount.CREATOR); text = in.readString(); location = in.readParcelable(ParcelableLocation.class.getClassLoader()); media = in.createTypedArray(ParcelableMediaUpdate.CREATOR); @@ -103,7 +103,7 @@ public class ParcelableStatusUpdate implements Parcelable { public static final class Builder { - private Account[] accounts; + private ParcelableAccount[] accounts; private String text; private ParcelableLocation location; private ParcelableMediaUpdate[] media; @@ -123,7 +123,7 @@ public class ParcelableStatusUpdate implements Parcelable { isPossiblySensitive(base.is_possibly_sensitive); } - public Builder accounts(final Account[] accounts) { + public Builder accounts(final ParcelableAccount[] accounts) { this.accounts = accounts; return this; } diff --git a/twidere/src/main/java/org/mariotaku/twidere/preference/AccountsListPreference.java b/twidere/src/main/java/org/mariotaku/twidere/preference/AccountsListPreference.java index 55d8371df..e26b98195 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/preference/AccountsListPreference.java +++ b/twidere/src/main/java/org/mariotaku/twidere/preference/AccountsListPreference.java @@ -36,8 +36,6 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.ImageView; -import android.widget.ImageView.ScaleType; import android.widget.Switch; import android.widget.TextView; @@ -47,7 +45,7 @@ import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; import org.mariotaku.twidere.app.TwidereApplication; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.task.AsyncTask; import org.mariotaku.twidere.util.ImageLoaderWrapper; import org.mariotaku.twidere.util.Utils; @@ -76,9 +74,9 @@ public abstract class AccountsListPreference extends PreferenceCategory implemen a.recycle(); } - public void setAccountsData(final List accounts) { + public void setAccountsData(final List accounts) { removeAll(); - for (final Account account : accounts) { + for (final ParcelableAccount account : accounts) { final AccountItemPreference preference = new AccountItemPreference(getContext(), account, mSwitchKey, mSwitchDefault); setupPreference(preference, account); @@ -95,12 +93,12 @@ public abstract class AccountsListPreference extends PreferenceCategory implemen new LoadAccountsTask(this).execute(); } - protected abstract void setupPreference(AccountItemPreference preference, Account account); + protected abstract void setupPreference(AccountItemPreference preference, ParcelableAccount account); public static final class AccountItemPreference extends Preference implements ImageLoadingListener, OnCheckedChangeListener, OnSharedPreferenceChangeListener, OnPreferenceClickListener, OnClickListener { - private final Account mAccount; + private final ParcelableAccount mAccount; private final SharedPreferences mSwitchPreference; private final ImageLoaderWrapper mImageLoader; @@ -108,7 +106,7 @@ public abstract class AccountsListPreference extends PreferenceCategory implemen private final boolean mSwitchDefault; private Switch mToggle; - public AccountItemPreference(final Context context, final Account account, final String switchKey, + public AccountItemPreference(final Context context, final ParcelableAccount account, final String switchKey, final boolean switchDefault) { super(context); setWidgetLayoutResource(R.layout.preference_widget_account_preference_item); @@ -215,7 +213,7 @@ public abstract class AccountsListPreference extends PreferenceCategory implemen } } - private static class LoadAccountsTask extends AsyncTask> { + private static class LoadAccountsTask extends AsyncTask> { private final AccountsListPreference mPreference; @@ -224,12 +222,12 @@ public abstract class AccountsListPreference extends PreferenceCategory implemen } @Override - protected List doInBackground(final Void... params) { - return Account.getAccountsList(mPreference.getContext(), false); + protected List doInBackground(final Void... params) { + return ParcelableAccount.getAccountsList(mPreference.getContext(), false); } @Override - protected void onPostExecute(final List result) { + protected void onPostExecute(final List result) { mPreference.setAccountsData(result); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/preference/AutoRefreshAccountsListPreference.java b/twidere/src/main/java/org/mariotaku/twidere/preference/AutoRefreshAccountsListPreference.java index a6759f223..c060f14eb 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/preference/AutoRefreshAccountsListPreference.java +++ b/twidere/src/main/java/org/mariotaku/twidere/preference/AutoRefreshAccountsListPreference.java @@ -25,7 +25,7 @@ import android.util.AttributeSet; import org.mariotaku.twidere.TwidereConstants; import org.mariotaku.twidere.fragment.AccountRefreshSettingsFragment; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; public class AutoRefreshAccountsListPreference extends AccountsListPreference implements TwidereConstants { @@ -42,7 +42,7 @@ public class AutoRefreshAccountsListPreference extends AccountsListPreference im } @Override - protected void setupPreference(final AccountItemPreference preference, final Account account) { + protected void setupPreference(final AccountItemPreference preference, final ParcelableAccount account) { preference.setFragment(AccountRefreshSettingsFragment.class.getName()); final Bundle args = preference.getExtras(); args.putParcelable(EXTRA_ACCOUNT, account); diff --git a/twidere/src/main/java/org/mariotaku/twidere/preference/NotificationAccountsListPreference.java b/twidere/src/main/java/org/mariotaku/twidere/preference/NotificationAccountsListPreference.java index ba0153019..3529b6846 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/preference/NotificationAccountsListPreference.java +++ b/twidere/src/main/java/org/mariotaku/twidere/preference/NotificationAccountsListPreference.java @@ -25,7 +25,7 @@ import android.util.AttributeSet; import org.mariotaku.twidere.TwidereConstants; import org.mariotaku.twidere.fragment.AccountNotificationSettingsFragment; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; public class NotificationAccountsListPreference extends AccountsListPreference implements TwidereConstants { @@ -42,7 +42,7 @@ public class NotificationAccountsListPreference extends AccountsListPreference i } @Override - protected void setupPreference(final AccountItemPreference preference, final Account account) { + protected void setupPreference(final AccountItemPreference preference, final ParcelableAccount account) { preference.setFragment(AccountNotificationSettingsFragment.class.getName()); final Bundle args = preference.getExtras(); args.putParcelable(EXTRA_ACCOUNT, account); diff --git a/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereCommandProvider.java b/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereCommandProvider.java index b9f30ea61..bf5c1c2c3 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereCommandProvider.java +++ b/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereCommandProvider.java @@ -146,7 +146,7 @@ public class TwidereCommandProvider extends ContentProvider implements Constants case CODE_REFRESH_HOME_TIMELINE: if (mTwitterWrapper.isHomeTimelineRefreshing()) return getEmptyCursor(); case CODE_REFRESH_MENTIONS: - if (mTwitterWrapper.isMentionsRefreshing()) return getEmptyCursor(); + if (mTwitterWrapper.isMentionsTimelineRefreshing()) return getEmptyCursor(); case CODE_REFRESH_INBOX: if (mTwitterWrapper.isReceivedDirectMessagesRefreshing()) return getEmptyCursor(); case CODE_REFRESH_OUTBOX: diff --git a/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java b/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java index 8bbd0fb05..5415e4f31 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java +++ b/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java @@ -591,7 +591,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta isAccountSpecific = true; break; } - case NOTIFICATION_ID_MENTIONS: { + case NOTIFICATION_ID_MENTIONS_TIMELINE: { mNewMentions.clear(); isAccountSpecific = true; break; @@ -889,7 +889,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta private Cursor getNotificationsCursor() { final MatrixCursor c = new MatrixCursor(TweetStore.Notifications.MATRIX_COLUMNS); c.addRow(new Integer[]{NOTIFICATION_ID_HOME_TIMELINE, mUnreadStatuses.size()}); - c.addRow(new Integer[]{NOTIFICATION_ID_MENTIONS, mNewMentions.size()}); + c.addRow(new Integer[]{NOTIFICATION_ID_MENTIONS_TIMELINE, mNewMentions.size()}); c.addRow(new Integer[]{NOTIFICATION_ID_DIRECT_MESSAGES, mNewMessages.size()}); return c; } @@ -898,7 +898,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta final MatrixCursor c = new MatrixCursor(TweetStore.Notifications.MATRIX_COLUMNS); if (id == NOTIFICATION_ID_HOME_TIMELINE) { c.addRow(new Integer[]{id, mNewStatuses.size()}); - } else if (id == NOTIFICATION_ID_MENTIONS) { + } else if (id == NOTIFICATION_ID_MENTIONS_TIMELINE) { c.addRow(new Integer[]{id, mNewMentions.size()}); } else if (id == NOTIFICATION_ID_DIRECT_MESSAGES) { c.addRow(new Integer[]{id, mNewMessages.size()}); @@ -1018,7 +1018,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta if (values == null || values.length == 0) return 0; // Add statuses that not filtered to list for future use. int result = 0; - final boolean enabled = mPreferences.getBoolean(KEY_FILTERS_IN_MENTIONS, true); + final boolean enabled = mPreferences.getBoolean(KEY_FILTERS_IN_MENTIONS_TIMELINE, true); final boolean filtersForRts = mPreferences.getBoolean(KEY_FILTERS_FOR_RTS, true); for (final ContentValues value : values) { final ParcelableStatus status = new ParcelableStatus(value); @@ -1112,12 +1112,12 @@ public final class TwidereDataProvider extends ContentProvider implements Consta if (pref.isMentionsNotificationEnabled()) { final long accountId = pref.getAccountId(); displayStatusesNotification(notifiedCount, pref, pref.getMentionsNotificationType(), - NOTIFICATION_ID_MENTIONS, getStatusesForAccounts(items, accountId), + NOTIFICATION_ID_MENTIONS_TIMELINE, getStatusesForAccounts(items, accountId), R.string.notification_mention, R.string.notification_mention_multiple, R.drawable.ic_stat_mention); } } - notifyUnreadCountChanged(NOTIFICATION_ID_MENTIONS); + notifyUnreadCountChanged(NOTIFICATION_ID_MENTIONS_TIMELINE); break; } case TABLE_ID_DIRECT_MESSAGES_INBOX: { diff --git a/twidere/src/main/java/org/mariotaku/twidere/service/BackgroundOperationService.java b/twidere/src/main/java/org/mariotaku/twidere/service/BackgroundOperationService.java index 2512da855..b44141bab 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/service/BackgroundOperationService.java +++ b/twidere/src/main/java/org/mariotaku/twidere/service/BackgroundOperationService.java @@ -45,7 +45,7 @@ import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.MainActivity; import org.mariotaku.twidere.activity.MainHondaJOJOActivity; import org.mariotaku.twidere.app.TwidereApplication; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.MediaUploadResult; import org.mariotaku.twidere.model.ParcelableDirectMessage; import org.mariotaku.twidere.model.ParcelableLocation; @@ -278,7 +278,7 @@ public class BackgroundOperationService extends IntentService implements Constan final List> result = updateStatus(builder, item); boolean failed = false; Exception exception = null; - final List failed_account_ids = ListUtils.fromArray(Account.getAccountIds(item.accounts)); + final List failed_account_ids = ListUtils.fromArray(ParcelableAccount.getAccountIds(item.accounts)); for (final SingleResponse response : result) { if (response.getData() == null) { @@ -446,7 +446,7 @@ public class BackgroundOperationService extends IntentService implements Constan } } } - for (final Account account : statusUpdate.accounts) { + for (final ParcelableAccount account : statusUpdate.accounts) { final Twitter twitter = getTwitterInstance(this, account.account_id, true, true); final StatusUpdate status = new StatusUpdate(shortenedText); status.setInReplyToStatusId(statusUpdate.in_reply_to_status_id); diff --git a/twidere/src/main/java/org/mariotaku/twidere/service/RefreshService.java b/twidere/src/main/java/org/mariotaku/twidere/service/RefreshService.java index 998db280b..fea937f99 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/service/RefreshService.java +++ b/twidere/src/main/java/org/mariotaku/twidere/service/RefreshService.java @@ -184,7 +184,7 @@ public class RefreshService extends Service implements Constants { } private int getMentions(final long[] accountIds, final long[] maxIds, final long[] sinceIds) { - return mTwitterWrapper.getMentionsAsync(accountIds, maxIds, sinceIds); + return mTwitterWrapper.getMentionsTimelineAsync(accountIds, maxIds, sinceIds); } private int getReceivedDirectMessages(final long[] accountIds, final long[] maxIds, final long[] sinceIds) { @@ -220,7 +220,7 @@ public class RefreshService extends Service implements Constants { } private boolean isMentionsRefreshing() { - return mTwitterWrapper.isMentionsRefreshing(); + return mTwitterWrapper.isMentionsTimelineRefreshing(); } private boolean isReceivedDirectMessagesRefreshing() { diff --git a/twidere/src/main/java/org/mariotaku/twidere/task/CacheUsersStatusesTask.java b/twidere/src/main/java/org/mariotaku/twidere/task/CacheUsersStatusesTask.java index 43e10596b..1c5b6cffa 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/task/CacheUsersStatusesTask.java +++ b/twidere/src/main/java/org/mariotaku/twidere/task/CacheUsersStatusesTask.java @@ -30,7 +30,7 @@ import android.content.Context; import com.twitter.Extractor; -import org.mariotaku.querybuilder.Where; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.provider.TweetStore.CachedHashtags; import org.mariotaku.twidere.provider.TweetStore.CachedStatuses; @@ -84,7 +84,7 @@ public class CacheUsersStatusesTask extends AsyncTask implemen final ContentValues filtered_users_values = new ContentValues(); filtered_users_values.put(Filters.Users.NAME, user.getName()); filtered_users_values.put(Filters.Users.SCREEN_NAME, user.getScreenName()); - final String filtered_users_where = Where.equals(Filters.Users.USER_ID, user.getId()).getSQL(); + final String filtered_users_where = Expression.equals(Filters.Users.USER_ID, user.getId()).getSQL(); resolver.update(Filters.Users.CONTENT_URI, filtered_users_values, filtered_users_where, null); } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java b/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java index a0f02ae9f..1e57616d3 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java @@ -29,11 +29,11 @@ import android.net.Uri; import android.os.Bundle; import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.RawItemArray; -import org.mariotaku.querybuilder.Where; import org.mariotaku.twidere.R; import org.mariotaku.twidere.app.TwidereApplication; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.ListResponse; import org.mariotaku.twidere.model.ParcelableLocation; import org.mariotaku.twidere.model.ParcelableMediaUpdate; @@ -246,7 +246,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper { return mGetLocalTrendsTaskId = mAsyncTaskManager.add(task, true); } - public int getMentionsAsync(final long[] accountIds, final long[] max_ids, final long[] since_ids) { + public int getMentionsTimelineAsync(final long[] accountIds, final long[] max_ids, final long[] since_ids) { mAsyncTaskManager.cancel(mGetMentionsTaskId); final GetMentionsTask task = new GetMentionsTask(accountIds, max_ids, since_ids); return mGetMentionsTaskId = mAsyncTaskManager.add(task, true); @@ -302,7 +302,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper { || mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_STORE_TRENDS); } - public boolean isMentionsRefreshing() { + public boolean isMentionsTimelineRefreshing() { return mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_GET_MENTIONS) || mAsyncTaskManager.hasRunningTasksForTag(TASK_TAG_STORE_MENTIONS); } @@ -325,7 +325,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper { public int refreshAll(final long[] accountIds) { if (mPreferences.getBoolean(KEY_HOME_REFRESH_MENTIONS, HomeRefreshContentPreference.DEFAULT_ENABLE_MENTIONS)) { final long[] sinceIds = getNewestStatusIdsFromDatabase(mContext, Mentions.CONTENT_URI, accountIds); - getMentionsAsync(accountIds, null, sinceIds); + getMentionsTimelineAsync(accountIds, null, sinceIds); } if (mPreferences.getBoolean(KEY_HOME_REFRESH_DIRECT_MESSAGES, HomeRefreshContentPreference.DEFAULT_ENABLE_DIRECT_MESSAGES)) { @@ -398,7 +398,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper { final ParcelableMediaUpdate[] media, final long inReplyToStatusId, final boolean isPossiblySensitive) { final ParcelableStatusUpdate.Builder builder = new ParcelableStatusUpdate.Builder(); - builder.accounts(Account.getAccounts(mContext, accountIds)); + builder.accounts(ParcelableAccount.getAccounts(mContext, accountIds)); builder.text(text); builder.location(location); builder.media(media); @@ -693,15 +693,15 @@ public class AsyncTwitterWrapper extends TwitterWrapper { try { final User user = twitter.createBlock(user_id); for (final Uri uri : STATUSES_URIS) { - final Where where = Where.and(Where.equals(Statuses.ACCOUNT_ID, account_id), - Where.equals(Statuses.USER_ID, user_id)); + final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, account_id), + Expression.equals(Statuses.USER_ID, user_id)); mResolver.delete(uri, where.getSQL(), null); } // I bet you don't want to see this user in your auto // complete // list. - final Where where = Where.equals(CachedUsers.USER_ID, user_id); + final Expression where = Expression.equals(CachedUsers.USER_ID, user_id); mResolver.delete(CachedUsers.CONTENT_URI, where.getSQL(), null); return SingleResponse.getInstance(user, null); } catch (final TwitterException e) { @@ -742,8 +742,8 @@ public class AsyncTwitterWrapper extends TwitterWrapper { if (twitter == null) return SingleResponse.getInstance(); try { final User user = twitter.createMute(mUserId); - final Where where = Where.and(Where.equals(Statuses.ACCOUNT_ID, mAccountId), - Where.equals(Statuses.USER_ID, mUserId)); + final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, mAccountId), + Expression.equals(Statuses.USER_ID, mUserId)); mResolver.delete(Statuses.CONTENT_URI, where.getSQL(), null); return SingleResponse.getInstance(user, null); @@ -788,9 +788,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper { final twitter4j.Status status = twitter.createFavorite(status_id); final ContentValues values = new ContentValues(); values.put(Statuses.IS_FAVORITE, true); - final Where where = Where.and(Where.equals(Statuses.ACCOUNT_ID, account_id), - Where.or(Where.equals(Statuses.STATUS_ID, status_id), - Where.equals(Statuses.RETWEET_ID, status_id))); + final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, account_id), + Expression.or(Expression.equals(Statuses.STATUS_ID, status_id), + Expression.equals(Statuses.RETWEET_ID, status_id))); for (final Uri uri : TweetStore.STATUSES_URIS) { mResolver.update(uri, values, where.getSQL(), null); } @@ -1310,8 +1310,8 @@ public class AsyncTwitterWrapper extends TwitterWrapper { final twitter4j.Status status = twitter.destroyFavorite(status_id); final ContentValues values = new ContentValues(); values.put(Statuses.IS_FAVORITE, 0); - final Where where = Where.and(Where.equals(Statuses.ACCOUNT_ID, account_id), - Where.or(Where.equals(Statuses.STATUS_ID, status_id), Where.equals(Statuses.RETWEET_ID, status_id))); + final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, account_id), + Expression.or(Expression.equals(Statuses.STATUS_ID, status_id), Expression.equals(Statuses.RETWEET_ID, status_id))); for (final Uri uri : TweetStore.STATUSES_URIS) { mResolver.update(uri, values, where.getSQL(), null); } @@ -1366,9 +1366,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper { try { final User user = twitter.destroyFriendship(user_id); // remove user tweets and retweets - final Where where = Where.and(Where.equals(Statuses.ACCOUNT_ID, account_id), - Where.or(Where.equals(Statuses.USER_ID, user_id), - Where.equals(Statuses.RETWEETED_BY_USER_ID, user_id))); + final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, account_id), + Expression.or(Expression.equals(Statuses.USER_ID, user_id), + Expression.equals(Statuses.RETWEETED_BY_USER_ID, user_id))); mResolver.delete(Statuses.CONTENT_URI, where.getSQL(), null); return SingleResponse.getInstance(user, null); } catch (final TwitterException e) { @@ -1943,8 +1943,8 @@ public class AsyncTwitterWrapper extends TwitterWrapper { if (result != null) { final String user_id_where = ListUtils.toString(result.list, ',', false); for (final Uri uri : STATUSES_URIS) { - final Where where = Where.and(Where.equals(Statuses.ACCOUNT_ID, account_id), - new Where(String.format(Locale.ROOT, "%s IN (%s)", Statuses.USER_ID, user_id_where))); + final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, account_id), + new Expression(String.format(Locale.ROOT, "%s IN (%s)", Statuses.USER_ID, user_id_where))); mResolver.delete(uri, where.getSQL(), null); } mMessagesManager.showInfoMessage(R.string.reported_users_for_spam, false); @@ -2043,9 +2043,8 @@ public class AsyncTwitterWrapper extends TwitterWrapper { for (final Uri uri : STATUSES_URIS) { mResolver.update(uri, values, where, null); } - final Intent intent = new Intent(BROADCAST_RETWEET_CHANGED); + final Intent intent = new Intent(BROADCAST_STATUS_RETWEETED); intent.putExtra(EXTRA_STATUS_ID, status_id); - intent.putExtra(EXTRA_RETWEETED, true); mContext.sendBroadcast(intent); mMessagesManager.showOkMessage(R.string.status_retweeted, false); } else { @@ -2090,8 +2089,8 @@ public class AsyncTwitterWrapper extends TwitterWrapper { // Delete all rows conflicting before new data inserted. { - final Where deleteWhere = Where.and(Where.equals(DirectMessages.ACCOUNT_ID, accountId), - Where.in(new Column(DirectMessages.MESSAGE_ID), new RawItemArray(messageIds))); + final Expression deleteWhere = Expression.and(Expression.equals(DirectMessages.ACCOUNT_ID, accountId), + Expression.in(new Column(DirectMessages.MESSAGE_ID), new RawItemArray(messageIds))); final Uri deleteUri = appendQueryParameters(uri, new NameValuePairImpl(QUERY_PARAM_NOTIFY, false)); mResolver.delete(deleteUri, deleteWhere.getSQL(), null); @@ -2211,9 +2210,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper { statusIds[i] = status.getId(); } // Delete all rows conflicting before new data inserted. - final Where accountWhere = Where.equals(Statuses.ACCOUNT_ID, accountId); - final Where statusWhere = Where.in(new Column(Statuses.STATUS_ID), new RawItemArray(statusIds)); - final String deleteWhere = Where.and(accountWhere, statusWhere).getSQL(); + final Expression accountWhere = Expression.equals(Statuses.ACCOUNT_ID, accountId); + final Expression statusWhere = Expression.in(new Column(Statuses.STATUS_ID), new RawItemArray(statusIds)); + final String deleteWhere = Expression.and(accountWhere, statusWhere).getSQL(); final Uri deleteUri = appendQueryParameters(uri, new NameValuePairImpl(QUERY_PARAM_NOTIFY, false)); final int rowsDeleted = mResolver.delete(deleteUri, deleteWhere, null); // UCD @@ -2233,8 +2232,8 @@ public class AsyncTwitterWrapper extends TwitterWrapper { if (insertGap) { final ContentValues gapValue = new ContentValues(); gapValue.put(Statuses.IS_GAP, 1); - final Where where = Where.and(Where.equals(Statuses.ACCOUNT_ID, accountId), - Where.equals(Statuses.STATUS_ID, minId)); + final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, accountId), + Expression.equals(Statuses.STATUS_ID, minId)); final Uri updateUri = appendQueryParameters(uri, new NameValuePairImpl(QUERY_PARAM_NOTIFY, true)); mResolver.update(updateUri, gapValue, where.getSQL(), null); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java b/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java index 9b7283b37..9aba1fdf3 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java @@ -25,7 +25,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.mariotaku.jsonserializer.JSONSerializer; import org.mariotaku.twidere.TwidereConstants; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.ParcelableDirectMessage; import org.mariotaku.twidere.model.ParcelableLocation; import org.mariotaku.twidere.model.ParcelableMedia; @@ -326,7 +326,7 @@ public final class ContentValuesCreator implements TwidereConstants { } public static ContentValues makeStatusDraftContentValues(final ParcelableStatusUpdate status) { - return makeStatusDraftContentValues(status, Account.getAccountIds(status.accounts)); + return makeStatusDraftContentValues(status, ParcelableAccount.getAccountIds(status.accounts)); } public static ContentValues makeStatusDraftContentValues(final ParcelableStatusUpdate status, diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/CustomTabUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/CustomTabUtils.java index fbd0e2f55..5b6d1bef7 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/CustomTabUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/CustomTabUtils.java @@ -36,12 +36,10 @@ import org.mariotaku.twidere.fragment.support.ActivitiesAboutMeFragment; import org.mariotaku.twidere.fragment.support.ActivitiesByFriendsFragment; import org.mariotaku.twidere.fragment.support.DirectMessagesFragment; import org.mariotaku.twidere.fragment.support.HomeTimelineFragment; -import org.mariotaku.twidere.fragment.support.HomeTimelineListFragment; import org.mariotaku.twidere.fragment.support.InvalidTabFragment; import org.mariotaku.twidere.fragment.support.MentionsTimelineFragment; import org.mariotaku.twidere.fragment.support.RetweetsOfMeFragment; import org.mariotaku.twidere.fragment.support.SearchStatusesFragment; -import org.mariotaku.twidere.fragment.support.StaggeredHomeTimelineFragment; import org.mariotaku.twidere.fragment.support.TrendsSuggectionsFragment; import org.mariotaku.twidere.fragment.support.UserFavoritesFragment; import org.mariotaku.twidere.fragment.support.UserListTimelineFragment; @@ -102,10 +100,6 @@ public class CustomTabUtils implements Constants { RetweetsOfMeFragment.class, R.string.retweets_of_me, R.drawable.ic_action_retweet, CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_NONE, 10)); if (Utils.hasStaggeredTimeline()) { - CUSTOM_TABS_CONFIGURATION_MAP.put(TAB_TYPE_STAGGERED_HOME_TIMELINE, new CustomTabConfiguration( - StaggeredHomeTimelineFragment.class, R.string.staggered_home_timeline, - R.drawable.ic_action_view_quilt, CustomTabConfiguration.ACCOUNT_OPTIONAL, - CustomTabConfiguration.FIELD_TYPE_NONE, 11, false)); } CUSTOM_TABS_ICON_NAME_MAP.put("accounts", R.drawable.ic_action_accounts); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/ImageLoaderWrapper.java b/twidere/src/main/java/org/mariotaku/twidere/util/ImageLoaderWrapper.java index e7670ccde..5b03ba7aa 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/ImageLoaderWrapper.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/ImageLoaderWrapper.java @@ -20,7 +20,6 @@ package org.mariotaku.twidere.util; import android.graphics.Bitmap; -import android.text.TextUtils; import android.widget.ImageView; import com.nostra13.universalimageloader.core.DisplayImageOptions; @@ -33,7 +32,7 @@ import org.mariotaku.twidere.R; import org.mariotaku.twidere.util.imageloader.AccountExtra; import org.mariotaku.twidere.util.imageloader.OvalBitmapDisplayer; -import static org.mariotaku.twidere.util.Utils.getBestBannerType; +import static org.mariotaku.twidere.util.Utils.getBestBannerUrl; public class ImageLoaderWrapper implements Constants { @@ -104,10 +103,17 @@ public class ImageLoaderWrapper implements Constants { mImageLoader.displayImage(url, view, b.build(), loadingHandler, loadingHandler); } - public void displayProfileBanner(final ImageView view, final String base_url, final int width) { - final String type = getBestBannerType(width); - final String url = TextUtils.isEmpty(base_url) ? null : base_url + "/" + type; - mImageLoader.displayImage(url, view, mBannerDisplayOptions); + public void displayProfileBanner(final ImageView view, final String url, + final ImageLoadingListener listener) { + mImageLoader.displayImage(url, view, mBannerDisplayOptions, listener); + } + + public void displayProfileBanner(final ImageView view, final String url) { + displayProfileBanner(view, url, null); + } + + public void displayProfileBanner(final ImageView view, final String baseUrl, final int width) { + displayProfileBanner(view, getBestBannerUrl(baseUrl, width)); } public void displayProfileImage(final ImageView view, final String url) { diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/MultiSelectEventHandler.java b/twidere/src/main/java/org/mariotaku/twidere/util/MultiSelectEventHandler.java index 5e2d2e86f..7cec10f2e 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/MultiSelectEventHandler.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/MultiSelectEventHandler.java @@ -36,7 +36,7 @@ import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.support.BaseSupportActivity; import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.menu.AccountActionProvider; -import org.mariotaku.twidere.model.Account; +import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableUser; import org.mariotaku.twidere.provider.TweetStore.Filters; @@ -179,7 +179,7 @@ public class MultiSelectEventHandler implements Constants, ActionMode.Callback, if (item.getGroupId() == AccountActionProvider.MENU_GROUP) { final Intent intent = item.getIntent(); if (intent == null || !intent.hasExtra(EXTRA_ACCOUNT)) return false; - final Account account = intent.getParcelableExtra(EXTRA_ACCOUNT); + final ParcelableAccount account = intent.getParcelableExtra(EXTRA_ACCOUNT); mMultiSelectManager.setAccountId(account.account_id); if (mAccountActionProvider != null) { mAccountActionProvider.setAccountId(account.account_id); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/TwidereQueryBuilder.java b/twidere/src/main/java/org/mariotaku/twidere/util/TwidereQueryBuilder.java index 184c11e2a..393142f23 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/TwidereQueryBuilder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/TwidereQueryBuilder.java @@ -21,11 +21,11 @@ package org.mariotaku.twidere.util; import org.mariotaku.querybuilder.Columns; import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.OrderBy; import org.mariotaku.querybuilder.SQLQueryBuilder; import org.mariotaku.querybuilder.Selectable; import org.mariotaku.querybuilder.Tables; -import org.mariotaku.querybuilder.Where; import org.mariotaku.querybuilder.query.SQLSelectQuery; import org.mariotaku.twidere.provider.TweetStore.DirectMessages; import org.mariotaku.twidere.provider.TweetStore.DirectMessages.Conversation; @@ -42,16 +42,16 @@ public class TwidereQueryBuilder { final Selectable select = Utils.getColumnsFromProjection(projection); final SQLSelectQuery.Builder qb = SQLQueryBuilder.select(select); qb.from(new Tables(DirectMessages.TABLE_NAME)); - final Where accountIdWhere = Where.equals(DirectMessages.ACCOUNT_ID, account_id); - final Where incomingWhere = Where.and(Where.notEquals(DirectMessages.IS_OUTGOING, 1), - Where.equals(DirectMessages.SENDER_ID, conversationId)); - final Where outgoingWhere = Where.and(Where.equals(DirectMessages.IS_OUTGOING, 1), - Where.equals(DirectMessages.RECIPIENT_ID, conversationId)); - final Where conversationWhere = Where.or(incomingWhere, outgoingWhere); + final Expression accountIdWhere = Expression.equals(DirectMessages.ACCOUNT_ID, account_id); + final Expression incomingWhere = Expression.and(Expression.notEquals(DirectMessages.IS_OUTGOING, 1), + Expression.equals(DirectMessages.SENDER_ID, conversationId)); + final Expression outgoingWhere = Expression.and(Expression.equals(DirectMessages.IS_OUTGOING, 1), + Expression.equals(DirectMessages.RECIPIENT_ID, conversationId)); + final Expression conversationWhere = Expression.or(incomingWhere, outgoingWhere); if (selection != null) { - qb.where(Where.and(accountIdWhere, conversationWhere, new Where(selection))); + qb.where(Expression.and(accountIdWhere, conversationWhere, new Expression(selection))); } else { - qb.where(Where.and(accountIdWhere, conversationWhere)); + qb.where(Expression.and(accountIdWhere, conversationWhere)); } qb.orderBy(new OrderBy(sortOrder != null ? sortOrder : Conversation.DEFAULT_SORT_ORDER)); return qb.build().getSQL(); @@ -63,15 +63,15 @@ public class TwidereQueryBuilder { final SQLSelectQuery.Builder qb = SQLQueryBuilder.select(select); qb.select(select); qb.from(new Tables(DirectMessages.TABLE_NAME)); - final Where accountIdWhere = Where.equals(DirectMessages.ACCOUNT_ID, account_id); - final Where incomingWhere = Where.and(Where.notEquals(DirectMessages.IS_OUTGOING, 1), - Where.equals(new Column(DirectMessages.SENDER_SCREEN_NAME), screen_name)); - final Where outgoingWhere = Where.and(Where.equals(DirectMessages.IS_OUTGOING, 1), - Where.equals(new Column(DirectMessages.RECIPIENT_SCREEN_NAME), screen_name)); + final Expression accountIdWhere = Expression.equals(DirectMessages.ACCOUNT_ID, account_id); + final Expression incomingWhere = Expression.and(Expression.notEquals(DirectMessages.IS_OUTGOING, 1), + Expression.equals(new Column(DirectMessages.SENDER_SCREEN_NAME), screen_name)); + final Expression outgoingWhere = Expression.and(Expression.equals(DirectMessages.IS_OUTGOING, 1), + Expression.equals(new Column(DirectMessages.RECIPIENT_SCREEN_NAME), screen_name)); if (selection != null) { - qb.where(Where.and(accountIdWhere, incomingWhere, outgoingWhere, new Where(selection))); + qb.where(Expression.and(accountIdWhere, incomingWhere, outgoingWhere, new Expression(selection))); } else { - qb.where(Where.and(accountIdWhere, incomingWhere, outgoingWhere)); + qb.where(Expression.and(accountIdWhere, incomingWhere, outgoingWhere)); } qb.orderBy(new OrderBy(sortOrder != null ? sortOrder : Conversation.DEFAULT_SORT_ORDER)); return qb.build().getSQL(); @@ -127,20 +127,20 @@ public class TwidereQueryBuilder { conversationIds.select(new Columns(new Column(DirectMessages.MESSAGE_ID), new Column( DirectMessages.SENDER_ID, ConversationEntries.CONVERSATION_ID))); conversationIds.from(new Tables(Inbox.TABLE_NAME)); - conversationIds.where(Where.in(new Column(DirectMessages.MESSAGE_ID), recent_inbox_msg_ids.build())); + conversationIds.where(Expression.in(new Column(DirectMessages.MESSAGE_ID), recent_inbox_msg_ids.build())); conversationIds.union(); conversationIds.select(new Columns(new Column(DirectMessages.MESSAGE_ID), new Column( DirectMessages.RECIPIENT_ID, ConversationEntries.CONVERSATION_ID))); conversationIds.from(new Tables(Outbox.TABLE_NAME)); - conversationIds.where(Where.in(new Column(DirectMessages.MESSAGE_ID), recent_outbox_msg_ids.build())); + conversationIds.where(Expression.in(new Column(DirectMessages.MESSAGE_ID), recent_outbox_msg_ids.build())); final SQLSelectQuery.Builder groupedConversationIds = new SQLSelectQuery.Builder(); groupedConversationIds.select(new Column(DirectMessages.MESSAGE_ID)); groupedConversationIds.from(conversationIds.build()); groupedConversationIds.groupBy(new Column(ConversationEntries.CONVERSATION_ID)); - final Where groupedWhere = Where.in(new Column(DirectMessages.MESSAGE_ID), groupedConversationIds.build()); - final Where where; + final Expression groupedWhere = Expression.in(new Column(DirectMessages.MESSAGE_ID), groupedConversationIds.build()); + final Expression where; if (selection != null) { - where = Where.and(groupedWhere, new Where(selection)); + where = Expression.and(groupedWhere, new Expression(selection)); } else { where = groupedWhere; } @@ -164,12 +164,12 @@ public class TwidereQueryBuilder { final Selectable select = Utils.getColumnsFromProjection(projection); qb.select(select).from(new Tables(DirectMessages.Inbox.TABLE_NAME)); if (selection != null) { - qb.where(new Where(selection)); + qb.where(new Expression(selection)); } qb.union(); qb.select(select).from(new Tables(DirectMessages.Outbox.TABLE_NAME)); if (selection != null) { - qb.where(new Where(selection)); + qb.where(new Expression(selection)); } qb.orderBy(new OrderBy(sortOrder != null ? sortOrder : DirectMessages.DEFAULT_SORT_ORDER)); return qb.build(); 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 81cb98357..78a56586d 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java @@ -102,8 +102,6 @@ import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; -import com.etsy.android.grid.StaggeredGridView; - import org.apache.http.NameValuePair; import org.json.JSONException; import org.mariotaku.gallery3d.ImageViewerGLActivity; @@ -112,11 +110,13 @@ import org.mariotaku.menucomponent.internal.menu.MenuUtils; import org.mariotaku.querybuilder.AllColumns; import org.mariotaku.querybuilder.Columns; import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.OrderBy; import org.mariotaku.querybuilder.RawItemArray; +import org.mariotaku.querybuilder.SQLQueryBuilder; import org.mariotaku.querybuilder.Selectable; +import org.mariotaku.querybuilder.Table; import org.mariotaku.querybuilder.Tables; -import org.mariotaku.querybuilder.Where; import org.mariotaku.querybuilder.query.SQLSelectQuery; import org.mariotaku.refreshnow.widget.RefreshNowListView; import org.mariotaku.twidere.BuildConfig; @@ -154,9 +154,9 @@ import org.mariotaku.twidere.fragment.support.UserMentionsFragment; import org.mariotaku.twidere.fragment.support.UserTimelineFragment; import org.mariotaku.twidere.fragment.support.UsersListFragment; import org.mariotaku.twidere.graphic.PaddingDrawable; -import org.mariotaku.twidere.model.Account; -import org.mariotaku.twidere.model.Account.AccountWithCredentials; import org.mariotaku.twidere.model.AccountPreferences; +import org.mariotaku.twidere.model.ParcelableAccount; +import org.mariotaku.twidere.model.ParcelableAccount.ParcelableAccountWithCredentials; import org.mariotaku.twidere.model.ParcelableDirectMessage; import org.mariotaku.twidere.model.ParcelableLocation; import org.mariotaku.twidere.model.ParcelableStatus; @@ -469,10 +469,10 @@ public final class Utils implements Constants, TwitterConstants { public static String buildActivatedStatsWhereClause(final Context context, final String selection) { if (context == null) return null; final long[] account_ids = getActivatedAccountIds(context); - final Where accountWhere = Where.in(new Column(Statuses.ACCOUNT_ID), new RawItemArray(account_ids)); - final Where where; + final Expression accountWhere = Expression.in(new Column(Statuses.ACCOUNT_ID), new RawItemArray(account_ids)); + final Expression where; if (selection != null) { - where = Where.and(accountWhere, new Where(selection)); + where = Expression.and(accountWhere, new Expression(selection)); } else { where = accountWhere; } @@ -489,50 +489,48 @@ public final class Utils implements Constants, TwitterConstants { return builder.build(); } - public static String buildStatusFilterWhereClause(final String table, final String selection, - final boolean enableInRts) { + public static Expression buildStatusFilterWhereClause(final String table, final Expression extraSelection, + final boolean enableInRts) { if (table == null) return null; - final StringBuilder builder = new StringBuilder(); - if (selection != null) { - builder.append(selection); - builder.append(" AND "); - } - builder.append(Statuses._ID + " NOT IN ( "); - builder.append("SELECT DISTINCT " + table + "." + Statuses._ID + " FROM " + table); - builder.append(" WHERE " + table + "." + Statuses.USER_ID + " IN ( SELECT " + Filters.Users.TABLE_NAME + "." - + Filters.Users.USER_ID + " FROM " + Filters.Users.TABLE_NAME + " )"); + final SQLSelectQuery filteredUsersQuery = SQLQueryBuilder + .select(new Column(new Table(Filters.Users.TABLE_NAME), Filters.Users.USER_ID)) + .from(new Tables(Filters.Users.TABLE_NAME)) + .build(); + final Expression filteredUsersWhere; if (enableInRts) { - builder.append(" OR " + table + "." + Statuses.RETWEETED_BY_USER_ID + " IN ( SELECT " - + Filters.Users.TABLE_NAME + "." + Filters.Users.USER_ID + " FROM " + Filters.Users.TABLE_NAME - + " )"); + filteredUsersWhere = Expression.or( + Expression.in(new Column(new Table(table), Statuses.USER_ID), filteredUsersQuery), + Expression.in(new Column(new Table(table), Statuses.RETWEETED_BY_USER_ID), filteredUsersQuery)); + } else { + filteredUsersWhere = Expression.in(new Column(new Table(table), Statuses.USER_ID), filteredUsersQuery); } - builder.append(" AND " + table + "." + Statuses.IS_GAP + " IS NULL"); - builder.append(" OR " + table + "." + Statuses.IS_GAP + " == 0"); - builder.append(" UNION "); - builder.append("SELECT DISTINCT " + table + "." + Statuses._ID + " FROM " + table + ", " - + Filters.Sources.TABLE_NAME); - builder.append(" WHERE " + table + "." + Statuses.SOURCE + " LIKE '%>'||" + Filters.Sources.TABLE_NAME + "." - + Filters.Sources.VALUE + "||'%'"); - builder.append(" AND " + table + "." + Statuses.IS_GAP + " IS NULL"); - builder.append(" OR " + table + "." + Statuses.IS_GAP + " == 0"); - builder.append(" UNION "); - builder.append("SELECT DISTINCT " + table + "." + Statuses._ID + " FROM " + table + ", " - + Filters.Keywords.TABLE_NAME); - builder.append(" WHERE " + table + "." + Statuses.TEXT_PLAIN + " LIKE '%'||" + Filters.Keywords.TABLE_NAME - + "." + Filters.Keywords.VALUE + "||'%'"); - builder.append(" AND " + table + "." + Statuses.IS_GAP + " IS NULL"); - builder.append(" OR " + table + "." + Statuses.IS_GAP + " == 0"); - builder.append(" UNION "); - builder.append("SELECT DISTINCT " + table + "." + Statuses._ID + " FROM " + table + ", " - + Filters.Links.TABLE_NAME); - builder.append(" WHERE " + table + "." + Statuses.TEXT_HTML + " LIKE '%%'"); - builder.append(" OR " + table + "." + Statuses.TEXT_HTML + " LIKE '%>%'||" + Filters.Links.TABLE_NAME + "." - + Filters.Links.VALUE + "||'%%'"); - builder.append(" AND " + table + "." + Statuses.IS_GAP + " IS NULL"); - builder.append(" OR " + table + "." + Statuses.IS_GAP + " == 0"); - builder.append(" )"); - return builder.toString(); + final SQLSelectQuery.Builder filteredIdsQueryBuilder = SQLQueryBuilder + .select(true, new Column(new Table(table), Statuses._ID)) + .from(new Tables(table)) + .where(filteredUsersWhere) + .union() + .select(true, new Columns(new Column(new Table(table), Statuses._ID))) + .from(new Tables(table, Filters.Sources.TABLE_NAME)) + .where(Expression.like(new Column(new Table(table), Statuses.SOURCE), + "%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'%")) + .union() + .select(true, new Columns(new Column(new Table(table), Statuses._ID))) + .from(new Tables(table, Filters.Keywords.TABLE_NAME)) + .where(Expression.like(new Column(new Table(table), Statuses.TEXT_PLAIN), + "%'||" + Filters.Keywords.TABLE_NAME + "." + Filters.Keywords.VALUE + "||'%")) + .union() + .select(true, new Columns(new Column(new Table(table), Statuses._ID))) + .from(new Tables(table, Filters.Links.TABLE_NAME)) + .where(Expression.like(new Column(new Table(table), Statuses.SOURCE), + "%>%'||" + Filters.Links.TABLE_NAME + "." + Filters.Links.VALUE + "||'%%")); + final Expression filterExpression = Expression.or( + Expression.notIn(new Column(new Table(table), Statuses._ID), filteredIdsQueryBuilder.build()), + Expression.equals(new Column(new Table(table), Statuses.IS_GAP), 1) + ); + if (extraSelection != null) { + return Expression.and(filterExpression, extraSelection); + } + return filterExpression; } public static int calculateInSampleSize(final int width, final int height, final int preferredWidth, @@ -569,24 +567,24 @@ public final class Utils implements Constants, TwitterConstants { continue; } final String table = getTableNameByUri(uri); - final Where account_where = new Where(Statuses.ACCOUNT_ID + " = " + account_id); + final Expression account_where = new Expression(Statuses.ACCOUNT_ID + " = " + account_id); final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder(); qb.select(new Column(Statuses._ID)).from(new Tables(table)); - qb.where(new Where(Statuses.ACCOUNT_ID + " = " + account_id)); + qb.where(new Expression(Statuses.ACCOUNT_ID + " = " + account_id)); qb.orderBy(new OrderBy(Statuses.STATUS_ID + " DESC")); qb.limit(itemLimit); - final Where where = Where.and(Where.notIn(new Column(Statuses._ID), qb.build()), account_where); + final Expression where = Expression.and(Expression.notIn(new Column(Statuses._ID), qb.build()), account_where); resolver.delete(uri, where.getSQL(), null); } for (final Uri uri : DIRECT_MESSAGES_URIS) { final String table = getTableNameByUri(uri); - final Where account_where = new Where(DirectMessages.ACCOUNT_ID + " = " + account_id); + final Expression account_where = new Expression(DirectMessages.ACCOUNT_ID + " = " + account_id); final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder(); qb.select(new Column(DirectMessages._ID)).from(new Tables(table)); - qb.where(new Where(DirectMessages.ACCOUNT_ID + " = " + account_id)); + qb.where(new Expression(DirectMessages.ACCOUNT_ID + " = " + account_id)); qb.orderBy(new OrderBy(DirectMessages.MESSAGE_ID + " DESC")); qb.limit(itemLimit); - final Where where = Where.and(Where.notIn(new Column(DirectMessages._ID), qb.build()), account_where); + final Expression where = Expression.and(Expression.notIn(new Column(DirectMessages._ID), qb.build()), account_where); resolver.delete(uri, where.getSQL(), null); } } @@ -598,7 +596,7 @@ public final class Utils implements Constants, TwitterConstants { qb.from(new Tables(table)); qb.orderBy(new OrderBy(BaseColumns._ID + " DESC")); qb.limit(itemLimit * 20); - final Where where = Where.notIn(new Column(BaseColumns._ID), qb.build()); + final Expression where = Expression.notIn(new Column(BaseColumns._ID), qb.build()); resolver.delete(uri, where.getSQL(), null); } } @@ -632,20 +630,6 @@ public final class Utils implements Constants, TwitterConstants { // Utils.scrollListToPosition(view, position, offset); } - public static void clearListViewChoices(final StaggeredGridView view) { - if (view == null) return; - final ListAdapter adapter = view.getAdapter(); - if (adapter == null) return; - view.clearChoices(); - view.setChoiceMode(AbsListView.CHOICE_MODE_NONE); - view.invalidateViews(); - // Workaround for Android bug - // http://stackoverflow.com/questions/9754170/listview-selection-remains-persistent-after-exiting-choice-mode - // final int position = view.getFirstVisiblePosition(); - // view.setAdapter(adapter); - // Utils.scrollListToPosition(view, position); - } - public static boolean closeSilently(final Closeable c) { if (c == null) return false; try { @@ -1313,7 +1297,7 @@ public final class Utils implements Constants, TwitterConstants { public static int[] getAccountColors(final Context context, final long[] accountIds) { if (context == null || accountIds == null) return new int[0]; final String[] cols = new String[]{Accounts.ACCOUNT_ID, Accounts.COLOR}; - final String where = Where.in(new Column(Accounts.ACCOUNT_ID), new RawItemArray(accountIds)).getSQL(); + final String where = Expression.in(new Column(Accounts.ACCOUNT_ID), new RawItemArray(accountIds)).getSQL(); final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, cols, where, null, null); if (cur == null) return new int[0]; @@ -1398,7 +1382,7 @@ public final class Utils implements Constants, TwitterConstants { public static String[] getAccountNames(final Context context, final long[] accountIds) { if (context == null) return new String[0]; final String[] cols = new String[]{Accounts.NAME}; - final String where = accountIds != null ? Where.in(new Column(Accounts.ACCOUNT_ID), + final String where = accountIds != null ? Expression.in(new Column(Accounts.ACCOUNT_ID), new RawItemArray(accountIds)).getSQL() : null; final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, cols, where, null, null); @@ -1456,7 +1440,7 @@ public final class Utils implements Constants, TwitterConstants { final boolean includeAtChar) { if (context == null) return new String[0]; final String[] cols = new String[]{Accounts.SCREEN_NAME}; - final String where = accountIds != null ? Where.in(new Column(Accounts.ACCOUNT_ID), + final String where = accountIds != null ? Expression.in(new Column(Accounts.ACCOUNT_ID), new RawItemArray(accountIds)).getSQL() : null; final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, cols, where, null, null); @@ -1498,8 +1482,8 @@ public final class Utils implements Constants, TwitterConstants { if (context == null) return 0; final ContentResolver resolver = context.getContentResolver(); final Cursor cur = ContentResolverUtils.query(resolver, uri, new String[]{Statuses.STATUS_ID}, - buildStatusFilterWhereClause(getTableNameByUri(uri), null, shouldEnableFiltersForRTs(context)), null, - null); + buildStatusFilterWhereClause(getTableNameByUri(uri), null, shouldEnableFiltersForRTs(context)).getSQL(), + null, null); if (cur == null) return 0; try { return cur.getCount(); @@ -1512,8 +1496,8 @@ public final class Utils implements Constants, TwitterConstants { if (context == null) return new long[0]; final ContentResolver resolver = context.getContentResolver(); final Cursor cur = ContentResolverUtils.query(resolver, uri, new String[]{Statuses.STATUS_ID}, - buildStatusFilterWhereClause(getTableNameByUri(uri), null, shouldEnableFiltersForRTs(context)), null, - null); + buildStatusFilterWhereClause(getTableNameByUri(uri), null, shouldEnableFiltersForRTs(context)).getSQL(), + null, null); if (cur == null) return new long[0]; final long[] ids = new long[cur.getCount()]; cur.moveToFirst(); @@ -1552,6 +1536,11 @@ public final class Utils implements Constants, TwitterConstants { return sb.toString(); } + public static String getBestBannerUrl(final String baseUrl, final int width) { + final String type = getBestBannerType(width); + return TextUtils.isEmpty(baseUrl) ? null : baseUrl + "/" + type; + } + public static String getBestBannerType(final int width) { if (width <= 320) return "mobile"; @@ -2288,7 +2277,7 @@ public final class Utils implements Constants, TwitterConstants { return date.getTime(); } - public static Authorization getTwitterAuthorization(final Context context, final AccountWithCredentials account) { + public static Authorization getTwitterAuthorization(final Context context, final ParcelableAccountWithCredentials account) { if (context == null || account == null) return null; switch (account.auth_type) { case Accounts.AUTH_TYPE_OAUTH: @@ -2342,7 +2331,7 @@ public final class Utils implements Constants, TwitterConstants { public static Authorization getTwitterAuthorization(final Context context, final long accountId) { - final String where = Where.equals(new Column(Accounts.ACCOUNT_ID), accountId).getSQL(); + final String where = Expression.equals(new Column(Accounts.ACCOUNT_ID), accountId).getSQL(); final Cursor c = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, Accounts.COLUMNS, where, null, null); if (c == null) return null; @@ -2468,7 +2457,7 @@ public final class Utils implements Constants, TwitterConstants { final boolean enableProxy = prefs.getBoolean(KEY_ENABLE_PROXY, false); // Here I use old consumer key/secret because it's default key for older // versions - final String where = Where.equals(new Column(Accounts.ACCOUNT_ID), accountId).getSQL(); + final String where = Expression.equals(new Column(Accounts.ACCOUNT_ID), accountId).getSQL(); final Cursor c = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, Accounts.COLUMNS, where, null, null); if (c == null) return null; @@ -2596,7 +2585,7 @@ public final class Utils implements Constants, TwitterConstants { Accounts.COLUMNS, null, null, null); if (cur == null) return false; final String[] keySecrets = context.getResources().getStringArray(R.array.values_official_consumer_key_secret); - final Account.Indices indices = new Account.Indices(cur); + final ParcelableAccount.Indices indices = new ParcelableAccount.Indices(cur); cur.moveToFirst(); try { while (!cur.isAfterLast()) { @@ -2827,7 +2816,7 @@ public final class Utils implements Constants, TwitterConstants { public static boolean isOfficialKeyAccount(final Context context, final long accountId) { if (context == null) return false; final String[] projection = {Accounts.CONSUMER_KEY, Accounts.CONSUMER_SECRET}; - final String selection = Where.equals(Accounts.ACCOUNT_ID, accountId).getSQL(); + final String selection = Expression.equals(Accounts.ACCOUNT_ID, accountId).getSQL(); final Cursor c = context.getContentResolver().query(Accounts.CONTENT_URI, projection, selection, null, null); try { if (c.moveToPosition(0)) @@ -3544,8 +3533,8 @@ public final class Utils implements Constants, TwitterConstants { } final MenuItem translate = menu.findItem(MENU_TRANSLATE); if (translate != null) { - final AccountWithCredentials account = Account.getAccountWithCredentials(context, status.account_id); - final boolean isOfficialKey = AccountWithCredentials.isOfficialCredentials(context, account); + final ParcelableAccountWithCredentials account = ParcelableAccount.getAccountWithCredentials(context, status.account_id); + final boolean isOfficialKey = ParcelableAccountWithCredentials.isOfficialCredentials(context, account); setMenuItemAvailability(menu, MENU_TRANSLATE, isOfficialKey); } menu.removeGroup(MENU_GROUP_STATUS_EXTENSION); @@ -3975,7 +3964,7 @@ public final class Utils implements Constants, TwitterConstants { public static boolean isFilteringUser(Context context, long userId) { final ContentResolver cr = context.getContentResolver(); - final Where where = Where.equals(Users.USER_ID, userId); + final Expression where = Expression.equals(Users.USER_ID, userId); final Cursor c = cr.query(Users.CONTENT_URI, new String[0], where.getSQL(), null, null); try { return c.getCount() > 0; @@ -3987,8 +3976,8 @@ public final class Utils implements Constants, TwitterConstants { public static ParcelableUser getUserForConversation(Context context, long accountId, long conversationId) { final ContentResolver cr = context.getContentResolver(); - final Where where = Where.and(Where.equals(ConversationEntries.ACCOUNT_ID, accountId), - Where.equals(ConversationEntries.CONVERSATION_ID, conversationId)); + final Expression where = Expression.and(Expression.equals(ConversationEntries.ACCOUNT_ID, accountId), + Expression.equals(ConversationEntries.CONVERSATION_ID, conversationId)); final Cursor c = cr.query(ConversationEntries.CONTENT_URI, null, where.getSQL(), null, null); try { if (c.moveToFirst()) return ParcelableUser.fromDirectMessageConversationEntry(c); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/content/DatabaseUpgradeHelper.java b/twidere/src/main/java/org/mariotaku/twidere/util/content/DatabaseUpgradeHelper.java index 3f0e505f7..3e768779e 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/content/DatabaseUpgradeHelper.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/content/DatabaseUpgradeHelper.java @@ -19,23 +19,17 @@ package org.mariotaku.twidere.util.content; -import static org.mariotaku.querybuilder.SQLQueryBuilder.alterTable; -import static org.mariotaku.querybuilder.SQLQueryBuilder.createTable; -import static org.mariotaku.querybuilder.SQLQueryBuilder.dropTable; -import static org.mariotaku.querybuilder.SQLQueryBuilder.insertInto; -import static org.mariotaku.querybuilder.SQLQueryBuilder.select; - import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.text.TextUtils; import org.mariotaku.querybuilder.Columns; import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.NewColumn; +import org.mariotaku.querybuilder.OnConflict; import org.mariotaku.querybuilder.Tables; -import org.mariotaku.querybuilder.Where; -import org.mariotaku.querybuilder.query.SQLInsertIntoQuery; -import org.mariotaku.querybuilder.query.SQLInsertIntoQuery.OnConflict; +import org.mariotaku.querybuilder.query.SQLInsertQuery; import org.mariotaku.querybuilder.query.SQLSelectQuery; import org.mariotaku.twidere.util.ArrayUtils; @@ -46,145 +40,151 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import static org.mariotaku.querybuilder.SQLQueryBuilder.alterTable; +import static org.mariotaku.querybuilder.SQLQueryBuilder.createTable; +import static org.mariotaku.querybuilder.SQLQueryBuilder.dropTable; +import static org.mariotaku.querybuilder.SQLQueryBuilder.insertInto; +import static org.mariotaku.querybuilder.SQLQueryBuilder.select; + public final class DatabaseUpgradeHelper { - public static void safeUpgrade(final SQLiteDatabase db, final String table, final String[] newColNames, - final String[] newColTypes, final boolean dropDirectly, final boolean strictMode, - final Map colAliases) { - safeUpgrade(db, table, newColNames, newColTypes, dropDirectly, strictMode, colAliases, OnConflict.REPLACE); - } + public static void safeUpgrade(final SQLiteDatabase db, final String table, final String[] newColNames, + final String[] newColTypes, final boolean dropDirectly, final boolean strictMode, + final Map colAliases) { + safeUpgrade(db, table, newColNames, newColTypes, dropDirectly, strictMode, colAliases, OnConflict.REPLACE); + } - public static void safeUpgrade(final SQLiteDatabase db, final String table, final String[] newColNames, - final String[] newColTypes, final boolean dropDirectly, final boolean strictMode, - final Map colAliases, final OnConflict onConflict) { + public static void safeUpgrade(final SQLiteDatabase db, final String table, final String[] newColNames, + final String[] newColTypes, final boolean dropDirectly, final boolean strictMode, + final Map colAliases, final OnConflict onConflict) { - if (newColNames == null || newColTypes == null || newColNames.length != newColTypes.length) - throw new IllegalArgumentException("Invalid parameters for upgrading table " + table - + ", length of columns and types not match."); + if (newColNames == null || newColTypes == null || newColNames.length != newColTypes.length) + throw new IllegalArgumentException("Invalid parameters for upgrading table " + table + + ", length of columns and types not match."); - // First, create the table if not exists. - final NewColumn[] newCols = NewColumn.createNewColumns(newColNames, newColTypes); - final String createQuery = createTable(true, table).columns(newCols).buildSQL(); - db.execSQL(createQuery); + // First, create the table if not exists. + final NewColumn[] newCols = NewColumn.createNewColumns(newColNames, newColTypes); + final String createQuery = createTable(true, table).columns(newCols).buildSQL(); + db.execSQL(createQuery); - // We need to get all data from old table. - final String[] oldCols = getColumnNames(db, table); - if (strictMode) { - final String oldCreate = getCreateSQL(db, table); - final Map map = getTypeMapByCreateQuery(oldCreate); - boolean differenct = false; - for (final NewColumn newCol : newCols) { - if (!newCol.getType().equalsIgnoreCase(map.get(newCol.getName()))) { - differenct = true; - } - } - if (!differenct) return; - } else if (oldCols == null || ArrayUtils.contentMatch(newColNames, oldCols)) return; - if (dropDirectly) { - db.beginTransaction(); - db.execSQL(dropTable(true, table).getSQL()); - db.execSQL(createQuery); - db.setTransactionSuccessful(); - db.endTransaction(); - return; - } - final String tempTable = String.format(Locale.US, "temp_%s_%d", table, System.currentTimeMillis()); - db.beginTransaction(); - db.execSQL(alterTable(table).renameTo(tempTable).buildSQL()); - db.execSQL(createQuery); - final String[] notNullCols = getNotNullColumns(newCols); - final String insertQuery = createInsertDataQuery(table, tempTable, newColNames, oldCols, colAliases, - notNullCols, onConflict); - if (insertQuery != null) { - db.execSQL(insertQuery); - } - db.execSQL(dropTable(true, tempTable).getSQL()); - db.setTransactionSuccessful(); - db.endTransaction(); - } + // We need to get all data from old table. + final String[] oldCols = getColumnNames(db, table); + if (strictMode) { + final String oldCreate = getCreateSQL(db, table); + final Map map = getTypeMapByCreateQuery(oldCreate); + boolean differenct = false; + for (final NewColumn newCol : newCols) { + if (!newCol.getType().equalsIgnoreCase(map.get(newCol.getName()))) { + differenct = true; + } + } + if (!differenct) return; + } else if (oldCols == null || ArrayUtils.contentMatch(newColNames, oldCols)) return; + if (dropDirectly) { + db.beginTransaction(); + db.execSQL(dropTable(true, table).getSQL()); + db.execSQL(createQuery); + db.setTransactionSuccessful(); + db.endTransaction(); + return; + } + final String tempTable = String.format(Locale.US, "temp_%s_%d", table, System.currentTimeMillis()); + db.beginTransaction(); + db.execSQL(alterTable(table).renameTo(tempTable).buildSQL()); + db.execSQL(createQuery); + final String[] notNullCols = getNotNullColumns(newCols); + final String insertQuery = createInsertDataQuery(table, tempTable, newColNames, oldCols, colAliases, + notNullCols, onConflict); + if (insertQuery != null) { + db.execSQL(insertQuery); + } + db.execSQL(dropTable(true, tempTable).getSQL()); + db.setTransactionSuccessful(); + db.endTransaction(); + } - public static void safeUpgrade(final SQLiteDatabase db, final String table, final String[] newColNames, - final String[] newColTypes, final boolean dropDirectly, final Map colAliases) { - safeUpgrade(db, table, newColNames, newColTypes, dropDirectly, true, colAliases, OnConflict.REPLACE); - } + public static void safeUpgrade(final SQLiteDatabase db, final String table, final String[] newColNames, + final String[] newColTypes, final boolean dropDirectly, final Map colAliases) { + safeUpgrade(db, table, newColNames, newColTypes, dropDirectly, true, colAliases, OnConflict.REPLACE); + } - private static String createInsertDataQuery(final String table, final String tempTable, final String[] newCols, - final String[] oldCols, final Map colAliases, final String[] notNullCols, - final OnConflict onConflict) { - final SQLInsertIntoQuery.Builder qb = insertInto(onConflict, table); - final List newInsertColsList = new ArrayList(); - for (final String newCol : newCols) { - final String oldAliasedCol = colAliases != null ? colAliases.get(newCol) : null; - if (ArrayUtils.contains(oldCols, newCol) || oldAliasedCol != null - && ArrayUtils.contains(oldCols, oldAliasedCol)) { - newInsertColsList.add(newCol); - } - } - final String[] newInsertCols = newInsertColsList.toArray(new String[newInsertColsList.size()]); - if (!ArrayUtils.contains(newInsertCols, notNullCols)) return null; - qb.columns(newInsertCols); - final Columns.Column[] oldDataCols = new Columns.Column[newInsertCols.length]; - for (int i = 0, j = oldDataCols.length; i < j; i++) { - final String newCol = newInsertCols[i]; - final String oldAliasedCol = colAliases != null ? colAliases.get(newCol) : null; - if (oldAliasedCol != null && ArrayUtils.contains(oldCols, oldAliasedCol)) { - oldDataCols[i] = new Columns.Column(oldAliasedCol, newCol); - } else { - oldDataCols[i] = new Columns.Column(newCol); - } - } - final SQLSelectQuery.Builder selectOldBuilder = select(new Columns(oldDataCols)); - selectOldBuilder.from(new Tables(tempTable)); - qb.select(selectOldBuilder.build()); - return qb.buildSQL(); - } + private static String createInsertDataQuery(final String table, final String tempTable, final String[] newCols, + final String[] oldCols, final Map colAliases, final String[] notNullCols, + final OnConflict onConflict) { + final SQLInsertQuery.Builder qb = insertInto(onConflict, table); + final List newInsertColsList = new ArrayList(); + for (final String newCol : newCols) { + final String oldAliasedCol = colAliases != null ? colAliases.get(newCol) : null; + if (ArrayUtils.contains(oldCols, newCol) || oldAliasedCol != null + && ArrayUtils.contains(oldCols, oldAliasedCol)) { + newInsertColsList.add(newCol); + } + } + final String[] newInsertCols = newInsertColsList.toArray(new String[newInsertColsList.size()]); + if (!ArrayUtils.contains(newInsertCols, notNullCols)) return null; + qb.columns(newInsertCols); + final Columns.Column[] oldDataCols = new Columns.Column[newInsertCols.length]; + for (int i = 0, j = oldDataCols.length; i < j; i++) { + final String newCol = newInsertCols[i]; + final String oldAliasedCol = colAliases != null ? colAliases.get(newCol) : null; + if (oldAliasedCol != null && ArrayUtils.contains(oldCols, oldAliasedCol)) { + oldDataCols[i] = new Columns.Column(oldAliasedCol, newCol); + } else { + oldDataCols[i] = new Columns.Column(newCol); + } + } + final SQLSelectQuery.Builder selectOldBuilder = select(new Columns(oldDataCols)); + selectOldBuilder.from(new Tables(tempTable)); + qb.select(selectOldBuilder.build()); + return qb.buildSQL(); + } - private static String[] getColumnNames(final SQLiteDatabase db, final String table) { - final Cursor cur = db.query(table, null, null, null, null, null, null, "1"); - if (cur == null) return null; - try { - return cur.getColumnNames(); - } finally { - cur.close(); - } - } + private static String[] getColumnNames(final SQLiteDatabase db, final String table) { + final Cursor cur = db.query(table, null, null, null, null, null, null, "1"); + if (cur == null) return null; + try { + return cur.getColumnNames(); + } finally { + cur.close(); + } + } - private static String getCreateSQL(final SQLiteDatabase db, final String table) { - final SQLSelectQuery.Builder qb = select(new Column("sql")); - qb.from(new Tables("sqlite_master")); - qb.where(new Where("type = ? AND name = ?")); - final Cursor c = db.rawQuery(qb.buildSQL(), new String[] { "table", table }); - if (c == null) return null; - try { - if (c.moveToFirst()) return c.getString(0); - return null; - } finally { - c.close(); - } - } + private static String getCreateSQL(final SQLiteDatabase db, final String table) { + final SQLSelectQuery.Builder qb = select(new Column("sql")); + qb.from(new Tables("sqlite_master")); + qb.where(new Expression("type = ? AND name = ?")); + final Cursor c = db.rawQuery(qb.buildSQL(), new String[]{"table", table}); + if (c == null) return null; + try { + if (c.moveToFirst()) return c.getString(0); + return null; + } finally { + c.close(); + } + } - private static String[] getNotNullColumns(final NewColumn[] newCols) { - if (newCols == null) return null; - final String[] notNullCols = new String[newCols.length]; - int count = 0; - for (final NewColumn column : newCols) { - if (column.getType().endsWith(" NOT NULL")) { - notNullCols[count++] = column.getName(); - } - } - return ArrayUtils.subArray(notNullCols, 0, count); - } + private static String[] getNotNullColumns(final NewColumn[] newCols) { + if (newCols == null) return null; + final String[] notNullCols = new String[newCols.length]; + int count = 0; + for (final NewColumn column : newCols) { + if (column.getType().endsWith(" NOT NULL")) { + notNullCols[count++] = column.getName(); + } + } + return ArrayUtils.subArray(notNullCols, 0, count); + } - private static Map getTypeMapByCreateQuery(final String query) { - if (TextUtils.isEmpty(query)) return Collections.emptyMap(); - final int start = query.indexOf("("), end = query.lastIndexOf(")"); - if (start < 0 || end < 0) return Collections.emptyMap(); - final HashMap map = new HashMap(); - for (final String segment : query.substring(start + 1, end).split(",")) { - final String trimmed = segment.trim().replaceAll(" +", " "); - final int idx = trimmed.indexOf(" "); - map.put(trimmed.substring(0, idx), trimmed.substring(idx + 1, trimmed.length())); - } - return map; - } + private static Map getTypeMapByCreateQuery(final String query) { + if (TextUtils.isEmpty(query)) return Collections.emptyMap(); + final int start = query.indexOf("("), end = query.lastIndexOf(")"); + if (start < 0 || end < 0) return Collections.emptyMap(); + final HashMap map = new HashMap(); + for (final String segment : query.substring(start + 1, end).split(",")) { + final String trimmed = segment.trim().replaceAll(" +", " "); + final int idx = trimmed.indexOf(" "); + map.put(trimmed.substring(0, idx), trimmed.substring(idx + 1, trimmed.length())); + } + return map; + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/imageloader/TwidereImageDownloader.java b/twidere/src/main/java/org/mariotaku/twidere/util/imageloader/TwidereImageDownloader.java index 9f2b817e5..7979711ab 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/imageloader/TwidereImageDownloader.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/imageloader/TwidereImageDownloader.java @@ -35,8 +35,8 @@ import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; -import org.mariotaku.twidere.model.Account; -import org.mariotaku.twidere.model.Account.AccountWithCredentials; +import org.mariotaku.twidere.model.ParcelableAccount; +import org.mariotaku.twidere.model.ParcelableAccount.ParcelableAccountWithCredentials; import org.mariotaku.twidere.model.ParcelableMedia; import org.mariotaku.twidere.util.MediaPreviewUtils; import org.mariotaku.twidere.util.Utils; @@ -125,10 +125,10 @@ public class TwidereImageDownloader extends BaseImageDownloader implements Const throws IOException, TwitterException { final Uri uri = Uri.parse(uriString); final Authorization auth; - final AccountWithCredentials account; + final ParcelableAccountWithCredentials account; if (isTwitterAuthRequired(uri) && extras instanceof AccountExtra) { final AccountExtra accountExtra = (AccountExtra) extras; - account = Account.getAccountWithCredentials(mContext, accountExtra.account_id); + account = ParcelableAccount.getAccountWithCredentials(mContext, accountExtra.account_id); auth = getTwitterAuthorization(mContext, accountExtra.account_id); } else { account = null; diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/BottomDividerFrameLayout.java b/twidere/src/main/java/org/mariotaku/twidere/view/BottomDividerFrameLayout.java new file mode 100644 index 000000000..b518eed5f --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/view/BottomDividerFrameLayout.java @@ -0,0 +1,71 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2014 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.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +/** + * Created by mariotaku on 14/12/4. + */ +public class BottomDividerFrameLayout extends FrameLayout { + + private final Drawable mDividerDrawable; + + public BottomDividerFrameLayout(Context context) { + this(context, null); + } + + public BottomDividerFrameLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BottomDividerFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setWillNotDraw(false); + TypedArray a = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.divider}); + mDividerDrawable = a.getDrawable(0); + a.recycle(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + final Drawable divider = mDividerDrawable; + if (divider != null) { + final int drawableLeft = 0, drawableRight = drawableLeft + getMeasuredWidth(); + final int drawableBottom = getMeasuredHeight(), drawableTop = drawableBottom - divider.getIntrinsicHeight(); + divider.setBounds(drawableLeft, drawableTop, drawableRight, drawableBottom); + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + final Drawable divider = mDividerDrawable; + if (divider != null) { + divider.draw(canvas); + } + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/RefreshNowStaggeredGridView.java b/twidere/src/main/java/org/mariotaku/twidere/view/RefreshNowStaggeredGridView.java deleted file mode 100644 index decbc51b5..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/view/RefreshNowStaggeredGridView.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.mariotaku.twidere.view; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; - -import com.etsy.android.grid.StaggeredGridView; - -import org.mariotaku.refreshnow.widget.OnRefreshListener; -import org.mariotaku.refreshnow.widget.RefreshMode; -import org.mariotaku.refreshnow.widget.RefreshNowConfig; -import org.mariotaku.refreshnow.widget.iface.IRefreshNowView; - -public class RefreshNowStaggeredGridView extends StaggeredGridView implements IRefreshNowView { - - private final Helper mHelper; - - public RefreshNowStaggeredGridView(final Context context) { - this(context, null); - } - - public RefreshNowStaggeredGridView(final Context context, final AttributeSet attrs) { - this(context, attrs, 0); - } - - public RefreshNowStaggeredGridView(final Context context, final AttributeSet attrs, final int defStyle) { - super(context, attrs, defStyle); - mHelper = new Helper(this, context, attrs, defStyle); - } - - @Override - public RefreshMode getRefreshMode() { - return mHelper.getRefreshMode(); - } - - @Override - public boolean isRefreshing() { - return mHelper.isRefreshing(); - } - - @Override - public boolean onTouchEvent(final MotionEvent ev) { - return super.onTouchEvent(mHelper.processOnTouchEvent(ev)); - } - - @Override - public void setConfig(final RefreshNowConfig config) { - mHelper.setConfig(config); - } - - @Override - public void setFriction(final float friction) { - super.setFriction(friction); - mHelper.setFriction(friction); - } - - @Override - public void setOnRefreshListener(final OnRefreshListener listener) { - mHelper.setOnRefreshListener(listener); - } - - @Override - public void setRefreshComplete() { - mHelper.setRefreshComplete(); - } - - @Override - public void setRefreshIndicatorView(final View view) { - mHelper.setRefreshIndicatorView(view); - } - - @Override - public View getRefreshIndicatorView() { - return mHelper.getRefreshIndicatorView(); - } - - @Override - public void setRefreshing(final boolean refreshing) { - mHelper.setRefreshing(refreshing); - } - - @Override - public void setRefreshMode(final RefreshMode mode) { - mHelper.setRefreshMode(mode); - } - - @Override - protected void onScrollChanged(final int l, final int t, final int oldl, final int oldt) { - super.onScrollChanged(l, t, oldl, oldt); - mHelper.dispatchOnScrollChanged(l, t, oldl, oldt); - } - - @Override - protected boolean overScrollBy(final int deltaX, final int deltaY, final int scrollX, final int scrollY, - final int scrollRangeX, final int scrollRangeY, final int maxOverScrollX, final int maxOverScrollY, - final boolean isTouchEvent) { - return true; - } - -} \ No newline at end of file diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageEntryViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageEntryViewHolder.java index d4aea9ffe..1b2bd2f96 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageEntryViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageEntryViewHolder.java @@ -28,60 +28,52 @@ import android.widget.TextView; import org.mariotaku.twidere.R; import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.view.ShortTimeView; -import org.mariotaku.twidere.view.iface.ICardItemView; -public class DirectMessageEntryViewHolder extends CardViewHolder { +public class DirectMessageEntryViewHolder extends ViewHolder { - public final ImageView profile_image; - public final TextView name, screen_name, text; - public final ShortTimeView time; - public final ICardItemView content; - private float text_size; - private boolean account_color_enabled; - private final boolean is_rtl; + public final ImageView profile_image; + public final TextView name, text; + public final ShortTimeView time; + private float text_size; + private boolean account_color_enabled; + private final boolean is_rtl; - public DirectMessageEntryViewHolder(final View view) { - super(view); - final Context context = view.getContext(); - content = (ICardItemView) findViewById(R.id.content); - profile_image = (ImageView) findViewById(R.id.profile_image); - name = (TextView) findViewById(R.id.name); - screen_name = (TextView) findViewById(R.id.screen_name); - text = (TextView) findViewById(R.id.text); - time = (ShortTimeView) findViewById(R.id.time); - is_rtl = Utils.isRTL(context); - } + public DirectMessageEntryViewHolder(final View view) { + super(view); + final Context context = view.getContext(); + profile_image = (ImageView) findViewById(R.id.profile_image); + name = (TextView) findViewById(R.id.name); + text = (TextView) findViewById(R.id.text); + time = (ShortTimeView) findViewById(R.id.time); + is_rtl = Utils.isRTL(context); + } - public void setAccountColor(final int color) { - content.drawEnd(account_color_enabled ? color : Color.TRANSPARENT); - } + public void setAccountColor(final int color) { +// content.drawEnd(account_color_enabled ? color : Color.TRANSPARENT); + } - public void setAccountColorEnabled(final boolean enabled) { - if (account_color_enabled == enabled) return; - account_color_enabled = enabled; - if (!account_color_enabled) { - content.drawEnd(Color.TRANSPARENT); - } - } + public void setAccountColorEnabled(final boolean enabled) { + if (account_color_enabled == enabled) return; + account_color_enabled = enabled; + if (!account_color_enabled) { +// content.drawEnd(Color.TRANSPARENT); + } + } - public void setIsOutgoing(final boolean is_outgoing) { - if (is_rtl) { - time.setCompoundDrawablesWithIntrinsicBounds(is_outgoing ? R.drawable.ic_indicator_sent : 0, 0, 0, 0); - } else { - time.setCompoundDrawablesWithIntrinsicBounds(0, 0, is_outgoing ? R.drawable.ic_indicator_sent : 0, 0); - } - } + public void setIsOutgoing(final boolean is_outgoing) { + if (is_rtl) { + time.setCompoundDrawablesWithIntrinsicBounds(is_outgoing ? R.drawable.ic_indicator_sent : 0, 0, 0, 0); + } else { + time.setCompoundDrawablesWithIntrinsicBounds(0, 0, is_outgoing ? R.drawable.ic_indicator_sent : 0, 0); + } + } - public void setTextSize(final float text_size) { - if (this.text_size == text_size) return; - this.text_size = text_size; - text.setTextSize(text_size); - name.setTextSize(text_size); - screen_name.setTextSize(text_size * 0.75f); - time.setTextSize(text_size * 0.65f); - } + public void setTextSize(final float text_size) { + if (this.text_size == text_size) return; + this.text_size = text_size; + } - public void setUserColor(final int color) { - content.drawStart(color); - } + public void setUserColor(final int color) { +// content.drawStart(color); + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java index 8653b9151..54d89c1a9 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java @@ -1,7 +1,6 @@ package org.mariotaku.twidere.view.holder; import android.content.Context; -import android.content.Intent; import android.database.Cursor; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; @@ -12,7 +11,6 @@ import android.widget.TextView; import org.mariotaku.twidere.R; import org.mariotaku.twidere.adapter.iface.IStatusesAdapter; -import org.mariotaku.twidere.constant.IntentConstants; import org.mariotaku.twidere.model.ParcelableMedia; import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatus.CursorIndices; @@ -29,9 +27,9 @@ import static org.mariotaku.twidere.util.Utils.getUserTypeIconRes; /** * Created by mariotaku on 14/11/19. */ -public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClickListener { +public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClickListener { - private final IStatusesAdapter adapter; + private final IStatusesAdapter adapter; private final ImageView retweetProfileImageView; private final CircularImageView profileImageView; @@ -44,7 +42,7 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements O private final View mediaPreviewContainer; private final TextView replyCountView, retweetCountView, favoriteCountView; - public StatusViewHolder(IStatusesAdapter adapter, View itemView) { + public StatusViewHolder(IStatusesAdapter adapter, View itemView) { super(itemView); this.adapter = adapter; itemView.findViewById(R.id.item_content).setOnClickListener(this); @@ -70,7 +68,7 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements O itemView.setOnClickListener(this); profileImageView.setOnClickListener(this); mediaPreviewContainer.setOnClickListener(this); - retweetCountView.setOnClickListener(this); + replyCountView.setOnClickListener(this); retweetCountView.setOnClickListener(this); favoriteCountView.setOnClickListener(this); } @@ -280,9 +278,7 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements O @Override public void onClick(View v) { - final Context context = itemView.getContext(); final int position = getPosition(); - final ParcelableStatus status = adapter.getStatus(position); switch (v.getId()) { case R.id.item_content: { adapter.onStatusClick(this, position); @@ -297,10 +293,7 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements O break; } case R.id.reply_count: { - final Intent intent = new Intent(IntentConstants.INTENT_ACTION_REPLY); - intent.setPackage(context.getPackageName()); - intent.putExtra(IntentConstants.EXTRA_STATUS, status); - context.startActivity(intent); + adapter.onItemActionClick(this, v.getId(), position); break; } } diff --git a/twidere/src/main/res/drawable-hdpi/ic_user_type_protected.png b/twidere/src/main/res/drawable-hdpi/ic_user_type_protected.png index afe19e747ac3ac935eebcb63368a0e93dc2c6df3..e83a82f2aaaef0149cf9c8e619dbbce5a5fe109e 100755 GIT binary patch delta 1523 zcmV3$g6vxkfGXrfcbc(H5wo*$iQoFE9a3hAr zm9VK%`2b?l4?++STtX@~M1vqmzz-xMd_av_kl@M^gBvM(TPy+VG}J%(nq*D6AJO!3%g5 z*Hf@PLWsXw6S~y^Abbdf&4rDFB?S?A1@||@mI7p7YkvVn_ zanli^YxSP;JEyZq&Lv74O(J!;orH~XcR6eUKveHoPB8&Md@^cY2b)9XFiww`(it>9$9(9&Z6Hdy6Eww z1OA&}lL28^frzZa03`$9P24Ww(;q?n5+&ruWKT9IG1GM4Y(B1$-lTwUNTT07DSAGq zd`kiF#!Ul&_ySZ`PM4}2W>v99MziLtJgKiCGK)=d>wokLyt2Me( z=LfMRnp(kXle!j2JT)8;RnbCN>Hz?(LPalf<#fpn`+Ti0oWpdHw?rd*iYoU9*w5a_Q^Gt5o?VBgUIarFqHL8sF0Wjl#2Y~oasN@(m9qDfFd)hyA5_%fip!I?r zsrATz3V&jLyj~3a9>D+4NM1}g_fKKEdfL|$+Tc$mCFh<)em4AE(LhAq1K#0N$tTuG zu3hPG{hS3-^q8z#R*6@WyG4TUWCg)z%5<(-@>VILh!^jGN8V+Cap?Q+) zxA{b9Bfgj9=yt$6meo2R2td`hi{ynObLT^TfPeJSQVeh}1djvYzhM_qM_2)vfRZC#5*P`~Fwz_Ks^bCCb8xpSn8WP`^)WuQwdTH7W7UIM_1FaWcV z|9{?11wgkCMy1cgvS1moq98qR7Y{B@Qf{jz62xFttwIj?yhbvTI<`y$}e zRFF3v5TArm?ss+A>%R zC!iJbMfS^5^qQjTz6$_qezi!U0Mx|Y%STFd{&REa7{3+71{8PuI9=qHs=9{+n}5*Z z0S>>e^SN*01JM0ZUG#se1Hf7jI8qIfQsh3+aASprC^YItK*XyDM4xFAhZ-Ac37&(F zc~u=|3_h_os>L8K03vOq6nV=rZbc=e}-(lHPFSvXO5S!`Rxrfd2v27e+;5tcNi!MNU zJ|NWbQuRPOE(hsSMK|-n)V2*EGR8}B=RzfNJFL{(1tqwS;-d>CpWW290Wf(oW6 z*&WzZP6nb3cP9Q8@BGK|!5Id=yhOc<#L^r1lB0|KRW=g#EfD`mu(HAd41nWSygtw1 zlj{t|rz3J*4ErV{7U9$>m28Fj6xDmGjckeP70Va^9I^!94Fhs;Rj>QTdTv Z@Gm?;>Q4jbVcGxy002ovPDHLkV1j|7*g*gQ delta 1021 zcmVXsE6t$D5A?lmxnG7SwulE)k9GaT_2*tupOuf3c)qYgs>6|BD+zkU3S;?>%Z%6 zE;Q|2Y24ZYXPudI&isGpobUT)hAG82PI3K|25`E9=?eT)6@L(y3SBhJT}6T`FuG;| zZcGfYg9JS!7Pa&#aFwfDf_8!1)c0!1$V~}TW;ca0$HjYIOGtD~Wc9rMHLx+YVlWqL`Ketu-$Fk54Fbj& zs%9?p&oClD_~gg1FN*nez<;a}|G2FcfOiNoG8%}8RX&+xB`{x9`*QQ%7p zS3D2siKlA}h+8F2G#NjFUZ%-Lm_v#%qHTPv8&*8^Cw zmtbx2C5x*K;PW5=duu`Nv_QY)L#_7y@qM0KVz)K3%P&l-Y%O8J@%CDP|LsHgmJUT>P zXf)e4fxr=fQ{?q;V20d;O+>I1`2nv2Pz=KQ+Z+2GgW|F#Z!o-)`49C5(lt)?otKtdG=!r?;PL& z6!~bil|C07=jzMh=qz+;nMC<*8!?Oz<52I%uZXJyE!3ahMo7QgcgXXx^BBl{+|*G3 z6MBDhNdpjG9aH{U=p_m}-j04S5#tq}Ms_D8^V57T!RL?Dm{r!8xVd!~rZf0lv(_5G z&EkoaCEW(ZMC*zSL||&CP`H#AHMLs;h|c2?2ua$7dEAS?JOeU}n+obgvJ?u3_iKrd rPSD2hFt9w*08ZBmr6Oe$idF^V84#VCQO#Ht9TrC^H((9@QE zTldZQ&FikaExY?>cb}Lhon*4tyqWKt-}TKbqe0%-AjeNb1b_Z-3l3Repap~wWANRh z=(EtXg1>j-^%(jt##pPJR?Gn-;dhyZ{W zVcu=%rNM>&U510ivQymn_AfCk-rBchvcTKLnggf@t}THdM_&OF-z(x*uLA%tgYtub zpz#OWMQ?3~pbLIMZjDh%2GJz=`Ag@xTV>%*g_`fYE`QjM-Nbsh5S}zfv$GhLjb=`l zMl#?Z0*M1Eb17K>;JujaaJNC8=NftMH=RPdPSOpF3T*dPYfm3R`c|)F*cc~uVe)w( z@txHossuhU1eL97;Qp^VQUh-VXxeb5R~2hcw?-#0_BN}Suq1%pAB%}DeAzAns~gl@ z#0sRP*1YL21Mw8RSOP}!xT5^(;54#g;vSP0<&{$A*JRC~^MkNrG z#DDb=W?4^QcVcN9RnVbv69B#v;0q(bqrTAECQkcWuZ{+(owmbL5P-5e7e$leu?Vce z1Dn@1@xaHI<7*pL5&&bm&3L#^sOi}8wP!gYImDc1el`niEYPIBDhGir0|&+;um^g6 z;k|VR3rS*CpwY$^LU?TTYNlQawJS#7&VNI8$DLYA@lU5a1kMvRAh1Z4!2T26V$Nf~ z@Tjsl5i*mIODc;r1Z)_*A3Og{1E;S$)s*wj63v(~f*k?@nGyT7SQbzwuyg{0k1~4s z9H)pFG#AMSc;piq|>Nmu3E@O z0qCofHJWu@^qPoTVg_uY;8QAOp?{EzloI<}h5o`>?v8en%0RA?1qvdpUxtt}VHpnj zjZzRK(AGgp+?T49h#*mh_xra0&h=4>TwVG!k)f5$=<0v-?1E2Y?*egd^qNYjmWXgCpgZz9Z%RaG|}ax==PI z`0YriFjm%In?pU^P+yR39_mX#;B-m^f+_VwE@*yvBlkmmrF17`L_GLB z6+LvlF0aU|6TDuRhZE^`q3iWi0N$($JTxvT0?`&3RDW>9+NDAL7gLDTp#GCDQUA4L rylpXH2bg5$7$T6&uG#s_ptrz3^0UcL`$dUX00000NkvXXu0mjffCCc~ delta 1439 zcmV;Q1z`I946qB3BYy>VNklRO^dtEo*IUy0x4wTx(^n^=Ia6_Rp}^(l*UiG8Se@3A*S*A8>_52I56Plo(v@ zd*Ac)yl^jFuJ`4f3omOl&d2+l^EpBIbVkTZi;p_h4Q{1EYN+vqfGD)2a?TJD0F{k#oTtc zM7Sq_NXtJ%Um?Q16Oki=+Ky7Z4-kbLA|@pu^BXGk7*;uYFiQx!G)Vdn8FS!H)abYh zDH6D8AOwwG!+%Nb?XJL9mG$v+QWB8WMESmmcu6y`iak9y21Ek-IL~|d zw_+6nMASlvH)sHXxcOl*3||h1aYs)j{-%O6db|`z_A$!y2B=;`xW#D+DFDjmaEi5u zOswo#rzS#Kz@i_m{p|TC6!lJNnq`Tjo#n;@Dj-S%(tqnHH)8>{a*E7X0d--HIoNNO z^ltQ?lVK?dSlATVGzYb>WngaF&4_p`xW+gj!&DLwXox)G@o1mQz}&P)Oxk*Bt^Sq_ zQ%OM1ceKvur&BVpRPDKQ07CpV=(nz4X|89_A7dO_i+;`jy;pg^MTGC8#7H~!kF(7X}wQ&<89AM8cjYG0i zWq)5M!D}4=iam9xy;Ms`yis0L5|GVx{EejuM|udp=)GR(GA8}m3WNYxo!E#l`<+~I zK9&~9ad?vuAW5N21K{Az2qkF%VJ7wbp9F`x|J}5h{bi@k;yBL>p7-Kk1P8iOjOt`k zNOp)fEMS718hSK_wr97qh{G(xKB%^>kbe(uY}k4JN;|=O!4%0)_7JO-(an4vsV+n? zq9Vea&~I~YSLNVH$&61M+w(Q4dVUmOa%I$BnfM_~b{Tmv8{p+7d_CjqRkAV2qIPg( zj`XN`K7NlWeaObPi?$MgVmBhLOh%(J17OcGgmitpb(qONd7j|JRW1 z?=Z;7ZqW5na{Qn97|H2WO6tP*Me9$Gx0(0`;SDzT6I?iwyBU$DFX3;x%C49O!JkZDY7gBgAtF>m}WwG zDv!HnKb!)8qznNIgcq(wQ2*)5Dopv=T{(rM9+^qc>rP^3_8%NkRWtf8CTW^Y!vy!v tz%-Ckft(ef;-%@FGaG^F^cb!R_CI!J4o|I}&QJgV002ovPDHLkV1jassZIa@ diff --git a/twidere/src/main/res/drawable-mdpi/ic_user_type_protected.png b/twidere/src/main/res/drawable-mdpi/ic_user_type_protected.png index 5a956191d4c4c8cabc20cde698e24c4ed5283886..d3854790a4ba7709a7726e435e0930a62dd4c56a 100755 GIT binary patch delta 943 zcmV;g15o_O1;huCBYy*nNklh5F549I9FioEH%Acsy$C( zYN^qGcOL!&HDzR$P|)xTm`nzQ<_W@EfE4=r?yy#x#N_zmToV|ha>yjnU!~yzlf7Jz z5RtWyDC2X5dVfc420)h20N8ynj8w_vRYB5FVj)(DW41YX;Nms}|Ay0n%$Aybo0O+n zNi^b+i7Jmh)r5&iKs0s-;V}XLSVZ=+Jni9PV}~4>tn$!}Wq3`1@hZc;NF*MG^5$r) zRGl^SlnMDN1;!>qr@&W$JyLyfmz1YF4Noal$uE2^41eEjZx|4>sI~5zc0a)h8nad9 zK}+^SZ?CTaX;N`KRoNBU>Ha6a{$1GUv{l%Dxp%W9JSKHL)K+NZ<`4K z*#J0WC2NpFnO4*Sqk3L+8h~ij?#BC*qX_^j0I>e|09?D-rJpk-S}L+b+X3i#^Tf@r z#sEOt0e^ggUZtKN5dAoQiJEa#9Syl^{t7idn( z7E}V0EmR|0n8tqL#L=snf=6A4ulXH&LstNEskQd1Rj6E6$- z^`8fTYwg!`1(1T8&XBT$&cP?Z(PpKgI9Q%;P)KWWn=_^YaKv(zhozRn_jFe{ z@5t-S@$&wh+enxT5VzC80~Q^^dG0nMAhJ(B6l|AQ2dwUL)?9$F1eHf*)WTV!%VD0p z*niSr>I?r&1dI+gcoja$%MAZgbDrewDYv^f^i55|-{=kh7(jJiI&L=}Yr7|bz%2^* zgG8?yQ6e?J$;AywAyLiw7O+KdL((WJ#gF=yfygg1TPUv64o@}(Ut}Tps z$XeolGag|w9ssS~0=VkBFP=G&nUG$-5h&0%tiJ_W1LvPR+A=4^J`nx~_zpW)&M;*F RQ||x(002ovPDHLkV1fa|!14e9 delta 688 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 diff --git a/twidere/src/main/res/drawable-xxhdpi/ic_user_type_protected.png b/twidere/src/main/res/drawable-xxhdpi/ic_user_type_protected.png index 362c91de603c10db2349b390aa3ec5a6f7090ef6..e6f3eb2ef52609a72dd28bf84b678e72fea61704 100755 GIT binary patch delta 3514 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 LNkvXXu0mjfpo4Hq diff --git a/twidere/src/main/res/layout-v21/list_item_preference_header_category.xml b/twidere/src/main/res/layout-v21/list_item_preference_header_category.xml new file mode 100644 index 000000000..6162f21b4 --- /dev/null +++ b/twidere/src/main/res/layout-v21/list_item_preference_header_category.xml @@ -0,0 +1,43 @@ + + + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/layout-v21/list_item_preference_header_item.xml b/twidere/src/main/res/layout-v21/list_item_preference_header_item.xml new file mode 100644 index 000000000..004a74f9f --- /dev/null +++ b/twidere/src/main/res/layout-v21/list_item_preference_header_item.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/layout-v21/list_item_preference_header_space.xml b/twidere/src/main/res/layout-v21/list_item_preference_header_space.xml new file mode 100644 index 000000000..ae8159d8d --- /dev/null +++ b/twidere/src/main/res/layout-v21/list_item_preference_header_space.xml @@ -0,0 +1,23 @@ + + + + diff --git a/twidere/src/main/res/layout/activity_filters.xml b/twidere/src/main/res/layout/activity_filters.xml index 14c3fd6a1..7e14b0f7b 100644 --- a/twidere/src/main/res/layout/activity_filters.xml +++ b/twidere/src/main/res/layout/activity_filters.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" tools:context=".activity.FiltersActivity"> - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/twidere/src/main/res/layout/card_item_message_entry_compact.xml b/twidere/src/main/res/layout/card_item_message_entry_compact.xml deleted file mode 100644 index 3edba8c8c..000000000 --- a/twidere/src/main/res/layout/card_item_message_entry_compact.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/twidere/src/main/res/layout/fragment_content_pages.xml b/twidere/src/main/res/layout/fragment_content_pages.xml index 96b870b8c..c45fa89bc 100644 --- a/twidere/src/main/res/layout/fragment_content_pages.xml +++ b/twidere/src/main/res/layout/fragment_content_pages.xml @@ -30,7 +30,9 @@ android:layout_width="match_parent" android:layout_height="@dimen/element_size_normal" android:textColor="?android:textColorSecondary" - app:pstsTabBackground="?android:selectableItemBackground"/> + app:pstsTabBackground="?android:selectableItemBackground" + app:pstsShouldExpand="true" + app:pstsTabPaddingLeftRight="@dimen/element_spacing_large"/> + android:layout_height="wrap_content" + android:foreground="?android:selectableItemBackground">