make savedreplies site aware

fix boardsetuppresenter worksafe ordering
make the sitemanager simpler
some boardmanager refactoring
move responsibility of loading of boards for dynamic sites to sitebase
multisite
Floens 8 years ago
parent 759744984e
commit 6921f31143
  1. 154
      Clover/app/src/main/java/org/floens/chan/core/database/DatabaseBoardManager.java
  2. 7
      Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java
  3. 113
      Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSavedReplyManager.java
  4. 3
      Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java
  5. 200
      Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java
  6. 21
      Clover/app/src/main/java/org/floens/chan/core/model/orm/SavedReply.java
  7. 20
      Clover/app/src/main/java/org/floens/chan/core/model/orm/SiteModel.java
  8. 74
      Clover/app/src/main/java/org/floens/chan/core/presenter/BoardSetupPresenter.java
  9. 13
      Clover/app/src/main/java/org/floens/chan/core/presenter/BrowsePresenter.java
  10. 3
      Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java
  11. 9
      Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java
  12. 33
      Clover/app/src/main/java/org/floens/chan/core/site/Site.java
  13. 45
      Clover/app/src/main/java/org/floens/chan/core/site/SiteBase.java
  14. 126
      Clover/app/src/main/java/org/floens/chan/core/site/SiteManager.java
  15. 35
      Clover/app/src/main/java/org/floens/chan/core/site/SiteRepository.java
  16. 6
      Clover/app/src/main/java/org/floens/chan/core/site/SiteResolver.java
  17. 6
      Clover/app/src/main/java/org/floens/chan/core/site/common/ChanParser.java
  18. 46
      Clover/app/src/main/java/org/floens/chan/core/site/common/FutabaChanParser.java
  19. 9
      Clover/app/src/main/java/org/floens/chan/core/site/common/PostParseCallable.java
  20. 62
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java
  21. 6
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan8/Chan8.java
  22. 5
      Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java
  23. 21
      Clover/app/src/main/java/org/floens/chan/ui/controller/DeveloperSettingsController.java
  24. 10
      Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java
  25. 9
      Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java

@ -4,10 +4,7 @@ import com.j256.ormlite.stmt.QueryBuilder;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.site.Site;
import org.floens.chan.core.site.Sites;
import org.floens.chan.utils.Logger;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.Callable;
@ -23,122 +20,91 @@ public class DatabaseBoardManager {
}
public Callable<Board> createOrUpdate(final Board board) {
return new Callable<Board>() {
@Override
public Board call() throws Exception {
helper.boardsDao.createOrUpdate(board);
return board;
return () -> {
QueryBuilder<Board, Integer> q = helper.boardsDao.queryBuilder();
q.where().eq("site", board.getSite().id())
.and().eq("value", board.code);
Board existing = q.queryForFirst();
if (existing != null) {
existing.update(board);
helper.boardsDao.update(existing);
board.update(existing);
} else {
helper.boardsDao.create(board);
}
return board;
};
}
public Callable<Void> update(final Board board) {
return new Callable<Void>() {
@Override
public Void call() throws Exception {
helper.boardsDao.update(board);
return () -> {
helper.boardsDao.update(board);
return null;
}
return null;
};
}
public Callable<Void> createAll(final List<Board> boards) {
return new Callable<Void>() {
@Override
public Void call() throws Exception {
// TODO: optimize
for (Board board : boards) {
QueryBuilder<Board, Integer> q = helper.boardsDao.queryBuilder();
q.where().eq("site", board.getSite().id())
.and().eq("value", board.code);
Board existing = q.queryForFirst();
if (existing != null) {
existing.update(board);
helper.boardsDao.update(existing);
board.update(existing);
} else {
helper.boardsDao.create(board);
}
return () -> {
// TODO: optimize
for (Board board : boards) {
QueryBuilder<Board, Integer> q = helper.boardsDao.queryBuilder();
q.where().eq("site", board.getSite().id())
.and().eq("value", board.code);
Board existing = q.queryForFirst();
if (existing != null) {
existing.update(board);
helper.boardsDao.update(existing);
board.update(existing);
} else {
helper.boardsDao.create(board);
}
return null;
}
};
}
public Callable<List<Board>> getSiteBoards(final Site site) {
return new Callable<List<Board>>() {
@Override
public List<Board> call() throws Exception {
List<Board> boards = helper.boardsDao.queryBuilder()
.where().eq("site", site.id())
.query();
for (int i = 0; i < boards.size(); i++) {
Board board = boards.get(i);
board.site = site;
}
return boards;
}
return null;
};
}
public Callable<List<Board>> getSiteSavedBoards(final Site site) {
return new Callable<List<Board>>() {
@Override
public List<Board> call() throws Exception {
List<Board> boards = helper.boardsDao.queryBuilder()
.where().eq("site", site.id())
.and().eq("saved", true)
.query();
for (int i = 0; i < boards.size(); i++) {
Board board = boards.get(i);
board.site = site;
}
return boards;
public Callable<Board> getBoard(final Site site, final String code) {
return () -> {
Board board = helper.boardsDao.queryBuilder()
.where().eq("site", site.id())
.and().eq("value", code)
.queryForFirst();
if (board != null) {
board.site = site;
}
return board;
};
}
public Callable<List<Board>> getSavedBoards() {
return new Callable<List<Board>>() {
@Override
public List<Board> call() throws Exception {
List<Board> boards = null;
try {
boards = helper.boardsDao.queryBuilder()
.where().eq("saved", true)
.query();
for (int i = 0; i < boards.size(); i++) {
Board board = boards.get(i);
board.site = Sites.forId(board.siteId);
}
} catch (SQLException e) {
Logger.e(TAG, "Error getting boards from db", e);
}
return boards;
public Callable<List<Board>> getSiteBoards(final Site site) {
return () -> {
List<Board> boards = helper.boardsDao.queryBuilder()
.where().eq("site", site.id())
.query();
for (int i = 0; i < boards.size(); i++) {
Board board = boards.get(i);
board.site = site;
}
return boards;
};
}
public Callable<List<Board>> getAllBoards() {
return new Callable<List<Board>>() {
@Override
public List<Board> call() throws Exception {
List<Board> boards = null;
try {
boards = helper.boardsDao.queryForAll();
for (int i = 0; i < boards.size(); i++) {
Board board = boards.get(i);
board.site = Sites.forId(board.siteId);
}
} catch (SQLException e) {
Logger.e(TAG, "Error getting boards from db", e);
}
return boards;
public Callable<List<Board>> getSiteSavedBoards(final Site site) {
return () -> {
List<Board> boards = helper.boardsDao.queryBuilder()
.where().eq("site", site.id())
.and().eq("saved", true)
.query();
for (int i = 0; i < boards.size(); i++) {
Board board = boards.get(i);
board.site = site;
}
return boards;
};
}
}

@ -33,6 +33,7 @@ import org.floens.chan.core.model.orm.Pin;
import org.floens.chan.core.model.orm.SavedReply;
import org.floens.chan.core.model.orm.SiteModel;
import org.floens.chan.core.model.orm.ThreadHide;
import org.floens.chan.core.site.SiteManager;
import org.floens.chan.utils.Logger;
import java.sql.SQLException;
@ -40,6 +41,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.floens.chan.Chan.getGraph;
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
private static final String TAG = "DatabaseHelper";
@ -219,7 +222,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
Logger.e(TAG, "Error upgrading to version 22", e);
}
int siteId = 0;
final int siteId = 0;
try {
boardsDao.executeRawNoArgs("ALTER TABLE loadable ADD COLUMN site INTEGER default " + siteId + ";");
@ -229,6 +232,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
} catch (SQLException e) {
Logger.e(TAG, "Error upgrading to version 22", e);
}
getGraph().get(SiteManager.class).addSiteForLegacy();
}
isUpgrading = false;

@ -22,7 +22,9 @@ import android.support.annotation.AnyThread;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.table.TableUtils;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.SavedReply;
import org.floens.chan.core.site.Sites;
import org.floens.chan.utils.Time;
import java.util.ArrayList;
@ -55,19 +57,19 @@ public class DatabaseSavedReplyManager {
* Check if the given board-no combination is in the database.<br>
* This is unlike other methods in that it immediately returns the result instead of
* a Callable. This method is thread-safe and optimized.
* @param board board code of the post
* @param no post number
*
* @param board board of the post
* @param postNo post number
* @return {@code true} if the post is in the saved reply database, {@code false} otherwise.
*/
@AnyThread
public boolean isSaved(String board, int no) {
// TODO(multi-site)
public boolean isSaved(Board board, int postNo) {
synchronized (savedRepliesByNo) {
if (savedRepliesByNo.containsKey(no)) {
List<SavedReply> items = savedRepliesByNo.get(no);
if (savedRepliesByNo.containsKey(postNo)) {
List<SavedReply> items = savedRepliesByNo.get(postNo);
for (int i = 0; i < items.size(); i++) {
SavedReply item = items.get(i);
if (item.board.equals(board)) {
if (item.board.equals(board.code) && item.siteId == board.site.id()) {
return true;
}
}
@ -79,74 +81,69 @@ public class DatabaseSavedReplyManager {
}
public Callable<Void> load() {
return new Callable<Void>() {
@Override
public Void call() throws Exception {
databaseManager.trimTable(helper.savedDao, "savedreply", SAVED_REPLY_TRIM_TRIGGER, SAVED_REPLY_TRIM_COUNT);
final List<SavedReply> all = helper.savedDao.queryForAll();
synchronized (savedRepliesByNo) {
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<SavedReply> items = new ArrayList<>();
items.add(savedReply);
savedRepliesByNo.put(savedReply.no, items);
}
return () -> {
databaseManager.trimTable(helper.savedDao, "savedreply",
SAVED_REPLY_TRIM_TRIGGER, SAVED_REPLY_TRIM_COUNT);
final List<SavedReply> all = helper.savedDao.queryForAll();
synchronized (savedRepliesByNo) {
savedRepliesByNo.clear();
for (int i = 0; i < all.size(); i++) {
SavedReply savedReply = all.get(i);
savedReply.site = Sites.forId(savedReply.siteId);
List<SavedReply> list = savedRepliesByNo.get(savedReply.no);
if (list == null) {
list = new ArrayList<>(1);
savedRepliesByNo.put(savedReply.no, list);
}
list.add(savedReply);
}
return null;
}
return null;
};
}
public Callable<Void> clearSavedReplies() {
return new Callable<Void>() {
@Override
public Void call() throws Exception {
long start = Time.startTiming();
TableUtils.clearTable(helper.getConnectionSource(), SavedReply.class);
synchronized (savedRepliesByNo) {
savedRepliesByNo.clear();
}
Time.endTiming("Clear saved replies", start);
return null;
return () -> {
long start = Time.startTiming();
TableUtils.clearTable(helper.getConnectionSource(), SavedReply.class);
synchronized (savedRepliesByNo) {
savedRepliesByNo.clear();
}
Time.endTiming("Clear saved replies", start);
return null;
};
}
public Callable<SavedReply> saveReply(final SavedReply savedReply) {
return new Callable<SavedReply>() {
@Override
public SavedReply call() throws Exception {
helper.savedDao.create(savedReply);
synchronized (savedRepliesByNo) {
if (savedRepliesByNo.containsKey(savedReply.no)) {
savedRepliesByNo.get(savedReply.no).add(savedReply);
} else {
List<SavedReply> items = new ArrayList<>();
items.add(savedReply);
savedRepliesByNo.put(savedReply.no, items);
}
return () -> {
helper.savedDao.create(savedReply);
synchronized (savedRepliesByNo) {
List<SavedReply> list = savedRepliesByNo.get(savedReply.no);
if (list == null) {
list = new ArrayList<>(1);
savedRepliesByNo.put(savedReply.no, list);
}
return savedReply;
list.add(savedReply);
}
return savedReply;
};
}
public Callable<SavedReply> findSavedReply(final String board, final int no) {
return new Callable<SavedReply>() {
@Override
public SavedReply call() throws Exception {
QueryBuilder<SavedReply, Integer> builder = helper.savedDao.queryBuilder();
List<SavedReply> query = builder.where().eq("board", board).and().eq("no", no).query();
return query.isEmpty() ? null : query.get(0);
}
public Callable<SavedReply> findSavedReply(final Board board, final int no) {
return () -> {
QueryBuilder<SavedReply, Integer> builder = helper.savedDao.queryBuilder();
List<SavedReply> query = builder.where()
.eq("site", board.site.id())
.and().eq("board", board)
.and().eq("no", no).query();
return query.isEmpty() ? null : query.get(0);
};
}
}

@ -24,7 +24,6 @@ import org.floens.chan.core.saver.ImageSaveTask;
import org.floens.chan.core.site.SiteManager;
import org.floens.chan.core.site.common.ChanReaderRequest;
import org.floens.chan.core.site.http.HttpCallManager;
import org.floens.chan.core.site.sites.chan4.Chan4;
import org.floens.chan.core.update.UpdateManager;
import org.floens.chan.ui.activity.BoardActivity;
import org.floens.chan.ui.adapter.DrawerAdapter;
@ -99,8 +98,6 @@ import dagger.Provides;
SitesSetupController.class,
BoardSetupController.class,
ReplyLayout.class,
Chan4.class,
},
complete = false,
includes = NetModule.class

@ -21,11 +21,8 @@ import android.util.Pair;
import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.site.Boards;
import org.floens.chan.core.site.Site;
import org.floens.chan.core.site.SiteManager;
import org.floens.chan.core.site.Sites;
import org.floens.chan.utils.Logger;
import java.util.ArrayList;
import java.util.Collections;
@ -36,8 +33,6 @@ import java.util.Observable;
import javax.inject.Inject;
import javax.inject.Singleton;
import de.greenrobot.event.EventBus;
/**
* <p>Keeps track of {@link Board}s in the system.
* <p>There are a few types of sites, those who provide a list of all boards known,
@ -45,7 +40,8 @@ import de.greenrobot.event.EventBus;
* and those who don't provide a board list at all.
* <p>We try to save as much info about boards as possible, this means that we try to save all
* boards we encounter.
* For sites with a small list of boards which does provide a board list api we save all those boards.
* For sites with a small list of boards which does provide a board list api we save all those
* boards.
* <p>All boards have a {@link Board#saved} flag indicating if it should be visible in the user's
* favorite board list, along with a {@link Board#order} in which they appear.
*/
@ -53,57 +49,36 @@ import de.greenrobot.event.EventBus;
public class BoardManager {
private static final String TAG = "BoardManager";
private static final Comparator<Board> ORDER_SORT = new Comparator<Board>() {
@Override
public int compare(Board lhs, Board rhs) {
return lhs.order - rhs.order;
}
};
private static final Comparator<Board> NAME_SORT = new Comparator<Board>() {
@Override
public int compare(Board lhs, Board rhs) {
return lhs.name.compareTo(rhs.name);
}
};
private static final Comparator<Board> ORDER_SORT = (lhs, rhs) -> lhs.order - rhs.order;
private final DatabaseManager databaseManager;
private final SiteManager siteManager;
private final List<Board> savedBoards = new ArrayList<>();
private final List<Pair<Site, List<Board>>> sitesWithBoards = new ArrayList<>();
private final List<Pair<Site, List<Board>>> sitesWithSavedBoards = new ArrayList<>();
private final SavedBoards savedBoardsObservable = new SavedBoards();
@Inject
public BoardManager(DatabaseManager databaseManager, SiteManager siteManager) {
public BoardManager(DatabaseManager databaseManager) {
this.databaseManager = databaseManager;
this.siteManager = siteManager;
updateSavedBoards();
fetchLimitedSitesTheirBoards();
}
public Board getForCode(Site site, String code) {
if (site.boardsType() == Site.BoardsType.DYNAMIC) {
for (Board board : getSavedBoards()) {
if (board.site == site && board.code.equals(code)) {
return board;
}
}
return null;
} else {
return Board.fromSiteNameCode(site, code, code);
}
public void createAll(List<Board> boards) {
databaseManager.runTaskSync(
databaseManager.getDatabaseBoardManager().createAll(boards));
}
public List<Board> getSavedBoards() {
return savedBoards;
}
public SavedBoards getSavedBoardsObservable() {
return savedBoardsObservable;
/**
* Get the board with the same {@code code} for the given site. The board does not need
* to be saved.
*
* @param site the site
* @param code the board code
* @return the board code with the same site and board code, or {@code null} if not found.
*/
public Board getBoard(Site site, String code) {
return databaseManager.runTaskSync(
databaseManager.getDatabaseBoardManager().getBoard(site, code));
}
public List<Board> getSiteBoards(Site site) {
@ -118,6 +93,10 @@ public class BoardManager {
return boards;
}
public SavedBoards getSavedBoardsObservable() {
return savedBoardsObservable;
}
public void saveBoard(Board board) {
setSaved(board, true);
}
@ -128,12 +107,9 @@ public class BoardManager {
public void updateBoardOrder(Board board, int order) {
board.order = order;
databaseManager.runTask(databaseManager.getDatabaseBoardManager().update(board), new DatabaseManager.TaskResult<Void>() {
@Override
public void onComplete(Void result) {
updateSavedBoards();
}
});
// not sync, does not update observable.
databaseManager.runTask(databaseManager.getDatabaseBoardManager().update(board),
result -> updateSavedBoards());
}
private void setSaved(Board board, boolean saved) {
@ -142,137 +118,19 @@ public class BoardManager {
updateSavedBoards();
}
private void fetchLimitedSitesTheirBoards() {
List<Site> sites = Sites.allSites();
for (final Site site : sites) {
if (site.boardsType() == Site.BoardsType.DYNAMIC) {
site.boards(new Site.BoardsListener() {
@Override
public void onBoardsReceived(Boards boards) {
handleBoardsFetch(site, boards);
}
});
}
}
}
private void handleBoardsFetch(Site site, Boards boards) {
Logger.i(TAG, "Got boards for " + site.name());
databaseManager.runTask(databaseManager.getDatabaseBoardManager().createAll(boards.boards));
}
private void updateSavedBoards() {
savedBoards.clear();
savedBoards.addAll(databaseManager.runTaskSync(databaseManager.getDatabaseBoardManager().getSavedBoards()));
sitesWithBoards.clear();
sitesWithSavedBoards.clear();
for (Site site : Sites.allSites()) {
List<Board> siteBoards = getSiteSavedBoards(site);
sitesWithBoards.add(new Pair<>(site, siteBoards));
sitesWithSavedBoards.add(new Pair<>(site, siteBoards));
}
// TODO: remove, use the observable
EventBus.getDefault().post(new BoardsChangedMessage());
savedBoardsObservable.notifyObservers();
}
public class SavedBoards extends Observable {
public List<Pair<Site, List<Board>>> get() {
return sitesWithBoards;
}
}
/*private void appendBoards(Boards response) {
List<Board> boardsToAddWs = new ArrayList<>();
List<Board> boardsToAddNws = new ArrayList<>();
for (int i = 0; i < response.boards.size(); i++) {
Board serverBoard = response.boards.get(i);
Board existing = getBoardByCode(serverBoard.code);
if (existing != null) {
existing.update(serverBoard);
} else {
serverBoard.saved = true;
if (serverBoard.workSafe) {
boardsToAddWs.add(serverBoard);
} else {
boardsToAddNws.add(serverBoard);
}
}
}
Collections.sort(boardsToAddWs, NAME_SORT);
Collections.sort(boardsToAddNws, NAME_SORT);
for (int i = 0; i < boardsToAddWs.size(); i++) {
Board board = boardsToAddWs.get(i);
board.order = boards.size();
boards.add(board);
}
for (int i = 0; i < boardsToAddNws.size(); i++) {
Board board = boardsToAddNws.get(i);
board.order = boards.size();
boards.add(board);
}
saveDatabase();
update(true);
}
// Thread-safe
private Board getBoardByCode(String code) {
synchronized (boardsByCode) {
return boardsByCode.get(code);
}
}
public List<Board> getAllBoards() {
return boards;
}
public List<Board> getSavedBoards() {
return savedBoards;
}
public void flushOrderAndSaved() {
saveDatabase();
update(true);
}*/
/*private void update(boolean notify) {
savedBoards.clear();
savedBoards.addAll(filterSaved(boards));
synchronized (boardsByCode) {
boardsByCode.clear();
for (Board board : boards) {
boardsByCode.put(board.code, board);
}
}
if (notify) {
EventBus.getDefault().post(new BoardsChangedMessage());
}
}*/
/*private void saveDatabase() {
databaseManager.runTask(databaseManager.getDatabaseBoardManager().setBoards(boards));
}*/
/*private List<Board> filterSaved(List<Board> all) {
List<Board> saved = new ArrayList<>(all.size());
for (int i = 0; i < all.size(); i++) {
Board board = all.get(i);
if (board.saved) {
saved.add(board);
}
return sitesWithSavedBoards;
}
Collections.sort(saved, ORDER_SORT);
return saved;
}*/
public static class BoardsChangedMessage {
}
}

@ -20,22 +20,41 @@ package org.floens.chan.core.model.orm;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import org.floens.chan.core.site.Site;
@DatabaseTable(tableName = "savedreply")
public class SavedReply {
public SavedReply() {
}
@Deprecated
public SavedReply(String board, int no, String password) {
this.board = board;
this.no = no;
this.password = password;
}
public static SavedReply fromSiteBoardNoPassword(Site site, Board board, int no,
String password) {
SavedReply savedReply = new SavedReply();
savedReply.siteId = site.id();
savedReply.site = site;
savedReply.board = board.code;
savedReply.no = no;
savedReply.password = password;
return savedReply;
}
@DatabaseField(generatedId = true)
private int id;
@DatabaseField(columnName = "site")
public int site;
public int siteId;
/**
* The site this board belongs to, loaded with {@link #siteId} in the database manager.
*/
public transient Site site;
@DatabaseField(index = true, canBeNull = false)
public String board;

@ -17,11 +17,19 @@
*/
package org.floens.chan.core.model.orm;
import android.util.Pair;
import com.google.gson.Gson;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.json.site.SiteUserSettings;
@DatabaseTable(tableName = "site")
public class SiteModel {
private static final Gson gson = new Gson();
@DatabaseField(generatedId = true, allowGeneratedIdInsert = true)
public int id;
@ -33,4 +41,16 @@ public class SiteModel {
public SiteModel() {
}
public void storeConfigFields(SiteConfig config, SiteUserSettings userSettings) {
this.configuration = gson.toJson(config);
this.userSettings = gson.toJson(userSettings);
}
public Pair<SiteConfig, SiteUserSettings> loadConfigFields() {
return Pair.create(
gson.fromJson(this.configuration, SiteConfig.class),
gson.fromJson(this.userSettings, SiteUserSettings.class)
);
}
}

@ -28,7 +28,6 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@ -105,9 +104,9 @@ public class BoardSetupPresenter {
}
} else {
for (String suggestion : selectedSuggestions) {
// TODO(multisite)
Board board = new Board(site, suggestion, suggestion, true, true);
Board board = site.createBoard(suggestion, suggestion);
boardManager.saveBoard(board);
boardManager.updateBoardOrder(board, savedBoards.size());
count++;
}
}
@ -164,53 +163,46 @@ public class BoardSetupPresenter {
final String query = userQuery == null ? null :
userQuery.replace("/", "").replace("\\", "");
suggestionCall = BackgroundUtils.runWithExecutor(executor, new Callable<List<BoardSuggestion>>() {
@Override
public List<BoardSuggestion> call() throws Exception {
List<BoardSuggestion> suggestions = new ArrayList<>();
if (site.boardsType() == Site.BoardsType.DYNAMIC) {
List<Board> siteBoards = boardManager.getSiteBoards(site);
List<Board> allUnsavedBoards = new ArrayList<>();
for (Board siteBoard : siteBoards) {
if (!siteBoard.saved) {
allUnsavedBoards.add(siteBoard);
}
suggestionCall = BackgroundUtils.runWithExecutor(executor, () -> {
List<BoardSuggestion> suggestions = new ArrayList<>();
if (site.boardsType() == Site.BoardsType.DYNAMIC) {
List<Board> siteBoards = boardManager.getSiteBoards(site);
List<Board> allUnsavedBoards = new ArrayList<>();
for (Board siteBoard : siteBoards) {
if (!siteBoard.saved) {
allUnsavedBoards.add(siteBoard);
}
}
List<Board> toSuggest;
if (query == null || query.equals("")) {
toSuggest = new ArrayList<>(allUnsavedBoards.size());
for (Board b : allUnsavedBoards) {
if (b.workSafe) toSuggest.add(b);
}
for (Board b : allUnsavedBoards) {
if (!b.workSafe) toSuggest.add(b);
}
toSuggest = allUnsavedBoards;
} else {
toSuggest = BoardHelper.search(allUnsavedBoards, query);
List<Board> toSuggest;
if (query == null || query.equals("")) {
toSuggest = new ArrayList<>(allUnsavedBoards.size());
for (Board b : allUnsavedBoards) {
if (b.workSafe) toSuggest.add(b);
}
for (Board board : toSuggest) {
BoardSuggestion suggestion = new BoardSuggestion(board);
suggestions.add(suggestion);
for (Board b : allUnsavedBoards) {
if (!b.workSafe) toSuggest.add(b);
}
} else {
if (query != null && !query.equals("")) {
suggestions.add(new BoardSuggestion(query));
}
toSuggest = BoardHelper.search(allUnsavedBoards, query);
}
return suggestions;
for (Board board : toSuggest) {
BoardSuggestion suggestion = new BoardSuggestion(board);
suggestions.add(suggestion);
}
} else {
if (query != null && !query.equals("")) {
suggestions.add(new BoardSuggestion(query));
}
}
}, new BackgroundUtils.BackgroundResult<List<BoardSuggestion>>() {
@Override
public void onResult(List<BoardSuggestion> result) {
updateSuggestions(result);
if (addCallback != null) {
addCallback.updateSuggestions();
}
return suggestions;
}, result -> {
updateSuggestions(result);
if (addCallback != null) {
addCallback.updateSuggestions();
}
});
}

@ -74,7 +74,10 @@ public class BrowsePresenter implements Observer {
}
public void loadWithDefaultBoard() {
loadBoard(firstBoard());
Board first = firstBoard();
if (first != null) {
loadBoard(first);
}
}
public void onBoardsFloatingMenuSiteClicked(Site site) {
@ -96,13 +99,7 @@ public class BrowsePresenter implements Observer {
}
private boolean hasBoards() {
for (Pair<Site, List<Board>> siteListPair : savedBoardsObservable.get()) {
if (!siteListPair.second.isEmpty()) {
return true;
}
}
return false;
return firstBoard() != null;
}
private Board firstBoard() {

@ -224,7 +224,8 @@ public class ReplyPresenter implements CaptchaCallback, ImagePickDelegate.ImageP
}
}
SavedReply savedReply = new SavedReply(loadable.boardCode, replyResponse.postNo, replyResponse.password);
SavedReply savedReply = SavedReply.fromSiteBoardNoPassword(
loadable.site, loadable.board, replyResponse.postNo, replyResponse.password);
databaseManager.runTask(databaseManager.getDatabaseSavedReplyManager().saveReply(savedReply));
switchPage(Page.INPUT, false);

@ -450,7 +450,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
}
if (loadable.site.feature(Site.Feature.POST_DELETE) &&
databaseManager.getDatabaseSavedReplyManager().isSaved(post.boardId, post.no)) {
databaseManager.getDatabaseSavedReplyManager().isSaved(post.board, post.no)) {
menu.add(new FloatingMenuItem(POST_OPTION_DELETE, R.string.delete));
}
@ -496,7 +496,8 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
requestDeletePost(post);
break;
case POST_OPTION_SAVE:
SavedReply savedReply = new SavedReply(post.boardId, post.no, "");
SavedReply savedReply = SavedReply.fromSiteBoardNoPassword(
post.board.site, post.board, post.no, "");
databaseManager.runTask(databaseManager.getDatabaseSavedReplyManager().saveReply(savedReply));
break;
case POST_OPTION_PIN:
@ -606,7 +607,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
threadPresenterCallback.showDeleting();
SavedReply reply = databaseManager.runTaskSync(
databaseManager.getDatabaseSavedReplyManager().findSavedReply(post.boardId, post.no)
databaseManager.getDatabaseSavedReplyManager().findSavedReply(post.board, post.no)
);
if (reply != null) {
Site site = loadable.getSite();
@ -634,7 +635,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
private void requestDeletePost(Post post) {
SavedReply reply = databaseManager.runTaskSync(
databaseManager.getDatabaseSavedReplyManager().findSavedReply(post.boardId, post.no)
databaseManager.getDatabaseSavedReplyManager().findSavedReply(post.board, post.no)
);
if (reply != null) {
threadPresenterCallback.confirmPostDelete(post);

@ -102,8 +102,18 @@ public interface Site {
INFINITE
}
/**
* Initialize the site with the given id, config, and userSettings.
* <p><b>Note: do not use any managers at this point, because they rely on the sites being initialized.
* Instead, use {@link #postInitialize()}</b>
* @param id the site id
* @param config the site config
* @param userSettings the site user settings
*/
void initialize(int id, SiteConfig config, SiteUserSettings userSettings);
void postInitialize();
/**
* Global positive (>0) integer that uniquely identifies this site.<br>
* Use the id received from {@link #initialize(int, SiteConfig, SiteUserSettings)}.
@ -136,13 +146,26 @@ public interface Site {
void onBoardsReceived(Boards boards);
}
/**
* Return the board for this site with the given {@code code}.
* <p>This does not need to create the board if it doesn't exist. This is important for
* sites that have a board type different to DYNAMIC. Returning from the database is
* enough.</p>
*
* @param code the board code
* @return a board with the board code, or {@code null}.
*/
Board board(String code);
interface BoardListener {
void onBoardReceived(Board board);
void onBoardNonexistent();
}
/**
* Create a new board with the specified {@code code} and {@code name}.
* <p>This is only applicable to sites with a board type other than DYNAMIC.</p>
*
* @param name the name of the board.
* @param code the code to create the board with.
* @return the created board.
*/
Board createBoard(String name, String code);
ChanReader chanReader();

@ -18,14 +18,29 @@
package org.floens.chan.core.site;
import com.android.volley.RequestQueue;
import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.json.site.SiteUserSettings;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.site.http.HttpCallManager;
import java.util.Collections;
import dagger.ObjectGraph;
import static org.floens.chan.Chan.getGraph;
public abstract class SiteBase implements Site {
protected int id;
protected SiteConfig config;
protected SiteUserSettings userSettings;
protected HttpCallManager httpCallManager;
protected RequestQueue requestQueue;
protected BoardManager boardManager;
@Override
public void initialize(int id, SiteConfig config, SiteUserSettings userSettings) {
this.id = id;
@ -33,8 +48,38 @@ public abstract class SiteBase implements Site {
this.userSettings = userSettings;
}
@Override
public void postInitialize() {
ObjectGraph graph = getGraph();
httpCallManager = graph.get(HttpCallManager.class);
requestQueue = graph.get(RequestQueue.class);
boardManager = graph.get(BoardManager.class);
if (boardsType() == BoardsType.DYNAMIC) {
boards(boards -> boardManager.createAll(boards.boards));
}
}
@Override
public int id() {
return id;
}
@Override
public Board board(String code) {
return boardManager.getBoard(this, code);
}
@Override
public Board createBoard(String name, String code) {
Board existing = board(code);
if (existing != null) {
return existing;
}
Board board = Board.fromSiteNameCode(this, name, code);
boardManager.createAll(Collections.singletonList(board));
return board;
}
}

@ -20,9 +20,6 @@ package org.floens.chan.core.site;
import android.util.Pair;
import com.google.gson.Gson;
import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.json.site.SiteUserSettings;
import org.floens.chan.core.model.orm.SiteModel;
@ -36,16 +33,17 @@ import javax.inject.Singleton;
@Singleton
public class SiteManager {
@Inject
DatabaseManager databaseManager;
private SiteRepository siteRepository;
private SiteResolver resolver;
private Gson gson = new Gson();
private boolean initialized = false;
private boolean addSiteForLegacy = false;
@Inject
public SiteManager() {
resolver = new SiteResolver();
public SiteManager(SiteRepository siteRepository,
SiteResolver resolver) {
this.siteRepository = siteRepository;
this.resolver = resolver;
}
public void addSite(String url, SiteAddCallback callback) {
@ -62,125 +60,90 @@ public class SiteManager {
return;
}
addSiteFromClass(siteClass, callback);
}
public void addSiteFromClass(Class<? extends Site> siteClass, SiteAddCallback callback) {
Site site = instantiateSiteClass(siteClass);
createNewSite(site);
List<Site> newAllSites = new ArrayList<>(Sites.allSites());
newAllSites.add(site);
setAvailableSites(newAllSites);
loadSites();
callback.onSiteAdded(site);
}
/**
* Called from the DatabaseHelper when upgrading to the tables with a site id.
*/
public void addSiteForLegacy() {
addSiteForLegacy = true;
}
public void initialize() {
List<Site> sites = loadSitesFromDatabase();
if (initialized) {
throw new IllegalStateException("Already initialized");
}
if (sites.isEmpty()) {
if (addSiteForLegacy) {
Site site = new Chan4();
SiteConfig config = new SiteConfig();
config.classId = Sites.SITE_CLASSES.indexOfValue(site.getClass());
config.external = false;
SiteUserSettings userSettings = new SiteUserSettings();
SiteModel siteModel = new SiteModel();
storeConfigFields(siteModel, config, userSettings);
siteModel = databaseManager.runTaskSync(databaseManager.getDatabaseSiteManager().add(siteModel));
databaseManager.runTaskSync(databaseManager.getDatabaseSiteManager().updateId(siteModel, 0));
sites.add(site);
SiteModel model = siteRepository.create(config, new SiteUserSettings());
siteRepository.setId(model, 0);
}
setAvailableSites(sites);
loadSites();
}
private void setAvailableSites(List<Site> newAllSites) {
Sites.initialize(newAllSites);
}
private List<Site> loadSitesFromDatabase() {
private void loadSites() {
List<Site> sites = new ArrayList<>();
List<SiteModel> siteModels = databaseManager.runTaskSync(databaseManager.getDatabaseSiteManager().getAll());
for (SiteModel siteModel : siteRepository.all()) {
sites.add(fromModel(siteModel));
}
for (SiteModel siteModel : siteModels) {
Pair<SiteConfig, SiteUserSettings> configPair = loadConfigFields(siteModel);
SiteConfig config = configPair.first;
SiteUserSettings userSettings = configPair.second;
Sites.initialize(sites);
Site site = loadSiteFromModel(siteModel, config, userSettings);
sites.add(site);
for (Site site : sites) {
site.postInitialize();
}
return sites;
}
/**
* Create a new site from the Site instance. This will insert the model for the site
* into the database and calls initialize on the site instance.
*
* @param site the site to add.
*/
private void createNewSite(Site site) {
SiteConfig config = new SiteConfig();
SiteUserSettings settings = new SiteUserSettings();
config.classId = Sites.SITE_CLASSES.indexOfValue(site.getClass());
config.external = false;
SiteUserSettings userSettings = new SiteUserSettings();
SiteModel siteModel = createNewSiteModel(site, config, userSettings);
initializeSite(site, siteModel.id, config, userSettings);
}
private SiteModel createNewSiteModel(Site site, SiteConfig config, SiteUserSettings userSettings) {
SiteModel siteModel = new SiteModel();
storeConfigFields(siteModel, config, userSettings);
siteModel = databaseManager.runTaskSync(databaseManager.getDatabaseSiteManager().add(siteModel));
return siteModel;
}
siteRepository.create(config, settings);
private void storeConfigFields(SiteModel siteModel, SiteConfig config, SiteUserSettings userSettings) {
siteModel.configuration = gson.toJson(config);
siteModel.userSettings = gson.toJson(userSettings);
loadSites();
}
private Pair<SiteConfig, SiteUserSettings> loadConfigFields(SiteModel siteModel) {
SiteConfig config = gson.fromJson(siteModel.configuration, SiteConfig.class);
SiteUserSettings userSettings = gson.fromJson(siteModel.userSettings, SiteUserSettings.class);
return Pair.create(config, userSettings);
}
private Site fromModel(SiteModel siteModel) {
Pair<SiteConfig, SiteUserSettings> configFields = siteModel.loadConfigFields();
SiteConfig config = configFields.first;
SiteUserSettings settings = configFields.second;
private Site loadSiteFromModel(SiteModel model, SiteConfig config, SiteUserSettings userSettings) {
Site site = instantiateSiteFromConfig(config);
return initializeSite(site, model.id, config, userSettings);
}
private Site initializeSite(Site site, int id, SiteConfig config, SiteUserSettings userSettings) {
site.initialize(id, config, userSettings);
Site site = instantiateSiteClass(config.classId);
site.initialize(siteModel.id, config, settings);
return site;
}
private Site instantiateSiteFromConfig(SiteConfig siteConfig) {
int siteClassId = siteConfig.classId;
Class<? extends Site> clazz = Sites.SITE_CLASSES.get(siteClassId);
private Site instantiateSiteClass(int classId) {
Class<? extends Site> clazz = Sites.SITE_CLASSES.get(classId);
if (clazz == null) {
throw new IllegalArgumentException();
throw new IllegalArgumentException("Unknown class id");
}
return instantiateSiteClass(clazz);
}
/**
* Create the instance of the site class. Catches any reflection exceptions as runtime
* exceptions.
* @param clazz the class to instantiate
* @return the instantiated clas
* @throws IllegalArgumentException on reflection exceptions, should never happen.
*/
private Site instantiateSiteClass(Class<? extends Site> clazz) {
Site site;
//noinspection TryWithIdenticalCatches
@ -194,9 +157,6 @@ public class SiteManager {
return site;
}
public static class SiteAlreadyAdded extends IllegalArgumentException {
}
public interface SiteAddCallback {
void onSiteAdded(Site site);

@ -0,0 +1,35 @@
package org.floens.chan.core.site;
import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.json.site.SiteUserSettings;
import org.floens.chan.core.model.orm.SiteModel;
import java.util.List;
import javax.inject.Inject;
public class SiteRepository {
private DatabaseManager databaseManager;
@Inject
public SiteRepository(DatabaseManager databaseManager) {
this.databaseManager = databaseManager;
}
public List<SiteModel> all() {
return databaseManager.runTaskSync(databaseManager.getDatabaseSiteManager().getAll());
}
public SiteModel create(SiteConfig config, SiteUserSettings userSettings) {
SiteModel siteModel = new SiteModel();
siteModel.storeConfigFields(config, userSettings);
databaseManager.runTaskSync(databaseManager.getDatabaseSiteManager().add(siteModel));
return siteModel;
}
public void setId(SiteModel siteModel, int id) {
databaseManager.runTaskSync(databaseManager.getDatabaseSiteManager()
.updateId(siteModel, id));
}
}

@ -20,9 +20,15 @@ package org.floens.chan.core.site;
import java.util.List;
import javax.inject.Inject;
import okhttp3.HttpUrl;
class SiteResolver {
@Inject
public SiteResolver() {
}
SiteResolverResult resolve(String url) {
List<Resolvable> resolvables = Sites.RESOLVABLES;

@ -21,5 +21,9 @@ import org.floens.chan.core.model.Post;
import org.floens.chan.ui.theme.Theme;
public interface ChanParser {
Post parse(Theme theme, Post.Builder builder);
Post parse(Theme theme, Post.Builder builder, Callback callback);
interface Callback {
boolean isSaved(int postNo);
}
}

@ -46,6 +46,7 @@ public class FutabaChanParser implements ChanParser {
private static final String TAG = "FutabaChanParser";
private static final String SAVED_REPLY_SUFFIX = " (You)";
private static final String OP_REPLY_SUFFIX = " (OP)";
public static final String EXTERN_THREAD_LINK_SUFFIX = " \u2192"; // arrow to the right
private FutabaChanParserHandler handler;
@ -54,7 +55,7 @@ public class FutabaChanParser implements ChanParser {
}
@Override
public Post parse(Theme theme, Post.Builder builder) {
public Post parse(Theme theme, Post.Builder builder, Callback callback) {
if (theme == null) {
theme = ThemeHelper.getInstance().getTheme();
}
@ -74,7 +75,7 @@ public class FutabaChanParser implements ChanParser {
parseSpans(theme, builder);
if (builder.comment != null) {
builder.comment = parseComment(theme, builder, builder.comment);
builder.comment = parseComment(theme, builder, builder.comment, callback);
} else {
builder.comment = "";
}
@ -177,7 +178,7 @@ public class FutabaChanParser implements ChanParser {
builder.spans(subjectSpan, nameTripcodeIdCapcodeSpan);
}
private CharSequence parseComment(Theme theme, Post.Builder post, CharSequence commentRaw) {
private CharSequence parseComment(Theme theme, Post.Builder post, CharSequence commentRaw, Callback callback) {
CharSequence total = new SpannableString("");
try {
@ -189,7 +190,7 @@ public class FutabaChanParser implements ChanParser {
List<CharSequence> texts = new ArrayList<>(nodes.size());
for (Node node : nodes) {
CharSequence nodeParsed = parseNode(theme, post, node);
CharSequence nodeParsed = parseNode(theme, post, callback, node);
if (nodeParsed != null) {
texts.add(nodeParsed);
}
@ -203,7 +204,7 @@ public class FutabaChanParser implements ChanParser {
return total;
}
private CharSequence parseNode(Theme theme, Post.Builder post, Node node) {
private CharSequence parseNode(Theme theme, Post.Builder post, Callback callback, Node node) {
if (node instanceof TextNode) {
String text = ((TextNode) node).text();
SpannableString spannable = new SpannableString(text);
@ -219,7 +220,7 @@ public class FutabaChanParser implements ChanParser {
List<CharSequence> texts = new ArrayList<>(innerNodes.size() + 1);
for (Node innerNode : innerNodes) {
CharSequence nodeParsed = parseNode(theme, post, innerNode);
CharSequence nodeParsed = parseNode(theme, post, callback, innerNode);
if (nodeParsed != null) {
texts.add(nodeParsed);
}
@ -246,7 +247,7 @@ public class FutabaChanParser implements ChanParser {
return handler.handleStrong(this, theme, post, (Element) node);
}
case "a": {
CharSequence anchor = parseAnchor(theme, post, (Element) node);
CharSequence anchor = parseAnchor(theme, post, callback, (Element) node);
if (anchor != null) {
return anchor;
} else {
@ -271,38 +272,41 @@ public class FutabaChanParser implements ChanParser {
}
}
private CharSequence parseAnchor(Theme theme, Post.Builder post, Element anchor) {
FutabaChanParserHandler.Link handlerLink = handler.handleAnchor(this, theme, post, anchor);
private CharSequence parseAnchor(Theme theme, Post.Builder post, Callback callback,
Element anchor) {
FutabaChanParserHandler.Link handlerLink =
handler.handleAnchor(this, theme, post, anchor);
if (handlerLink != null) {
SpannableString link = new SpannableString(handlerLink.key);
PostLinkable pl = new PostLinkable(theme, handlerLink.key, handlerLink.value, handlerLink.type);
link.setSpan(pl, 0, link.length(), 0);
post.addLinkable(pl);
if (handlerLink.type == PostLinkable.Type.THREAD) {
handlerLink.key += " \u2192"; // arrow to the right
handlerLink.key += EXTERN_THREAD_LINK_SUFFIX;
}
if (handlerLink.type == PostLinkable.Type.QUOTE) {
int postNo = (int) handlerLink.value;
post.addReplyTo(postNo);
// Append OP when its a reply to OP
// Append (OP) when its a reply to OP
if (postNo == post.opId) {
handlerLink.key += OP_REPLY_SUFFIX;
}
// Append You when it's a reply to an saved reply
// TODO(multisite)
/*if (databaseManager.getDatabaseSavedReplyManager().isSaved(post.board.code, id)) {
key += SAVED_REPLY_SUFFIX;
}*/
// Append (You) when it's a reply to an saved reply
if (callback.isSaved(postNo)) {
handlerLink.key += SAVED_REPLY_SUFFIX;
}
}
SpannableString link = new SpannableString(handlerLink.key);
PostLinkable pl = new PostLinkable(theme, handlerLink.key, handlerLink.value, handlerLink.type);
link.setSpan(pl, 0, link.length(), 0);
post.addLinkable(pl);
return link;
} else {
return null;
}
}
}

@ -53,13 +53,18 @@ class PostParseCallable implements Callable<Post> {
// Process the filters before finish, because parsing the html is dependent on filter matches
processPostFilter(post);
post.isSavedReply(savedReplyManager.isSaved(post.board.code, post.id));
post.isSavedReply(savedReplyManager.isSaved(post.board, post.id));
// if (!post.parse(parser)) {
// Logger.e(TAG, "Incorrect data about post received for post " + post.no);
// return null;
// }
return reader.getParser().parse(null, post);
return reader.getParser().parse(null, post, new ChanParser.Callback() {
@Override
public boolean isSaved(int postNo) {
return savedReplyManager.isSaved(post.board, postNo);
}
});
}
private void processPostFilter(Post.Builder post) {

@ -22,11 +22,6 @@ import android.support.annotation.Nullable;
import android.webkit.CookieManager;
import android.webkit.WebView;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Loadable;
@ -44,7 +39,6 @@ import org.floens.chan.core.site.common.CommonReplyHttpCall;
import org.floens.chan.core.site.common.FutabaChanReader;
import org.floens.chan.core.site.http.DeleteRequest;
import org.floens.chan.core.site.http.HttpCall;
import org.floens.chan.core.site.http.HttpCallManager;
import org.floens.chan.core.site.http.LoginRequest;
import org.floens.chan.core.site.http.LoginResponse;
import org.floens.chan.core.site.http.Reply;
@ -58,13 +52,9 @@ import java.util.Locale;
import java.util.Map;
import java.util.Random;
import javax.inject.Inject;
import okhttp3.HttpUrl;
import okhttp3.Request;
import static org.floens.chan.Chan.getGraph;
public class Chan4 extends SiteBase {
public static final Resolvable RESOLVABLE = new Resolvable() {
@Override
@ -88,12 +78,6 @@ public class Chan4 extends SiteBase {
private static final Random random = new Random();
@Inject
HttpCallManager httpCallManager;
@Inject
RequestQueue requestQueue;
private final SiteEndpoints endpoints = new SiteEndpoints() {
private final HttpUrl a = new HttpUrl.Builder()
.scheme("https")
@ -276,8 +260,6 @@ public class Chan4 extends SiteBase {
private final StringSetting passToken;
public Chan4() {
getGraph().inject(this);
SharedPreferences p = AndroidUtils.getPreferences();
passUser = new StringSetting(p, "preference_pass_token", "");
passPass = new StringSetting(p, "preference_pass_pin", "");
@ -353,40 +335,22 @@ public class Chan4 extends SiteBase {
@Override
public void boards(final BoardsListener listener) {
requestQueue.add(new Chan4BoardsRequest(this, new Response.Listener<List<Board>>() {
@Override
public void onResponse(List<Board> response) {
listener.onBoardsReceived(new Boards(response));
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Logger.e(TAG, "Failed to get boards from server", error);
// API fail, provide some default boards
List<Board> list = new ArrayList<>();
list.add(new Board(Chan4.this, "Technology", "g", true, true));
list.add(new Board(Chan4.this, "Food & Cooking", "ck", true, true));
list.add(new Board(Chan4.this, "Do It Yourself", "diy", true, true));
list.add(new Board(Chan4.this, "Animals & Nature", "an", true, true));
Collections.shuffle(list);
listener.onBoardsReceived(new Boards(list));
}
requestQueue.add(new Chan4BoardsRequest(this, response -> {
listener.onBoardsReceived(new Boards(response));
}, (error) -> {
Logger.e(TAG, "Failed to get boards from server", error);
// API fail, provide some default boards
List<Board> list = new ArrayList<>();
list.add(new Board(Chan4.this, "Technology", "g", true, true));
list.add(new Board(Chan4.this, "Food & Cooking", "ck", true, true));
list.add(new Board(Chan4.this, "Do It Yourself", "diy", true, true));
list.add(new Board(Chan4.this, "Animals & Nature", "an", true, true));
Collections.shuffle(list);
listener.onBoardsReceived(new Boards(list));
}));
}
@Override
public Board board(String code) {
List<Board> allBoards = getGraph().get(BoardManager.class).getSavedBoards();
for (Board board : allBoards) {
if (board.code.equals(code)) {
return board;
}
}
return null;
}
@Override
public SiteEndpoints endpoints() {
return endpoints;

@ -21,7 +21,6 @@ package org.floens.chan.core.site.sites.chan8;
import android.support.annotation.Nullable;
import android.webkit.WebView;
import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Loadable;
@ -236,11 +235,6 @@ public class Chan8 extends SiteBase {
public void boards(BoardsListener boardsListener) {
}
@Override
public Board board(String code) {
return getGraph().get(BoardManager.class).getForCode(this, code);
}
@Override
public ChanReader chanReader() {
FutabaChanParser parser = new FutabaChanParser(new Chan8ParserHandler());

@ -180,11 +180,12 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat
// Not from a state or from an url, launch the setup controller if no boards are setup up yet,
// otherwise load the default saved board.
if (loadDefault) {
if (boardManager.getSavedBoards().isEmpty()) {
/*if (boardManager.getSavedBoards().isEmpty()) {
setupWithNoBoards();
} else {
browseController.loadWithDefaultBoard();
}
}*/
browseController.loadWithDefaultBoard();
}
}

@ -27,9 +27,6 @@ import android.widget.TextView;
import org.floens.chan.R;
import org.floens.chan.controller.Controller;
import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.model.orm.SavedReply;
import java.util.Random;
import javax.inject.Inject;
@ -98,24 +95,6 @@ public class DeveloperSettingsController extends Controller {
resetDbButton.setText("Delete database");
wrapper.addView(resetDbButton);
Button savedReplyDummyAdd = new Button(context);
savedReplyDummyAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
Random r = new Random();
int j = 0;
for (int i = 0; i < 100; i++) {
j += r.nextInt(10000);
SavedReply saved = new SavedReply("g", j, "");
databaseManager.runTask(databaseManager.getDatabaseSavedReplyManager().saveReply(saved));
}
setDbSummary();
}
});
savedReplyDummyAdd.setText("Add test rows to savedReply");
wrapper.addView(savedReplyDummyAdd);
ScrollView scrollView = new ScrollView(context);
scrollView.addView(wrapper);
view = scrollView;

@ -37,6 +37,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import org.floens.chan.R;
import org.floens.chan.core.site.common.ChanParser;
import org.floens.chan.core.site.common.DefaultFutabaChanParserHandler;
import org.floens.chan.core.site.common.FutabaChanParser;
import org.floens.chan.controller.Controller;
@ -106,6 +107,13 @@ public class ThemeSettingsController extends Controller implements View.OnClickL
}
};
private ChanParser.Callback parserCallback = new ChanParser.Callback() {
@Override
public boolean isSaved(int postNo) {
return false;
}
};
private ViewPager pager;
private FloatingActionButton done;
private TextView textView;
@ -251,7 +259,7 @@ public class ThemeSettingsController extends Controller implements View.OnClickL
"http://example.com/" +
"<br>" +
"Phasellus consequat semper sodales. Donec dolor lectus, aliquet nec mollis vel, rutrum vel enim.");
Post post = new FutabaChanParser(new DefaultFutabaChanParserHandler()).parse(theme, builder);
Post post = new FutabaChanParser(new DefaultFutabaChanParserHandler()).parse(theme, builder, parserCallback);
LinearLayout linearLayout = new LinearLayout(themeContext);
linearLayout.setOrientation(LinearLayout.VERTICAL);

@ -31,6 +31,7 @@ import android.text.style.BackgroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@ -45,6 +46,7 @@ import org.floens.chan.core.manager.FilterEngine;
import org.floens.chan.core.manager.FilterType;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Filter;
import org.floens.chan.core.site.Site;
import org.floens.chan.ui.controller.FiltersController;
import org.floens.chan.ui.dialog.ColorPickerView;
import org.floens.chan.ui.drawable.DropdownArrowDrawable;
@ -228,7 +230,12 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
List<SelectLayout.SelectItem<Board>> items = new ArrayList<>();
for (Board board : boardManager.getSavedBoards()) {
List<Board> allSavedBoards = new ArrayList<>();
for (Pair<Site, List<Board>> sites : boardManager.getSavedBoardsObservable().get()) {
allSavedBoards.addAll(sites.second);
}
for (Board board : allSavedBoards) {
String name = BoardHelper.getName(board);
boolean checked = filterEngine.matchesBoard(filter, board);

Loading…
Cancel
Save