From ec6afb12e28990c91e66173fb35cfb308a1627a6 Mon Sep 17 00:00:00 2001 From: nuclearfog Date: Mon, 6 Mar 2023 21:42:36 +0100 Subject: [PATCH] user list bug fix, database fix, async fix --- .../backend/api/twitter/v1/TwitterV1.java | 18 +- .../twidda/backend/async/AsyncExecutor.java | 20 +- .../twidda/backend/async/UsersLoader.java | 13 +- .../twidda/backend/helper/Users.java | 56 +- .../twidda/database/AppDatabase.java | 737 ++++++++++-------- .../twidda/database/DatabaseAdapter.java | 8 +- .../twidda/ui/activities/ProfileActivity.java | 9 +- .../twidda/ui/adapter/UserAdapter.java | 49 +- .../twidda/ui/fragments/UserFragment.java | 41 +- 9 files changed, 522 insertions(+), 429 deletions(-) diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/TwitterV1.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/TwitterV1.java index a675bbed..1b5402a7 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/TwitterV1.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/TwitterV1.java @@ -316,7 +316,8 @@ public class TwitterV1 implements Connection { public Users getFollowing(long id, long cursor) throws TwitterException { List params = new ArrayList<>(); params.add("user_id=" + id); - params.add("cursor=" + cursor); + if (cursor != 0L) + params.add("cursor=" + cursor); return getUsers(USERS_FOLLOWING, params); } @@ -325,7 +326,8 @@ public class TwitterV1 implements Connection { public Users getFollower(long id, long cursor) throws TwitterException { List params = new ArrayList<>(); params.add("user_id=" + id); - params.add("cursor=" + cursor); + if (cursor != 0L) + params.add("cursor=" + cursor); return getUsers(USERS_FOLLOWER, params); } @@ -334,7 +336,8 @@ public class TwitterV1 implements Connection { public Users getListMember(long id, long cursor) throws TwitterException { List params = new ArrayList<>(); params.add("list_id=" + id); - params.add("cursor=" + cursor); + if (cursor != 0L) + params.add("cursor=" + cursor); Users result = getUsers(USERS_LIST_MEMBER, params); // fix API returns zero previous_cursor when the end of the list is reached // override previous cursor @@ -350,7 +353,8 @@ public class TwitterV1 implements Connection { public Users getListSubscriber(long id, long cursor) throws TwitterException { List params = new ArrayList<>(); params.add("list_id=" + id); - params.add("cursor=" + cursor); + if (cursor != 0L) + params.add("cursor=" + cursor); Users result = getUsers(USERS_LIST_SUBSCRIBER, params); // fix API returns zero previous_cursor when the end of the list is reached // override previous cursor @@ -365,7 +369,8 @@ public class TwitterV1 implements Connection { @Override public Users getBlockedUsers(long cursor) throws TwitterException { List params = new ArrayList<>(); - params.add("cursor=" + cursor); + if (cursor != 0L) + params.add("cursor=" + cursor); return getUsers(USERS_BLOCKED_LIST, params); } @@ -373,7 +378,8 @@ public class TwitterV1 implements Connection { @Override public Users getMutedUsers(long cursor) throws TwitterException { List params = new ArrayList<>(); - params.add("cursor=" + cursor); + if (cursor != 0L) + params.add("cursor=" + cursor); return getUsers(USERS_MUTES, params); } diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/async/AsyncExecutor.java b/app/src/main/java/org/nuclearfog/twidda/backend/async/AsyncExecutor.java index c963059c..f5281084 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/async/AsyncExecutor.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/async/AsyncExecutor.java @@ -23,11 +23,21 @@ public abstract class AsyncExecutor { /** * maximum task count to run in the background */ - private static final int N_THREAD = 2; + private static final int N_THREAD = 4; - private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(N_THREAD); + /** + * thread pool executor + */ + private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(N_THREAD); - private Handler uiHandler = new Handler(Looper.getMainLooper()); + /** + * handler used to send result back to activity/fragment + */ + private static final Handler UI_HANDLER = new Handler(Looper.getMainLooper()); + + /** + * callback to activity/fragment + */ private WeakReference> callback; /** @@ -43,7 +53,7 @@ public abstract class AsyncExecutor { */ public final void execute(final Parameter parameter, @Nullable AsyncCallback callback) { this.callback = new WeakReference<>(callback); - Future future = EXECUTOR.submit(new Runnable() { + Future future = THREAD_POOL.submit(new Runnable() { @Override public void run() { Result result = doInBackground(parameter); @@ -78,7 +88,7 @@ public abstract class AsyncExecutor { * @param result result of the background task */ private void onPostExecute(final Result result) { - uiHandler.post(new Runnable() { + UI_HANDLER.post(new Runnable() { @Override public void run() { if (!queue.isEmpty()) diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/async/UsersLoader.java b/app/src/main/java/org/nuclearfog/twidda/backend/async/UsersLoader.java index 9a5e17e9..74bea4cb 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/async/UsersLoader.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/async/UsersLoader.java @@ -80,11 +80,11 @@ public class UsersLoader extends AsyncExecutor { return super.get(index); } - /** - * check if list is linked to a previous list - * - * @return true if list is linked - */ - public boolean hasPrevious() { - return prevCursor != 0; - } - - /** - * check if list has a successor - * - * @return true if list has a successor - */ - public boolean hasNext() { - return nextCursor != 0; - } - /** * get next link to a list * @@ -64,18 +46,6 @@ public class Users extends LinkedList { return nextCursor; } - /** - * replace whole list including cursors - * - * @param list new list - */ - public void replace(Users list) { - super.clear(); - super.addAll(list); - prevCursor = list.prevCursor; - nextCursor = list.nextCursor; - } - /** * set previous cursor * @@ -94,17 +64,37 @@ public class Users extends LinkedList { this.nextCursor = nextCursor; } + /** + * replace whole list including cursors + * + * @param list new list + */ + public void replaceAll(Users list) { + super.clear(); + super.addAll(list); + prevCursor = list.prevCursor; + nextCursor = list.nextCursor; + } + /** * add a sublist at the bottom of this list including next cursor * * @param list new sublist * @param index index of the sub list */ - public void addAt(Users list, int index) { - super.addAll(index, list); - nextCursor = list.nextCursor; + public boolean addAll(int index, Users list) { + if (isEmpty()) { + prevCursor = list.prevCursor; + nextCursor = list.nextCursor; + } else if (index == 0) { + prevCursor = list.prevCursor; + } else if (index == size() - 1) { + nextCursor = list.nextCursor; + } + return super.addAll(index, list); } + @Override @NonNull public String toString() { diff --git a/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java b/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java index 755e5735..29bcc060 100644 --- a/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java +++ b/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java @@ -385,6 +385,11 @@ public class AppDatabase { */ private static final String SINGLE_ITEM = "1"; + /** + * database lock + */ + private static final Object LOCK = new Object(); + /** * limit of database entries */ @@ -409,11 +414,13 @@ public class AppDatabase { * @param statuses status from home timeline */ public void saveHomeTimeline(List statuses) { - if (!statuses.isEmpty()) { - SQLiteDatabase db = adapter.getDbWrite(); - for (Status status : statuses) - saveStatus(status, db, HOME_TIMELINE_MASK); - adapter.commit(); + synchronized (LOCK) { + if (!statuses.isEmpty()) { + SQLiteDatabase db = adapter.getDbWrite(); + for (Status status : statuses) + saveStatus(status, db, HOME_TIMELINE_MASK); + adapter.commit(); + } } } @@ -423,11 +430,13 @@ public class AppDatabase { * @param statuses user timeline */ public void saveUserTimeline(List statuses) { - if (!statuses.isEmpty()) { - SQLiteDatabase db = adapter.getDbWrite(); - for (Status status : statuses) - saveStatus(status, db, USER_TIMELINE_MASK); - adapter.commit(); + synchronized (LOCK) { + if (!statuses.isEmpty()) { + SQLiteDatabase db = adapter.getDbWrite(); + for (Status status : statuses) + saveStatus(status, db, USER_TIMELINE_MASK); + adapter.commit(); + } } } @@ -438,18 +447,20 @@ public class AppDatabase { * @param ownerId user ID */ public void saveFavoriteTimeline(List statuses, long ownerId) { - SQLiteDatabase db = adapter.getDbWrite(); - // delete old favorits - String[] delArgs = {Long.toString(ownerId)}; - db.delete(FavoriteTable.NAME, FAVORITE_SELECT_OWNER, delArgs); + synchronized (LOCK) { + SQLiteDatabase db = adapter.getDbWrite(); + // delete old favorits + String[] delArgs = {Long.toString(ownerId)}; + db.delete(FavoriteTable.NAME, FAVORITE_SELECT_OWNER, delArgs); - if (!statuses.isEmpty()) { - for (Status status : statuses) { - saveStatus(status, db, 0); - saveFavorite(status.getId(), ownerId, db); + if (!statuses.isEmpty()) { + for (Status status : statuses) { + saveStatus(status, db, 0); + saveFavorite(status.getId(), ownerId, db); + } } + adapter.commit(); } - adapter.commit(); } /** @@ -459,18 +470,20 @@ public class AppDatabase { * @param ownerId id of the owner */ public void saveBookmarkTimeline(List statuses, long ownerId) { - SQLiteDatabase db = adapter.getDbWrite(); - // delete old favorits - String[] delArgs = {Long.toString(ownerId)}; - db.delete(BookmarkTable.NAME, BOOKMARK_SELECT_OWNER, delArgs); + synchronized (LOCK) { + SQLiteDatabase db = adapter.getDbWrite(); + // delete old favorits + String[] delArgs = {Long.toString(ownerId)}; + db.delete(BookmarkTable.NAME, BOOKMARK_SELECT_OWNER, delArgs); - if (!statuses.isEmpty()) { - for (Status status : statuses) { - saveStatus(status, db, 0); - saveBookmark(status.getId(), ownerId, db); + if (!statuses.isEmpty()) { + for (Status status : statuses) { + saveStatus(status, db, 0); + saveBookmark(status.getId(), ownerId, db); + } } + adapter.commit(); } - adapter.commit(); } /** @@ -479,11 +492,13 @@ public class AppDatabase { * @param statuses status replies */ public void saveReplyTimeline(List statuses) { - if (!statuses.isEmpty()) { - SQLiteDatabase db = adapter.getDbWrite(); - for (Status status : statuses) - saveStatus(status, db, STATUS_REPLY_MASK); - adapter.commit(); + synchronized (LOCK) { + if (!statuses.isEmpty()) { + SQLiteDatabase db = adapter.getDbWrite(); + for (Status status : statuses) + saveStatus(status, db, STATUS_REPLY_MASK); + adapter.commit(); + } } } @@ -491,24 +506,26 @@ public class AppDatabase { * save notifications to database */ public void saveNotifications(List notifications) { - if (!notifications.isEmpty()) { - SQLiteDatabase db = adapter.getDbWrite(); - for (Notification notification : notifications) { - ContentValues column = new ContentValues(); - column.put(NotificationTable.ID, notification.getId()); - column.put(NotificationTable.TIME, notification.getTimestamp()); - column.put(NotificationTable.TYPE, notification.getType()); - column.put(NotificationTable.OWNER, settings.getLogin().getId()); - column.put(NotificationTable.USER, notification.getUser().getId()); - saveUser(notification.getUser(), db, CONFLICT_IGNORE); - // add status - if (notification.getStatus() != null) { - saveStatus(notification.getStatus(), db, NOTIFICATION_MASK); - column.put(NotificationTable.ITEM, notification.getStatus().getId()); + synchronized (LOCK) { + if (!notifications.isEmpty()) { + SQLiteDatabase db = adapter.getDbWrite(); + for (Notification notification : notifications) { + ContentValues column = new ContentValues(); + column.put(NotificationTable.ID, notification.getId()); + column.put(NotificationTable.TIME, notification.getTimestamp()); + column.put(NotificationTable.TYPE, notification.getType()); + column.put(NotificationTable.OWNER, settings.getLogin().getId()); + column.put(NotificationTable.USER, notification.getUser().getId()); + saveUser(notification.getUser(), db, CONFLICT_IGNORE); + // add status + if (notification.getStatus() != null) { + saveStatus(notification.getStatus(), db, NOTIFICATION_MASK); + column.put(NotificationTable.ITEM, notification.getStatus().getId()); + } + db.insertWithOnConflict(NotificationTable.NAME, null, column, CONFLICT_REPLACE); } - db.insertWithOnConflict(NotificationTable.NAME, null, column, CONFLICT_REPLACE); + adapter.commit(); } - adapter.commit(); } } @@ -518,11 +535,13 @@ public class AppDatabase { * @param messages list of direct messages */ public void saveMessages(List messages) { - if (!messages.isEmpty()) { - SQLiteDatabase db = adapter.getDbWrite(); - for (Message message : messages) - saveMessages(message, db); - adapter.commit(); + synchronized (LOCK) { + if (!messages.isEmpty()) { + SQLiteDatabase db = adapter.getDbWrite(); + for (Message message : messages) + saveMessages(message, db); + adapter.commit(); + } } } @@ -532,20 +551,22 @@ public class AppDatabase { * @param ids list of user IDs */ public void saveFilterlist(List ids) { - long homeId = settings.getLogin().getId(); - String[] args = {Long.toString(homeId)}; - SQLiteDatabase db = adapter.getDbWrite(); - db.delete(UserExcludeTable.NAME, LIST_SELECT, args); + synchronized (LOCK) { + long homeId = settings.getLogin().getId(); + String[] args = {Long.toString(homeId)}; + SQLiteDatabase db = adapter.getDbWrite(); + db.delete(UserExcludeTable.NAME, LIST_SELECT, args); - if (!ids.isEmpty()) { - for (long id : ids) { - ContentValues column = new ContentValues(2); - column.put(UserExcludeTable.ID, id); - column.put(UserExcludeTable.OWNER, homeId); - db.insertWithOnConflict(UserExcludeTable.NAME, null, column, SQLiteDatabase.CONFLICT_IGNORE); + if (!ids.isEmpty()) { + for (long id : ids) { + ContentValues column = new ContentValues(2); + column.put(UserExcludeTable.ID, id); + column.put(UserExcludeTable.OWNER, homeId); + db.insertWithOnConflict(UserExcludeTable.NAME, null, column, SQLiteDatabase.CONFLICT_IGNORE); + } } + adapter.commit(); } - adapter.commit(); } /** @@ -554,18 +575,20 @@ public class AppDatabase { * @param trends List of Trends */ public void saveTrends(List trends) { - String[] args = {Long.toString(settings.getTrendLocation().getId())}; - SQLiteDatabase db = adapter.getDbWrite(); - db.delete(TrendTable.NAME, TREND_SELECT, args); - for (Trend trend : trends) { - ContentValues column = new ContentValues(4); - column.put(TrendTable.ID, trend.getLocationId()); - column.put(TrendTable.VOL, trend.getPopularity()); - column.put(TrendTable.TREND, trend.getName()); - column.put(TrendTable.INDEX, trend.getRank()); - db.insert(TrendTable.NAME, null, column); + synchronized (LOCK) { + String[] args = {Long.toString(settings.getTrendLocation().getId())}; + SQLiteDatabase db = adapter.getDbWrite(); + db.delete(TrendTable.NAME, TREND_SELECT, args); + for (Trend trend : trends) { + ContentValues column = new ContentValues(4); + column.put(TrendTable.ID, trend.getLocationId()); + column.put(TrendTable.VOL, trend.getPopularity()); + column.put(TrendTable.TREND, trend.getName()); + column.put(TrendTable.INDEX, trend.getRank()); + db.insert(TrendTable.NAME, null, column); + } + adapter.commit(); } - adapter.commit(); } /** @@ -574,12 +597,14 @@ public class AppDatabase { * @param status favorited status */ public void addToFavorits(Status status) { - if (status.getEmbeddedStatus() != null) - status = status.getEmbeddedStatus(); - SQLiteDatabase db = adapter.getDbWrite(); - saveStatus(status, db, 0); - saveFavorite(status.getId(), settings.getLogin().getId(), db); - adapter.commit(); + synchronized (LOCK) { + if (status.getEmbeddedStatus() != null) + status = status.getEmbeddedStatus(); + SQLiteDatabase db = adapter.getDbWrite(); + saveStatus(status, db, 0); + saveFavorite(status.getId(), settings.getLogin().getId(), db); + adapter.commit(); + } } /** @@ -588,12 +613,14 @@ public class AppDatabase { * @param status favorited status */ public void addToBookmarks(Status status) { - if (status.getEmbeddedStatus() != null) - status = status.getEmbeddedStatus(); - SQLiteDatabase db = adapter.getDbWrite(); - saveStatus(status, db, 0); - saveBookmark(status.getId(), settings.getLogin().getId(), db); - adapter.commit(); + synchronized (LOCK) { + if (status.getEmbeddedStatus() != null) + status = status.getEmbeddedStatus(); + SQLiteDatabase db = adapter.getDbWrite(); + saveStatus(status, db, 0); + saveBookmark(status.getId(), settings.getLogin().getId(), db); + adapter.commit(); + } } /** @@ -602,22 +629,24 @@ public class AppDatabase { * @param account login information */ public void saveLogin(Account account) { - ContentValues column = new ContentValues(9); - column.put(AccountTable.ID, account.getId()); - column.put(AccountTable.DATE, account.getTimestamp()); - column.put(AccountTable.HOSTNAME, account.getHostname()); - column.put(AccountTable.CLIENT_ID, account.getConsumerToken()); - column.put(AccountTable.CLIENT_SECRET, account.getConsumerSecret()); - column.put(AccountTable.API, account.getConfiguration().getAccountType()); - column.put(AccountTable.ACCESS_TOKEN, account.getOauthToken()); - column.put(AccountTable.TOKEN_SECRET, account.getOauthSecret()); - column.put(AccountTable.BEARER, account.getBearerToken()); - SQLiteDatabase db = adapter.getDbWrite(); - db.insertWithOnConflict(AccountTable.NAME, "", column, CONFLICT_REPLACE); - if (account.getUser() != null) { - saveUser(account.getUser(), db, CONFLICT_IGNORE); + synchronized (LOCK) { + ContentValues column = new ContentValues(9); + column.put(AccountTable.ID, account.getId()); + column.put(AccountTable.DATE, account.getTimestamp()); + column.put(AccountTable.HOSTNAME, account.getHostname()); + column.put(AccountTable.CLIENT_ID, account.getConsumerToken()); + column.put(AccountTable.CLIENT_SECRET, account.getConsumerSecret()); + column.put(AccountTable.API, account.getConfiguration().getAccountType()); + column.put(AccountTable.ACCESS_TOKEN, account.getOauthToken()); + column.put(AccountTable.TOKEN_SECRET, account.getOauthSecret()); + column.put(AccountTable.BEARER, account.getBearerToken()); + SQLiteDatabase db = adapter.getDbWrite(); + db.insertWithOnConflict(AccountTable.NAME, "", column, CONFLICT_REPLACE); + if (account.getUser() != null) { + saveUser(account.getUser(), db, CONFLICT_IGNORE); + } + adapter.commit(); } - adapter.commit(); } /** @@ -626,12 +655,14 @@ public class AppDatabase { * @param userId ID of the user */ public void addUserToFilterlist(long userId) { - SQLiteDatabase db = adapter.getDbWrite(); - ContentValues column = new ContentValues(2); - column.put(UserExcludeTable.ID, userId); - column.put(UserExcludeTable.OWNER, settings.getLogin().getId()); - db.insert(UserExcludeTable.NAME, null, column); - adapter.commit(); + synchronized (LOCK) { + SQLiteDatabase db = adapter.getDbWrite(); + ContentValues column = new ContentValues(2); + column.put(UserExcludeTable.ID, userId); + column.put(UserExcludeTable.OWNER, settings.getLogin().getId()); + db.insert(UserExcludeTable.NAME, null, column); + adapter.commit(); + } } /** @@ -640,12 +671,14 @@ public class AppDatabase { * @return home timeline */ public List getHomeTimeline() { - String homeStr = Long.toString(settings.getLogin().getId()); - String[] args = {homeStr, homeStr, Integer.toString(settings.getListSize())}; + synchronized (LOCK) { + String homeStr = Long.toString(settings.getLogin().getId()); + String[] args = {homeStr, homeStr, Integer.toString(settings.getListSize())}; - SQLiteDatabase db = adapter.getDbRead(); - Cursor cursor = db.rawQuery(HOME_QUERY, args); - return getStatuses(cursor, db); + SQLiteDatabase db = adapter.getDbRead(); + Cursor cursor = db.rawQuery(HOME_QUERY, args); + return getStatuses(cursor, db); + } } /** @@ -655,12 +688,14 @@ public class AppDatabase { * @return user timeline */ public List getUserTimeline(long userID) { - String homeStr = Long.toString(settings.getLogin().getId()); - String[] args = {homeStr, homeStr, Long.toString(userID), Integer.toString(settings.getListSize())}; + synchronized (LOCK) { + String homeStr = Long.toString(settings.getLogin().getId()); + String[] args = {homeStr, homeStr, Long.toString(userID), Integer.toString(settings.getListSize())}; - SQLiteDatabase db = adapter.getDbRead(); - Cursor cursor = db.rawQuery(USER_STATUS_QUERY, args); - return getStatuses(cursor, db); + SQLiteDatabase db = adapter.getDbRead(); + Cursor cursor = db.rawQuery(USER_STATUS_QUERY, args); + return getStatuses(cursor, db); + } } /** @@ -670,12 +705,14 @@ public class AppDatabase { * @return favorite timeline */ public List getUserFavorites(long ownerID) { - String homeStr = Long.toString(settings.getLogin().getId()); - String[] args = {Long.toString(ownerID), homeStr, homeStr, Integer.toString(settings.getListSize())}; + synchronized (LOCK) { + String homeStr = Long.toString(settings.getLogin().getId()); + String[] args = {Long.toString(ownerID), homeStr, homeStr, Integer.toString(settings.getListSize())}; - SQLiteDatabase db = adapter.getDbRead(); - Cursor cursor = db.rawQuery(USER_FAVORIT_QUERY, args); - return getStatuses(cursor, db); + SQLiteDatabase db = adapter.getDbRead(); + Cursor cursor = db.rawQuery(USER_FAVORIT_QUERY, args); + return getStatuses(cursor, db); + } } /** @@ -685,12 +722,14 @@ public class AppDatabase { * @return bookmark timeline */ public List getUserBookmarks(long ownerID) { - String homeStr = Long.toString(settings.getLogin().getId()); - String[] args = {Long.toString(ownerID), homeStr, homeStr, Integer.toString(settings.getListSize())}; + synchronized (LOCK) { + String homeStr = Long.toString(settings.getLogin().getId()); + String[] args = {Long.toString(ownerID), homeStr, homeStr, Integer.toString(settings.getListSize())}; - SQLiteDatabase db = adapter.getDbRead(); - Cursor cursor = db.rawQuery(USER_BOOKMARKS_QUERY, args); - return getStatuses(cursor, db); + SQLiteDatabase db = adapter.getDbRead(); + Cursor cursor = db.rawQuery(USER_BOOKMARKS_QUERY, args); + return getStatuses(cursor, db); + } } /** @@ -700,12 +739,14 @@ public class AppDatabase { * @return status reply timeline */ public List getReplies(long id) { - String homeStr = Long.toString(settings.getLogin().getId()); - String[] args = {Long.toString(id), homeStr, homeStr, Integer.toString(settings.getListSize())}; + synchronized (LOCK) { + String homeStr = Long.toString(settings.getLogin().getId()); + String[] args = {Long.toString(id), homeStr, homeStr, Integer.toString(settings.getListSize())}; - SQLiteDatabase db = adapter.getDbRead(); - Cursor cursor = db.rawQuery(REPLY_QUERY, args); - return getStatuses(cursor, db); + SQLiteDatabase db = adapter.getDbRead(); + Cursor cursor = db.rawQuery(REPLY_QUERY, args); + return getStatuses(cursor, db); + } } /** @@ -714,18 +755,20 @@ public class AppDatabase { * @return notification lsit */ public List getNotifications() { - Account login = settings.getLogin(); - String[] args = {Long.toString(login.getId()), Integer.toString(settings.getListSize())}; - SQLiteDatabase db = adapter.getDbRead(); - List result = new LinkedList<>(); - Cursor cursor = db.rawQuery(NOTIFICATION_QUERY, args); - if (cursor.moveToFirst()) { - do { - result.add(getNotification(cursor, login)); - } while (cursor.moveToNext()); + synchronized (LOCK) { + Account login = settings.getLogin(); + String[] args = {Long.toString(login.getId()), Integer.toString(settings.getListSize())}; + SQLiteDatabase db = adapter.getDbRead(); + List result = new LinkedList<>(); + Cursor cursor = db.rawQuery(NOTIFICATION_QUERY, args); + if (cursor.moveToFirst()) { + do { + result.add(getNotification(cursor, login)); + } while (cursor.moveToNext()); + } + cursor.close(); + return result; } - cursor.close(); - return result; } /** @@ -734,18 +777,20 @@ public class AppDatabase { * @return list of trends */ public List getTrends() { - String[] args = {Long.toString(settings.getTrendLocation().getId())}; - SQLiteDatabase db = adapter.getDbRead(); - Cursor cursor = db.query(TrendTable.NAME, DatabaseTrend.COLUMNS, TREND_SELECT, args, null, null, null); - List trends = new LinkedList<>(); - if (cursor.moveToFirst()) { - do { - trends.add(new DatabaseTrend(cursor)); - } while (cursor.moveToNext()); + synchronized (LOCK) { + String[] args = {Long.toString(settings.getTrendLocation().getId())}; + SQLiteDatabase db = adapter.getDbRead(); + Cursor cursor = db.query(TrendTable.NAME, DatabaseTrend.COLUMNS, TREND_SELECT, args, null, null, null); + List trends = new LinkedList<>(); + if (cursor.moveToFirst()) { + do { + trends.add(new DatabaseTrend(cursor)); + } while (cursor.moveToNext()); + } + cursor.close(); + Collections.sort(trends); + return trends; } - cursor.close(); - Collections.sort(trends); - return trends; } /** @@ -754,30 +799,32 @@ public class AppDatabase { * @return list of direct messages */ public Messages getMessages() { - Account login = settings.getLogin(); - String homeIdStr = Long.toString(login.getId()); - String[] args = {homeIdStr, homeIdStr, Integer.toString(settings.getListSize())}; - Messages result = new Messages(null, null); - SQLiteDatabase db = adapter.getDbRead(); - Cursor cursor = db.rawQuery(MESSAGE_QUERY, args); - if (cursor.moveToFirst()) { - do { - DatabaseMessage item = new DatabaseMessage(cursor, login); - result.add(item); - if (item.getMediaKeys().length > 0) { - List medias = new LinkedList<>(); - for (String key : item.getMediaKeys()) { - Media media = getMedia(db, key); - if (media != null) { - medias.add(media); + synchronized (LOCK) { + Account login = settings.getLogin(); + String homeIdStr = Long.toString(login.getId()); + String[] args = {homeIdStr, homeIdStr, Integer.toString(settings.getListSize())}; + Messages result = new Messages(null, null); + SQLiteDatabase db = adapter.getDbRead(); + Cursor cursor = db.rawQuery(MESSAGE_QUERY, args); + if (cursor.moveToFirst()) { + do { + DatabaseMessage item = new DatabaseMessage(cursor, login); + result.add(item); + if (item.getMediaKeys().length > 0) { + List medias = new LinkedList<>(); + for (String key : item.getMediaKeys()) { + Media media = getMedia(db, key); + if (media != null) { + medias.add(media); + } } + item.addMedia(medias.toArray(new Media[0])); } - item.addMedia(medias.toArray(new Media[0])); - } - } while (cursor.moveToNext()); + } while (cursor.moveToNext()); + } + cursor.close(); + return result; } - cursor.close(); - return result; } /** @@ -786,20 +833,22 @@ public class AppDatabase { * @return list of all logins */ public List getLogins() { - ArrayList result = new ArrayList<>(); + synchronized (LOCK) { + ArrayList result = new ArrayList<>(); - SQLiteDatabase db = adapter.getDbRead(); - Cursor cursor = db.query(AccountTable.NAME, DatabaseAccount.COLUMNS, null, null, null, null, SORT_BY_CREATION); - if (cursor.moveToFirst()) { - result.ensureCapacity(cursor.getCount()); - do { - DatabaseAccount account = new DatabaseAccount(cursor); - account.addUser(getUser(account.getId(), account)); - result.add(account); - } while (cursor.moveToNext()); + SQLiteDatabase db = adapter.getDbRead(); + Cursor cursor = db.query(AccountTable.NAME, DatabaseAccount.COLUMNS, null, null, null, null, SORT_BY_CREATION); + if (cursor.moveToFirst()) { + result.ensureCapacity(cursor.getCount()); + do { + DatabaseAccount account = new DatabaseAccount(cursor); + account.addUser(getUser(account.getId(), account)); + result.add(account); + } while (cursor.moveToNext()); + } + cursor.close(); + return result; } - cursor.close(); - return result; } /** @@ -810,7 +859,9 @@ public class AppDatabase { */ @Nullable public User getUser(long userId) { - return getUser(userId, settings.getLogin()); + synchronized (LOCK) { + return getUser(userId, settings.getLogin()); + } } /** @@ -821,14 +872,16 @@ public class AppDatabase { */ @Nullable public Notification getNotification(long id) { - String[] args = {Long.toString(id)}; - SQLiteDatabase db = adapter.getDbRead(); - Cursor cursor = db.rawQuery(SINGLE_NOTIFICATION_QUERY, args); - Notification notification = null; - if (cursor.moveToFirst()) - notification = new DatabaseNotification(cursor, settings.getLogin()); - cursor.close(); - return notification; + synchronized (LOCK) { + String[] args = {Long.toString(id)}; + SQLiteDatabase db = adapter.getDbRead(); + Cursor cursor = db.rawQuery(SINGLE_NOTIFICATION_QUERY, args); + Notification notification = null; + if (cursor.moveToFirst()) + notification = new DatabaseNotification(cursor, settings.getLogin()); + cursor.close(); + return notification; + } } /** @@ -840,14 +893,16 @@ public class AppDatabase { */ @Nullable public User getUser(long userId, Account account) { - String[] args = {Long.toString(userId)}; - SQLiteDatabase db = adapter.getDbRead(); - Cursor cursor = db.rawQuery(SINGLE_USER_QUERY, args); - User user = null; - if (cursor.moveToFirst()) - user = new DatabaseUser(cursor, account); - cursor.close(); - return user; + synchronized (LOCK) { + String[] args = {Long.toString(userId)}; + SQLiteDatabase db = adapter.getDbRead(); + Cursor cursor = db.rawQuery(SINGLE_USER_QUERY, args); + User user = null; + if (cursor.moveToFirst()) + user = new DatabaseUser(cursor, account); + cursor.close(); + return user; + } } /** @@ -858,16 +913,18 @@ public class AppDatabase { */ @Nullable public Status getStatus(long id) { - String homeStr = Long.toString(settings.getLogin().getId()); - String[] args = {Long.toString(id), homeStr, homeStr}; + synchronized (LOCK) { + String homeStr = Long.toString(settings.getLogin().getId()); + String[] args = {Long.toString(id), homeStr, homeStr}; - SQLiteDatabase db = adapter.getDbRead(); - Status result = null; - Cursor cursor = db.rawQuery(SINGLE_STATUS_QUERY, args); - if (cursor.moveToFirst()) - result = getStatus(cursor, db); - cursor.close(); - return result; + SQLiteDatabase db = adapter.getDbRead(); + Status result = null; + Cursor cursor = db.rawQuery(SINGLE_STATUS_QUERY, args); + if (cursor.moveToFirst()) + result = getStatus(cursor, db); + cursor.close(); + return result; + } } /** @@ -876,9 +933,11 @@ public class AppDatabase { * @param user Twitter user */ public void saveUser(User user) { - SQLiteDatabase db = adapter.getDbWrite(); - saveUser(user, db, CONFLICT_REPLACE); - adapter.commit(); + synchronized (LOCK) { + SQLiteDatabase db = adapter.getDbWrite(); + saveUser(user, db, CONFLICT_REPLACE); + adapter.commit(); + } } /** @@ -887,11 +946,13 @@ public class AppDatabase { * @param status status to update */ public void saveStatus(Status status) { - SQLiteDatabase db = adapter.getDbWrite(); - saveStatus(status, db, CONFLICT_REPLACE); - if (status.getEmbeddedStatus() != null) - saveStatus(status.getEmbeddedStatus(), db, CONFLICT_REPLACE); - adapter.commit(); + synchronized (LOCK) { + SQLiteDatabase db = adapter.getDbWrite(); + saveStatus(status, db, CONFLICT_REPLACE); + if (status.getEmbeddedStatus() != null) + saveStatus(status.getEmbeddedStatus(), db, CONFLICT_REPLACE); + adapter.commit(); + } } /** @@ -901,19 +962,21 @@ public class AppDatabase { * @param hide true to hide this status */ public void hideStatus(long id, boolean hide) { - String[] args = {Long.toString(id), Long.toString(settings.getLogin().getId())}; + synchronized (LOCK) { + String[] args = {Long.toString(id), Long.toString(settings.getLogin().getId())}; - SQLiteDatabase db = adapter.getDbWrite(); - int flags = getStatusFlags(db, id); - if (hide) { - flags |= HIDDEN_MASK; - } else { - flags &= ~HIDDEN_MASK; + SQLiteDatabase db = adapter.getDbWrite(); + int flags = getStatusFlags(db, id); + if (hide) { + flags |= HIDDEN_MASK; + } else { + flags &= ~HIDDEN_MASK; + } + ContentValues column = new ContentValues(1); + column.put(StatusRegisterTable.REGISTER, flags); + db.update(StatusRegisterTable.NAME, column, STATUS_REG_SELECT, args); + adapter.commit(); } - ContentValues column = new ContentValues(1); - column.put(StatusRegisterTable.REGISTER, flags); - db.update(StatusRegisterTable.NAME, column, STATUS_REG_SELECT, args); - adapter.commit(); } /** @@ -922,14 +985,16 @@ public class AppDatabase { * @param id status ID */ public void removeStatus(long id) { - String[] args = {Long.toString(id)}; + synchronized (LOCK) { + String[] args = {Long.toString(id)}; - SQLiteDatabase db = adapter.getDbWrite(); - db.delete(StatusTable.NAME, STATUS_SELECT, args); - db.delete(NotificationTable.NAME, NOTIFICATION_SELECT, args); - db.delete(FavoriteTable.NAME, FAVORITE_SELECT_STATUS, args); - db.delete(BookmarkTable.NAME, BOOKMARK_SELECT_STATUS, args); - adapter.commit(); + SQLiteDatabase db = adapter.getDbWrite(); + db.delete(StatusTable.NAME, STATUS_SELECT, args); + db.delete(NotificationTable.NAME, NOTIFICATION_SELECT, args); + db.delete(FavoriteTable.NAME, FAVORITE_SELECT_STATUS, args); + db.delete(BookmarkTable.NAME, BOOKMARK_SELECT_STATUS, args); + adapter.commit(); + } } /** @@ -938,19 +1003,21 @@ public class AppDatabase { * @param status status to remove from the favorites */ public void removeFromFavorite(Status status) { - String[] delArgs = {Long.toString(status.getId()), Long.toString(settings.getLogin().getId())}; + synchronized (LOCK) { + String[] delArgs = {Long.toString(status.getId()), Long.toString(settings.getLogin().getId())}; - if (status.getEmbeddedStatus() != null) { - status = status.getEmbeddedStatus(); + if (status.getEmbeddedStatus() != null) { + status = status.getEmbeddedStatus(); + } + SQLiteDatabase db = adapter.getDbWrite(); + // get status flags + int flags = getStatusFlags(db, status.getId()); + flags &= ~FAVORITE_MASK; // unset favorite flag + // update database + saveStatusFlags(db, status, flags); + db.delete(FavoriteTable.NAME, FAVORITE_SELECT, delArgs); + adapter.commit(); } - SQLiteDatabase db = adapter.getDbWrite(); - // get status flags - int flags = getStatusFlags(db, status.getId()); - flags &= ~FAVORITE_MASK; // unset favorite flag - // update database - saveStatusFlags(db, status, flags); - db.delete(FavoriteTable.NAME, FAVORITE_SELECT, delArgs); - adapter.commit(); } /** @@ -959,19 +1026,21 @@ public class AppDatabase { * @param status status to remove from the bookmarks */ public void removeFromBookmarks(Status status) { - String[] delArgs = {Long.toString(status.getId()), Long.toString(settings.getLogin().getId())}; + synchronized (LOCK) { + String[] delArgs = {Long.toString(status.getId()), Long.toString(settings.getLogin().getId())}; - if (status.getEmbeddedStatus() != null) { - status = status.getEmbeddedStatus(); + if (status.getEmbeddedStatus() != null) { + status = status.getEmbeddedStatus(); + } + SQLiteDatabase db = adapter.getDbWrite(); + // get status flags + int flags = getStatusFlags(db, status.getId()); + flags &= ~BOOKMARK_MASK; // unset bookmark flag + // update database + saveStatusFlags(db, status, flags); + db.delete(BookmarkTable.NAME, BOOKMARK_SELECT, delArgs); + adapter.commit(); } - SQLiteDatabase db = adapter.getDbWrite(); - // get status flags - int flags = getStatusFlags(db, status.getId()); - flags &= ~BOOKMARK_MASK; // unset bookmark flag - // update database - saveStatusFlags(db, status, flags); - db.delete(BookmarkTable.NAME, BOOKMARK_SELECT, delArgs); - adapter.commit(); } /** @@ -980,11 +1049,13 @@ public class AppDatabase { * @param id Direct Message ID */ public void removeMessage(long id) { - String[] messageId = {Long.toString(id)}; + synchronized (LOCK) { + String[] messageId = {Long.toString(id)}; - SQLiteDatabase db = adapter.getDbWrite(); - db.delete(MessageTable.NAME, MESSAGE_SELECT, messageId); - adapter.commit(); + SQLiteDatabase db = adapter.getDbWrite(); + db.delete(MessageTable.NAME, MESSAGE_SELECT, messageId); + adapter.commit(); + } } /** @@ -993,11 +1064,13 @@ public class AppDatabase { * @param id account ID to remove */ public void removeLogin(long id) { - String[] args = {Long.toString(id)}; + synchronized (LOCK) { + String[] args = {Long.toString(id)}; - SQLiteDatabase db = adapter.getDbWrite(); - db.delete(AccountTable.NAME, ACCOUNT_SELECTION, args); - adapter.commit(); + SQLiteDatabase db = adapter.getDbWrite(); + db.delete(AccountTable.NAME, ACCOUNT_SELECTION, args); + adapter.commit(); + } } /** @@ -1006,19 +1079,21 @@ public class AppDatabase { * @return a set of user IDs */ public Set getFilterlistUserIds() { - String[] args = {Long.toString(settings.getLogin().getId())}; - SQLiteDatabase db = adapter.getDbRead(); - Cursor cursor = db.query(UserExcludeTable.NAME, LIST_ID_COL, LIST_SELECT, args, null, null, null, null); + synchronized (LOCK) { + String[] args = {Long.toString(settings.getLogin().getId())}; + SQLiteDatabase db = adapter.getDbRead(); + Cursor cursor = db.query(UserExcludeTable.NAME, LIST_ID_COL, LIST_SELECT, args, null, null, null, null); - Set result = new TreeSet<>(); - if (cursor.moveToFirst()) { - do { - long id = cursor.getLong(0); - result.add(id); - } while (cursor.moveToNext()); + Set result = new TreeSet<>(); + if (cursor.moveToFirst()) { + do { + long id = cursor.getLong(0); + result.add(id); + } while (cursor.moveToNext()); + } + cursor.close(); + return result; } - cursor.close(); - return result; } /** @@ -1027,10 +1102,12 @@ public class AppDatabase { * @param userId ID of the user */ public void removeUserFromFilterlist(long userId) { - String[] args = {Long.toString(settings.getLogin().getId()), Long.toString(userId)}; - SQLiteDatabase db = adapter.getDbWrite(); - db.delete(UserExcludeTable.NAME, FILTER_SELECT, args); - adapter.commit(); + synchronized (LOCK) { + String[] args = {Long.toString(settings.getLogin().getId()), Long.toString(userId)}; + SQLiteDatabase db = adapter.getDbWrite(); + db.delete(UserExcludeTable.NAME, FILTER_SELECT, args); + adapter.commit(); + } } /** @@ -1040,12 +1117,14 @@ public class AppDatabase { * @return true if found */ public boolean containsStatus(long id) { - String[] args = {Long.toString(id)}; - SQLiteDatabase db = adapter.getDbRead(); - Cursor c = db.query(StatusTable.NAME, null, STATUS_SELECT, args, null, null, SINGLE_ITEM); - boolean result = c.moveToFirst(); - c.close(); - return result; + synchronized (LOCK) { + String[] args = {Long.toString(id)}; + SQLiteDatabase db = adapter.getDbRead(); + Cursor c = db.query(StatusTable.NAME, null, STATUS_SELECT, args, null, null, SINGLE_ITEM); + boolean result = c.moveToFirst(); + c.close(); + return result; + } } /** @@ -1055,12 +1134,14 @@ public class AppDatabase { * @return true if found */ public boolean containsLogin(long id) { - String[] args = {Long.toString(id)}; - SQLiteDatabase db = adapter.getDbRead(); - Cursor c = db.query(AccountTable.NAME, null, ACCOUNT_SELECTION, args, null, null, SINGLE_ITEM); - boolean result = c.moveToFirst(); - c.close(); - return result; + synchronized (LOCK) { + String[] args = {Long.toString(id)}; + SQLiteDatabase db = adapter.getDbRead(); + Cursor c = db.query(AccountTable.NAME, null, ACCOUNT_SELECTION, args, null, null, SINGLE_ITEM); + boolean result = c.moveToFirst(); + c.close(); + return result; + } } /** @@ -1070,37 +1151,49 @@ public class AppDatabase { * @param mute true remove user status from mention results */ public void muteUser(long id, boolean mute) { - SQLiteDatabase db = adapter.getDbWrite(); - int flags = getUserFlags(db, id); - if (mute) { - flags |= EXCLUDE_MASK; - } else { - flags &= ~EXCLUDE_MASK; + synchronized (LOCK) { + SQLiteDatabase db = adapter.getDbWrite(); + int flags = getUserFlags(db, id); + if (mute) { + flags |= EXCLUDE_MASK; + } else { + flags &= ~EXCLUDE_MASK; + } + saveUserFlags(db, id, flags); + adapter.commit(); } - saveUserFlags(db, id, flags); - adapter.commit(); } /** * remove database tables except account table */ public void resetDatabase() { - SQLiteDatabase db = adapter.getDbWrite(); - db.delete(UserTable.NAME, null, null); - db.delete(StatusTable.NAME, null, null); - db.delete(MessageTable.NAME, null, null); - db.delete(FavoriteTable.NAME, null, null); - db.delete(BookmarkTable.NAME, null, null); - db.delete(TrendTable.NAME, null, null); - db.delete(StatusRegisterTable.NAME, null, null); - db.delete(UserRegisterTable.NAME, null, null); - db.delete(UserExcludeTable.NAME, null, null); - db.delete(NotificationTable.NAME, null, null); - db.delete(MediaTable.NAME, null, null); - db.delete(LocationTable.NAME, null, null); - db.delete(EmojiTable.NAME, null, null); - db.delete(PollTable.NAME, null, null); - adapter.commit(); + synchronized (LOCK) { + // save logins first + List logins = getLogins(); + SQLiteDatabase db = adapter.getDbWrite(); + db.delete(UserTable.NAME, null, null); + db.delete(StatusTable.NAME, null, null); + db.delete(MessageTable.NAME, null, null); + db.delete(FavoriteTable.NAME, null, null); + db.delete(BookmarkTable.NAME, null, null); + db.delete(TrendTable.NAME, null, null); + db.delete(StatusRegisterTable.NAME, null, null); + db.delete(UserRegisterTable.NAME, null, null); + db.delete(UserExcludeTable.NAME, null, null); + db.delete(NotificationTable.NAME, null, null); + db.delete(MediaTable.NAME, null, null); + db.delete(LocationTable.NAME, null, null); + db.delete(EmojiTable.NAME, null, null); + db.delete(PollTable.NAME, null, null); + // save user information from logins + for (Account login : logins) { + if (login.getUser() != null) { + saveUser(login.getUser(), db, CONFLICT_IGNORE); + } + } + adapter.commit(); + } } /** diff --git a/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java b/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java index cafcb44d..703f724b 100644 --- a/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java +++ b/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java @@ -383,7 +383,7 @@ public class DatabaseAdapter { * @param context application context * @return database instance */ - public static DatabaseAdapter getInstance(@NonNull Context context) { + static DatabaseAdapter getInstance(@NonNull Context context) { if (instance == null) { try { instance = new DatabaseAdapter(context.getApplicationContext()); @@ -402,7 +402,7 @@ public class DatabaseAdapter { * * @return SQLite instance */ - synchronized SQLiteDatabase getDbRead() { + SQLiteDatabase getDbRead() { if (!db.isOpen()) db = SQLiteDatabase.openOrCreateDatabase(databasePath, null); return db; @@ -413,7 +413,7 @@ public class DatabaseAdapter { * * @return SQLite instance */ - synchronized SQLiteDatabase getDbWrite() { + SQLiteDatabase getDbWrite() { SQLiteDatabase db = getDbRead(); db.beginTransaction(); return db; @@ -422,7 +422,7 @@ public class DatabaseAdapter { /** * Commit changes and close Database */ - synchronized void commit() { + void commit() { db.setTransactionSuccessful(); db.endTransaction(); } diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/activities/ProfileActivity.java b/app/src/main/java/org/nuclearfog/twidda/ui/activities/ProfileActivity.java index b5c0cdc8..73976b60 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/activities/ProfileActivity.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/activities/ProfileActivity.java @@ -224,11 +224,10 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult if (o instanceof User) { user = (User) o; userId = user.getId(); - adapter.setupProfilePage(user.getId()); } else { userId = i.getLongExtra(KEY_PROFILE_ID, 0); - adapter.setupProfilePage(userId); } + adapter.setupProfilePage(userId); if (settings.likeEnabled()) { tabIndicator = AppStyles.setTabIconsWithText(tabLayout, settings, R.array.profile_tab_icons_like); } else { @@ -735,6 +734,8 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult Spanned bio = Tagger.makeTextWithLinks(user.getDescription(), settings.getHighlightColor(), this); following.setText(StringTools.NUMBER_FORMAT.format(user.getFollowing())); follower.setText(StringTools.NUMBER_FORMAT.format(user.getFollower())); + following.setVisibility(VISIBLE); + follower.setVisibility(VISIBLE); username.setText(user.getUsername()); screenName.setText(user.getScreenname()); if (user.getStatusCount() >= 0) { @@ -806,9 +807,5 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult profileImage.setImageResource(0); } } - if (following.getVisibility() != VISIBLE) { - following.setVisibility(VISIBLE); - follower.setVisibility(VISIBLE); - } } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/adapter/UserAdapter.java b/app/src/main/java/org/nuclearfog/twidda/ui/adapter/UserAdapter.java index 1c0672d7..30ce3f3b 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/adapter/UserAdapter.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/adapter/UserAdapter.java @@ -114,10 +114,10 @@ public class UserAdapter extends Adapter implements OnHolderClickLis @Override - public boolean onPlaceholderClick(int position) { - boolean actionPerformed = listener.onPlaceholderClick(users.getNext()); + public boolean onPlaceholderClick(int index) { + boolean actionPerformed = listener.onPlaceholderClick(users.getNext(), index); if (actionPerformed) { - loadingIndex = position; + loadingIndex = index; return true; } return false; @@ -148,36 +148,24 @@ public class UserAdapter extends Adapter implements OnHolderClickLis * * @param newUsers new userlist */ - public void addItems(@NonNull Users newUsers) { - disableLoading(); - // add empty list - if (newUsers.isEmpty()) { - // remove placeholder if there isn't a next page - if (!users.isEmpty() && users.peekLast() == null) { - int end = users.size() - 1; - users.remove(end); - notifyItemRemoved(end); - } - } - // add items to the top of the list - else if (users.isEmpty() || !newUsers.hasPrevious()) { - users.replace(newUsers); - // add placeholder if there is a next page - if (newUsers.hasNext()) { + public void addItems(@NonNull Users newUsers, int index) { + if (index < 0) { + users.replaceAll(newUsers); + if (users.getNext() != 0L) { users.add(null); } notifyDataSetChanged(); - } - // add items to the end of the list - else { - int end = users.size() - 1; - // remove placeholder if there isn't a next page - if (!newUsers.hasNext()) { - users.remove(end); - notifyItemRemoved(end); + } else { + users.addAll(index, newUsers); + if (users.getNext() != 0L && users.peekLast() != null) { + users.add(null); + notifyItemRangeInserted(index, newUsers.size() + 1); + } else if (users.getNext() == 0L && users.peekLast() == null) { + users.remove(users.size() - 1); + notifyItemRangeInserted(index, newUsers.size() - 1); + } else if (!newUsers.isEmpty()) { + notifyItemRangeInserted(index, newUsers.size()); } - users.addAt(newUsers, end); - notifyItemRangeInserted(end, newUsers.size()); } } @@ -234,9 +222,10 @@ public class UserAdapter extends Adapter implements OnHolderClickLis * handle placeholder click * * @param cursor next cursor of the list + * @param index index of the placeholder * @return true if click was handled */ - boolean onPlaceholderClick(long cursor); + boolean onPlaceholderClick(long cursor, int index); /** * remove user from a list diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/fragments/UserFragment.java b/app/src/main/java/org/nuclearfog/twidda/ui/fragments/UserFragment.java index ca3a20b2..24163674 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/fragments/UserFragment.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/fragments/UserFragment.java @@ -136,6 +136,11 @@ public class UserFragment extends ListFragment implements UserClickListener, Asy */ public static final int USER_FRAG_FOLLOW_OUTGOING = 0x72544f17; + /** + * "index" used to replace the whole list with new items + */ + public static final int CLEAR_LIST = -1; + private ActivityResultLauncher activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this); @@ -163,14 +168,14 @@ public class UserFragment extends ListFragment implements UserClickListener, Asy setAdapter(adapter); setRefresh(true); - load(-1L); + load(-1L, CLEAR_LIST); } @Override protected void onReset() { setRefresh(true); - load(-1L); + load(-1L, CLEAR_LIST); } @@ -196,7 +201,7 @@ public class UserFragment extends ListFragment implements UserClickListener, Asy @Override protected void onReload() { - load(-1L); + load(-1L, CLEAR_LIST); } @@ -211,9 +216,9 @@ public class UserFragment extends ListFragment implements UserClickListener, Asy @Override - public boolean onPlaceholderClick(long cursor) { + public boolean onPlaceholderClick(long cursor, int index) { if (userLoader.isIdle()) { - load(cursor); + load(cursor, index); return true; } return false; @@ -233,7 +238,7 @@ public class UserFragment extends ListFragment implements UserClickListener, Asy @Override public void onResult(UserResult result) { if (result.users != null) { - adapter.addItems(result.users); + adapter.addItems(result.users, result.index); } else if (getContext() != null) { String message = ErrorHandler.getErrorMessage(getContext(), result.exception); Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); @@ -257,51 +262,51 @@ public class UserFragment extends ListFragment implements UserClickListener, Asy * * @param cursor cursor of the list */ - private void load(long cursor) { + private void load(long cursor, int index) { UserParam param; switch (mode) { case USER_FRAG_FOLLOWER: - param = new UserParam(UserParam.FOLLOWS, id, cursor, search); + param = new UserParam(UserParam.FOLLOWS, index, id, cursor, search); break; case USER_FRAG_FOLLOWING: - param = new UserParam(UserParam.FRIENDS, id, cursor, search); + param = new UserParam(UserParam.FRIENDS, index, id, cursor, search); break; case USER_FRAG_REPOST: - param = new UserParam(UserParam.REPOST, id, cursor, search); + param = new UserParam(UserParam.REPOST, index, id, cursor, search); break; case USER_FRAG_FAVORIT: - param = new UserParam(UserParam.FAVORIT, id, cursor, search); + param = new UserParam(UserParam.FAVORIT, index, id, cursor, search); break; case USER_FRAG_SEARCH: - param = new UserParam(UserParam.SEARCH, id, cursor, search); + param = new UserParam(UserParam.SEARCH, index, id, cursor, search); break; case USER_FRAG_LIST_SUBSCRIBER: - param = new UserParam(UserParam.SUBSCRIBER, id, cursor, search); + param = new UserParam(UserParam.SUBSCRIBER, index, id, cursor, search); break; case USER_FRAG_LIST_MEMBERS: - param = new UserParam(UserParam.LISTMEMBER, id, cursor, search); + param = new UserParam(UserParam.LISTMEMBER, index, id, cursor, search); break; case USER_FRAG_BLOCKED_USERS: - param = new UserParam(UserParam.BLOCK, id, cursor, search); + param = new UserParam(UserParam.BLOCK, index, id, cursor, search); break; case USER_FRAG_MUTED_USERS: - param = new UserParam(UserParam.MUTE, id, cursor, search); + param = new UserParam(UserParam.MUTE, index, id, cursor, search); break; case USER_FRAG_FOLLOW_OUTGOING: - param = new UserParam(UserParam.REQUEST_OUT, id, cursor, search); + param = new UserParam(UserParam.REQUEST_OUT, index, id, cursor, search); break; case USER_FRAG_FOLLOW_INCOMING: - param = new UserParam(UserParam.REQUEST_IN, id, cursor, search); + param = new UserParam(UserParam.REQUEST_IN, index, id, cursor, search); break; default: