From 4a3b9058b888d40b8340a50a5016001213c23b4b Mon Sep 17 00:00:00 2001 From: Floens Date: Sun, 10 Apr 2016 20:13:40 +0200 Subject: [PATCH] Put savedreply handling it its own class --- .../java/org/floens/chan/chan/ChanParser.java | 2 +- .../chan/core/database/DatabaseManager.java | 96 ++------------ .../database/DatabaseSavedReplyManager.java | 120 ++++++++++++++++++ .../floens/chan/core/model/SavedReply.java | 23 +++- .../chan/core/net/ChanReaderRequest.java | 2 +- .../chan/core/presenter/ReplyPresenter.java | 3 +- .../chan/core/presenter/ThreadPresenter.java | 13 +- .../DeveloperSettingsController.java | 9 +- 8 files changed, 172 insertions(+), 96 deletions(-) create mode 100644 Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSavedReplyManager.java diff --git a/Clover/app/src/main/java/org/floens/chan/chan/ChanParser.java b/Clover/app/src/main/java/org/floens/chan/chan/ChanParser.java index 9dd820b0..a3eb8459 100644 --- a/Clover/app/src/main/java/org/floens/chan/chan/ChanParser.java +++ b/Clover/app/src/main/java/org/floens/chan/chan/ChanParser.java @@ -421,7 +421,7 @@ public class ChanParser { } // Append You when it's a reply to an saved reply - if (databaseManager.isSavedReply(post.board, id)) { + if (databaseManager.getDatabaseSavedReplyManager().isSaved(post.board, id)) { key += " (You)"; } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseManager.java b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseManager.java index 9b979edf..e9476f79 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseManager.java @@ -29,9 +29,9 @@ import org.floens.chan.Chan; import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Filter; import org.floens.chan.core.model.Post; -import org.floens.chan.core.model.SavedReply; import org.floens.chan.core.model.ThreadHide; import org.floens.chan.utils.Logger; +import org.floens.chan.utils.Time; import java.sql.SQLException; import java.util.ArrayList; @@ -50,26 +50,19 @@ import static com.j256.ormlite.misc.TransactionManager.callInTransaction; public class DatabaseManager { private static final String TAG = "DatabaseManager"; - private static final long SAVED_REPLY_TRIM_TRIGGER = 250; - private static final long SAVED_REPLY_TRIM_COUNT = 50; private static final long THREAD_HIDE_TRIM_TRIGGER = 250; private static final long THREAD_HIDE_TRIM_COUNT = 50; - private static final long HISTORY_TRIM_TRIGGER = 500; - private static final long HISTORY_TRIM_COUNT = 50; private final ExecutorService backgroundExecutor; private final DatabaseHelper helper; - private final Object savedRepliesLock = new Object(); - private final List savedReplies = new ArrayList<>(); - private final HashSet savedRepliesIds = new HashSet<>(); - private final List threadHides = new ArrayList<>(); private final HashSet threadHidesIds = new HashSet<>(); private final DatabasePinManager databasePinManager; private final DatabaseLoadableManager databaseLoadableManager; private final DatabaseHistoryManager databaseHistoryManager; + private final DatabaseSavedReplyManager databaseSavedReplyManager; public DatabaseManager(Context context) { backgroundExecutor = Executors.newSingleThreadExecutor(); @@ -78,6 +71,7 @@ public class DatabaseManager { databaseLoadableManager = new DatabaseLoadableManager(this, helper); databasePinManager = new DatabasePinManager(this, helper, databaseLoadableManager); databaseHistoryManager = new DatabaseHistoryManager(this, helper, databaseLoadableManager); + databaseSavedReplyManager = new DatabaseSavedReplyManager(this, helper); initialize(); EventBus.getDefault().register(this); } @@ -94,6 +88,10 @@ public class DatabaseManager { return databaseHistoryManager; } + public DatabaseSavedReplyManager getDatabaseSavedReplyManager() { + return databaseSavedReplyManager; + } + // Called when the app changes foreground state public void onEvent(Chan.ForegroundChangedMessage message) { if (!message.inForeground) { @@ -102,9 +100,9 @@ public class DatabaseManager { } private void initialize() { - loadSavedReplies(); loadThreadHides(); - databaseHistoryManager.load(); + runTaskSync(databaseHistoryManager.load()); + runTaskSync(databaseSavedReplyManager.load()); } /** @@ -115,59 +113,6 @@ public class DatabaseManager { initialize(); } - /** - * Save a reply to the savedreply table. - * Threadsafe. - * - * @param saved the {@link SavedReply} to save - */ - public void saveReply(SavedReply saved) { - try { - helper.savedDao.create(saved); - } catch (SQLException e) { - Logger.e(TAG, "Error saving reply", e); - } - - synchronized (savedRepliesLock) { - savedReplies.add(saved); - savedRepliesIds.add(saved.no); - } - } - - /** - * Searches a saved reply. This is done through caching members, no database lookups. - * Threadsafe. - * - * @param board board for the reply to search - * @param no no for the reply to search - * @return A {@link SavedReply} that matches {@code board} and {@code no}, or {@code null} - */ - public SavedReply getSavedReply(String board, int no) { - synchronized (savedRepliesLock) { - if (savedRepliesIds.contains(no)) { - for (SavedReply r : savedReplies) { - if (r.no == no && r.board.equals(board)) { - return r; - } - } - } - } - - return null; - } - - /** - * Searches if a saved reply exists. This is done through caching members, no database lookups. - * Threadsafe. - * - * @param board board for the reply to search - * @param no no for the reply to search - * @return true if a {@link SavedReply} matched {@code board} and {@code no}, {@code false} otherwise - */ - public boolean isSavedReply(String board, int no) { - return getSavedReply(board, no) != null; - } - public void addOrUpdateFilter(Filter filter) { try { helper.filterDao.createOrUpdate(filter); @@ -317,26 +262,6 @@ public class DatabaseManager { return o; } - /** - * Threadsafe. - */ - private void loadSavedReplies() { - try { - trimTable(helper.savedDao, "savedreply", SAVED_REPLY_TRIM_TRIGGER, SAVED_REPLY_TRIM_COUNT); - - synchronized (savedRepliesLock) { - savedReplies.clear(); - savedReplies.addAll(helper.savedDao.queryForAll()); - savedRepliesIds.clear(); - for (SavedReply reply : savedReplies) { - savedRepliesIds.add(reply.no); - } - } - } catch (SQLException e) { - Logger.e(TAG, "Error loading saved replies", e); - } - } - private void loadThreadHides() { try { trimTable(helper.threadHideDao, "threadhide", THREAD_HIDE_TRIM_TRIGGER, THREAD_HIDE_TRIM_COUNT); @@ -364,8 +289,9 @@ public class DatabaseManager { try { long count = dao.countOf(); if (count > trigger) { + long start = Time.startTiming(); dao.executeRaw("DELETE FROM " + table + " WHERE id IN (SELECT id FROM " + table + " ORDER BY id ASC LIMIT ?)", String.valueOf(trim)); - Logger.i(TAG, "Trimmed " + table + " from " + count + " to " + dao.countOf() + " rows"); + Time.endTiming("Trimmed " + table + " from " + count + " to " + dao.countOf() + " rows", start); } } catch (SQLException e) { Logger.e(TAG, "Error trimming table " + table, e); diff --git a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSavedReplyManager.java b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSavedReplyManager.java new file mode 100644 index 00000000..53d85d28 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSavedReplyManager.java @@ -0,0 +1,120 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * 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.floens.chan.core.database; + +import com.j256.ormlite.stmt.QueryBuilder; + +import org.floens.chan.core.model.SavedReply; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +public class DatabaseSavedReplyManager { + private static final String TAG = "DatabaseSavedReplyManager"; + + private static final long SAVED_REPLY_TRIM_TRIGGER = 250; + private static final long SAVED_REPLY_TRIM_COUNT = 50; + + private DatabaseManager databaseManager; + private DatabaseHelper helper; + + private final Object lock = new Object(); + private final Map> savedRepliesByNo = new HashMap<>(); + + public DatabaseSavedReplyManager(DatabaseManager databaseManager, DatabaseHelper helper) { + this.databaseManager = databaseManager; + this.helper = helper; + } + + // optimized and threadsafe + public boolean isSaved(String board, int no) { + synchronized (lock) { + if (savedRepliesByNo.containsKey(no)) { + List items = savedRepliesByNo.get(no); + for (int i = 0; i < items.size(); i++) { + SavedReply item = items.get(i); + if (item.board.equals(board)) { + return true; + } + } + return false; + } else { + return false; + } + } + } + + public Callable load() { + return new Callable() { + @Override + public Void call() throws Exception { + databaseManager.trimTable(helper.savedDao, "savedreply", SAVED_REPLY_TRIM_TRIGGER, SAVED_REPLY_TRIM_COUNT); + + final List all = helper.savedDao.queryForAll(); + + synchronized (lock) { + savedRepliesByNo.clear(); + for (int i = 0; i < all.size(); i++) { + SavedReply savedReply = all.get(i); + if (savedRepliesByNo.containsKey(savedReply.no)) { + savedRepliesByNo.get(savedReply.no).add(savedReply); + } else { + List items = new ArrayList<>(); + items.add(savedReply); + savedRepliesByNo.put(savedReply.no, items); + } + } + } + return null; + } + }; + } + + public Callable saveReply(final SavedReply savedReply) { + return new Callable() { + @Override + public SavedReply call() throws Exception { + helper.savedDao.create(savedReply); + synchronized (lock) { + if (savedRepliesByNo.containsKey(savedReply.no)) { + savedRepliesByNo.get(savedReply.no).add(savedReply); + } else { + List items = new ArrayList<>(); + items.add(savedReply); + savedRepliesByNo.put(savedReply.no, items); + } + } + return savedReply; + } + }; + } + + public Callable findSavedReply(final String board, final int no) { + return new Callable() { + @Override + public SavedReply call() throws Exception { + QueryBuilder builder = helper.savedDao.queryBuilder(); + List query = builder.where().eq("board", board).and().eq("no", no).query(); + return query.isEmpty() ? null : query.get(0); + } + }; + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/SavedReply.java b/Clover/app/src/main/java/org/floens/chan/core/model/SavedReply.java index 5eb3f732..3d3571a1 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/SavedReply.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/SavedReply.java @@ -34,12 +34,29 @@ public class SavedReply { @DatabaseField(generatedId = true) private int id; - @DatabaseField - public String board = ""; + @DatabaseField(index = true, canBeNull = false) + public String board; - @DatabaseField + @DatabaseField(index = true) public int no; @DatabaseField public String password = ""; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SavedReply that = (SavedReply) o; + + return no == that.no && board.equals(that.board); + } + + @Override + public int hashCode() { + int result = board.hashCode(); + result = 31 * result + no; + return result; + } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/net/ChanReaderRequest.java b/Clover/app/src/main/java/org/floens/chan/core/net/ChanReaderRequest.java index dbbc8bc6..772f8b84 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/net/ChanReaderRequest.java +++ b/Clover/app/src/main/java/org/floens/chan/core/net/ChanReaderRequest.java @@ -378,7 +378,7 @@ public class ChanReaderRequest extends JsonReaderRequest, } } - databaseManager.saveReply(new SavedReply(loadable.board, replyCall.postNo, replyCall.password)); + SavedReply savedReply = new SavedReply(loadable.board, replyCall.postNo, replyCall.password); + databaseManager.runTask(databaseManager.getDatabaseSavedReplyManager().saveReply(savedReply)); switchPage(Page.INPUT, false); closeAll(); diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java index 72311aa1..864bd29d 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java @@ -430,7 +430,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt } } - if (databaseManager.isSavedReply(post.board, post.no)) { + if (databaseManager.getDatabaseSavedReplyManager().isSaved(post.board, post.no)) { menu.add(new FloatingMenuItem(POST_OPTION_DELETE, R.string.delete)); } @@ -476,7 +476,8 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt requestDeletePost(post); break; case POST_OPTION_SAVE: - databaseManager.saveReply(new SavedReply(post.board, post.no, "foo")); + SavedReply savedReply = new SavedReply(post.board, post.no, ""); + databaseManager.runTask(databaseManager.getDatabaseSavedReplyManager().saveReply(savedReply)); break; case POST_OPTION_PIN: Loadable pinLoadable = databaseManager.getDatabaseLoadableManager().get(Loadable.forThread(post.board, post.no)); @@ -584,7 +585,9 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt public void deletePostConfirmed(Post post, boolean onlyImageDelete) { threadPresenterCallback.showDeleting(); - SavedReply reply = databaseManager.getSavedReply(post.board, post.no); + SavedReply reply = databaseManager.runTaskSync( + databaseManager.getDatabaseSavedReplyManager().findSavedReply(post.board, post.no) + ); if (reply != null) { replyManager.makeHttpCall(new DeleteHttpCall(reply, onlyImageDelete), new ReplyManager.HttpCallback() { @Override @@ -609,7 +612,9 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt } private void requestDeletePost(Post post) { - SavedReply reply = databaseManager.getSavedReply(post.board, post.no); + SavedReply reply = databaseManager.runTaskSync( + databaseManager.getDatabaseSavedReplyManager().findSavedReply(post.board, post.no) + ); if (reply != null) { threadPresenterCallback.confirmPostDelete(post); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/DeveloperSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/DeveloperSettingsController.java index 409e46fc..28e73564 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/DeveloperSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/DeveloperSettingsController.java @@ -27,6 +27,7 @@ import android.widget.TextView; import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.controller.Controller; +import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.model.SavedReply; import java.util.Random; @@ -37,6 +38,8 @@ import static org.floens.chan.utils.AndroidUtils.getAttrColor; public class DeveloperSettingsController extends Controller { private TextView summaryText; + private DatabaseManager databaseManager; + public DeveloperSettingsController(Context context) { super(context); } @@ -45,6 +48,8 @@ public class DeveloperSettingsController extends Controller { public void onCreate() { super.onCreate(); + databaseManager = Chan.getDatabaseManager(); + navigationItem.setTitle(R.string.settings_developer); LinearLayout wrapper = new LinearLayout(context); @@ -87,7 +92,9 @@ public class DeveloperSettingsController extends Controller { int j = 0; for (int i = 0; i < 100; i++) { j += r.nextInt(10000); - Chan.getDatabaseManager().saveReply(new SavedReply("g", j, "pass")); + + SavedReply saved = new SavedReply("g", j, ""); + databaseManager.runTask(databaseManager.getDatabaseSavedReplyManager().saveReply(saved)); } setDbSummary(); }