From 08df656d05c64e2b32fbd590be62c1a271974c99 Mon Sep 17 00:00:00 2001 From: Floens Date: Thu, 27 Jul 2017 16:13:54 +0200 Subject: [PATCH] Hardcode another site, board adding and removing from the new editor. --- Clover/app/src/main/assets/icons/8chan.png | Bin 0 -> 411 bytes .../core/database/DatabaseBoardManager.java | 42 ++- .../chan/core/database/DatabaseHelper.java | 39 ++- .../chan/core/database/DatabaseManager.java | 9 + .../org/floens/chan/core/di/AppModule.java | 14 +- .../chan/core/manager/BoardManager.java | 86 ++++-- .../org/floens/chan/core/model/orm/Board.java | 44 +-- .../core/presenter/BoardSetupPresenter.java | 109 ++++++++ ...Presenter.java => SiteSetupPresenter.java} | 8 +- .../java/org/floens/chan/core/site/Site.java | 2 +- .../org/floens/chan/core/site/SiteIcon.java | 3 + .../floens/chan/core/site/SiteManager.java | 19 +- .../java/org/floens/chan/core/site/Sites.java | 20 +- .../ChanReaderRequest.java} | 16 +- .../chan4 => common}/PostParseCallable.java | 3 +- .../chan/core/site/sites/chan4/Chan4.java | 5 +- .../chan/core/site/sites/chan8/Chan8.java | 254 ++++++++++++++++++ .../ui/controller/BoardEditController.java | 9 +- .../ui/controller/BoardSetupController.java | 237 +++++++++++----- .../chan/ui/controller/BrowseController.java | 3 +- .../ui/controller/SiteSetupController.java | 17 +- .../chan/ui/drawable/ThumbDrawable.java | 82 ------ .../org/floens/chan/ui/helper/PostHelper.java | 11 +- .../floens/chan/ui/layout/FilterLayout.java | 2 +- .../ui/layout/ThreadSlidingPaneLayout.java | 17 ++ .../org/floens/chan/ui/view/LoadView.java | 1 - .../drawable-hdpi/ic_reorder_black_24dp.png | Bin 0 -> 107 bytes .../drawable-mdpi/ic_reorder_black_24dp.png | Bin 0 -> 82 bytes .../drawable-xhdpi/ic_reorder_black_24dp.png | Bin 0 -> 101 bytes .../drawable-xxhdpi/ic_reorder_black_24dp.png | Bin 0 -> 113 bytes .../ic_reorder_black_24dp.png | Bin 0 -> 116 bytes .../src/main/res/layout/cell_board_edit.xml | 10 +- .../main/res/layout/cell_board_suggestion.xml | 42 +++ .../src/main/res/layout/cell_saved_board.xml | 7 + .../res/layout/controller_board_setup.xml | 5 +- docs/database.txt | 1 + 36 files changed, 865 insertions(+), 252 deletions(-) create mode 100644 Clover/app/src/main/assets/icons/8chan.png create mode 100644 Clover/app/src/main/java/org/floens/chan/core/presenter/BoardSetupPresenter.java rename Clover/app/src/main/java/org/floens/chan/core/presenter/{SetupPresenter.java => SiteSetupPresenter.java} (94%) rename Clover/app/src/main/java/org/floens/chan/core/site/{sites/chan4/Chan4ReaderRequest.java => common/ChanReaderRequest.java} (97%) rename Clover/app/src/main/java/org/floens/chan/core/site/{sites/chan4 => common}/PostParseCallable.java (97%) create mode 100644 Clover/app/src/main/java/org/floens/chan/core/site/sites/chan8/Chan8.java delete mode 100644 Clover/app/src/main/java/org/floens/chan/ui/drawable/ThumbDrawable.java create mode 100644 Clover/app/src/main/res/drawable-hdpi/ic_reorder_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-mdpi/ic_reorder_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xhdpi/ic_reorder_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxhdpi/ic_reorder_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxxhdpi/ic_reorder_black_24dp.png create mode 100644 Clover/app/src/main/res/layout/cell_board_suggestion.xml diff --git a/Clover/app/src/main/assets/icons/8chan.png b/Clover/app/src/main/assets/icons/8chan.png new file mode 100644 index 0000000000000000000000000000000000000000..cabdb1ba884d77c05b9f608906d6c73d4da40f40 GIT binary patch literal 411 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>cM?V0YC*^O8(udK|TNf|KGvC z(-UX}V@Z%-FoVOh8)+a;lDE5y$fWxxO@SQt5>H=O_J{0}BBH!^u9>$3g?v0+977~7 zul2PSYcSwp5?=d1Yw!Qqr7ss%ODa5MW%|N+vY(;(pnHPXjNrS|L$|!G^Ejuo$zqR9 z(DoCn4+_;~U-X!4vne|JF!)uFd>%zZZhlH;S|x4`-MY7nff^V*UHx3vIVCg! E06f@!3jhEB literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseBoardManager.java b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseBoardManager.java index 383ee3dd..4cdd93ff 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseBoardManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseBoardManager.java @@ -20,14 +20,18 @@ public class DatabaseBoardManager { this.helper = helper; } - /** - * Save the boards listed in the database.
- * The boards need to either come from a {@link Site} or from {@link #getBoards(Site)}. - * - * @param boards boards to set or update - * @return void - */ - public Callable setBoards(final List boards) { + public Callable createOrUpdate(final Board board) { + return new Callable() { + @Override + public Board call() throws Exception { + helper.boardsDao.createOrUpdate(board); + + return board; + } + }; + } + + public Callable createAll(final List boards) { return new Callable() { @Override public Void call() throws Exception { @@ -62,6 +66,28 @@ public class DatabaseBoardManager { }; } + public Callable> getSavedBoards() { + return new Callable>() { + @Override + public List call() throws Exception { + List 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> getAllBoards() { return new Callable>() { @Override diff --git a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java index ae4a7abf..6ee26895 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java +++ b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java @@ -44,7 +44,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private static final String TAG = "DatabaseHelper"; private static final String DATABASE_NAME = "ChanDB"; - private static final int DATABASE_VERSION = 23; + private static final int DATABASE_VERSION = 22; public Dao pinDao; public Dao loadableDao; @@ -57,6 +57,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private final Context context; + public boolean isUpgrading = false; + public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); @@ -96,6 +98,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { @Override public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) { Logger.i(TAG, "Upgrading database from " + oldVersion + " to " + newVersion); + isUpgrading = true; + if (oldVersion < 12) { try { boardsDao.executeRawNoArgs("ALTER TABLE board ADD COLUMN perPage INTEGER;"); @@ -210,22 +214,39 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { if (oldVersion < 22) { try { - boardsDao.executeRawNoArgs("ALTER TABLE loadable ADD COLUMN site INTEGER default 0;"); - boardsDao.executeRawNoArgs("ALTER TABLE board ADD COLUMN site INTEGER default 0;"); - boardsDao.executeRawNoArgs("ALTER TABLE savedreply ADD COLUMN site INTEGER default 0;"); - boardsDao.executeRawNoArgs("ALTER TABLE threadhide ADD COLUMN site INTEGER default 0;"); + siteDao.executeRawNoArgs("CREATE TABLE `site` (`configuration` VARCHAR , `id` INTEGER PRIMARY KEY AUTOINCREMENT , `userSettings` VARCHAR );"); } catch (SQLException e) { Logger.e(TAG, "Error upgrading to version 22", e); } - } - if (oldVersion < 23) { +// final Site[] siteRef = new Site[1]; +// getGraph().get(SiteManager.class).addSiteFromClass(Chan4.class, new SiteManager.SiteAddCallback() { +// @Override +// public void onSiteAdded(Site site) { +// siteRef[0] = site; +// } +// +// @Override +// public void onSiteAddFailed(String message) { +// throw new RuntimeException("Error adding site for db upgrade: " + message); +// } +// }); +// +// // Will always be 1 +// int siteId = siteRef[0].id(); + int siteId = 0; + try { - siteDao.executeRawNoArgs("CREATE TABLE `site` (`configuration` VARCHAR , `id` INTEGER PRIMARY KEY AUTOINCREMENT , `userSettings` VARCHAR );"); + boardsDao.executeRawNoArgs("ALTER TABLE loadable ADD COLUMN site INTEGER default " + siteId + ";"); + boardsDao.executeRawNoArgs("ALTER TABLE board ADD COLUMN site INTEGER default " + siteId + ";"); + boardsDao.executeRawNoArgs("ALTER TABLE savedreply ADD COLUMN site INTEGER default " + siteId + ";"); + boardsDao.executeRawNoArgs("ALTER TABLE threadhide ADD COLUMN site INTEGER default " + siteId + ";"); } catch (SQLException e) { - Logger.e(TAG, "Error upgrading to version 23", e); + Logger.e(TAG, "Error upgrading to version 22", e); } } + + isUpgrading = false; } public void reset() { 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 9e4647a2..762c2231 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 @@ -281,6 +281,15 @@ public class DatabaseManager { } private Future executeTask(final Callable taskCallable, final TaskResult taskResult) { + if (helper.isUpgrading) { + try { + taskCallable.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return null; + } + return backgroundExecutor.submit(new Callable() { @Override public T call() { diff --git a/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java b/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java index 0531c588..9738082e 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java +++ b/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java @@ -13,7 +13,8 @@ import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.manager.FilterEngine; import org.floens.chan.core.manager.ReplyManager; -import org.floens.chan.core.presenter.SetupPresenter; +import org.floens.chan.core.presenter.BoardSetupPresenter; +import org.floens.chan.core.presenter.SiteSetupPresenter; import org.floens.chan.core.site.SiteManager; import org.floens.chan.core.manager.WatchManager; import org.floens.chan.core.net.BitmapLruImageCache; @@ -24,12 +25,13 @@ import org.floens.chan.core.receiver.WatchUpdateReceiver; import org.floens.chan.core.saver.ImageSaveTask; import org.floens.chan.core.site.http.HttpCallManager; import org.floens.chan.core.site.sites.chan4.Chan4; -import org.floens.chan.core.site.sites.chan4.Chan4ReaderRequest; +import org.floens.chan.core.site.common.ChanReaderRequest; import org.floens.chan.core.update.UpdateManager; import org.floens.chan.ui.activity.BoardActivity; import org.floens.chan.ui.adapter.DrawerAdapter; import org.floens.chan.ui.adapter.PostsFilter; import org.floens.chan.ui.controller.BoardEditController; +import org.floens.chan.ui.controller.BoardSetupController; import org.floens.chan.ui.controller.BrowseController; import org.floens.chan.ui.controller.DeveloperSettingsController; import org.floens.chan.ui.controller.DrawerController; @@ -38,6 +40,7 @@ import org.floens.chan.ui.controller.HistoryController; import org.floens.chan.ui.controller.ImageViewerController; import org.floens.chan.ui.controller.MainSettingsController; import org.floens.chan.ui.controller.PassSettingsController; +import org.floens.chan.ui.controller.SiteSetupController; import org.floens.chan.ui.controller.ViewThreadController; import org.floens.chan.ui.helper.ImagePickDelegate; import org.floens.chan.ui.layout.FilterLayout; @@ -65,7 +68,7 @@ import dagger.Provides; ChanApplication.class, MainSettingsController.class, ReplyPresenter.class, - Chan4ReaderRequest.class, + ChanReaderRequest.class, ThreadLayout.class, DeveloperSettingsController.class, BoardActivity.class, @@ -92,7 +95,10 @@ import dagger.Provides; WatchManager.PinWatcher.class, UpdateManager.class, SiteManager.class, - SetupPresenter.class, + SiteSetupPresenter.class, + BoardSetupPresenter.class, + SiteSetupController.class, + BoardSetupController.class, Chan4.class, }, diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java b/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java index c0ed715c..270afbde 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java @@ -22,13 +22,11 @@ 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.Sites; +import org.floens.chan.utils.Logger; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.inject.Inject; import javax.inject.Singleton; @@ -36,7 +34,15 @@ import javax.inject.Singleton; import de.greenrobot.event.EventBus; /** - * Keeps track of {@link Board}s that the user has "saved" to their list. + *

Keeps track of {@link Board}s in the system. + *

There are a few types of sites, those who provide a list of all boards known, + * sites where users can create boards and have a very long list of known boards, + * and those who don't provide a board list at all. + *

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

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. */ @Singleton public class BoardManager { @@ -57,24 +63,66 @@ public class BoardManager { }; private final DatabaseManager databaseManager; - private final Site defaultSite; - private final List boards; private final List savedBoards = new ArrayList<>(); - private final Map boardsByCode = new HashMap<>(); @Inject public BoardManager(DatabaseManager databaseManager) { this.databaseManager = databaseManager; - defaultSite = Sites.defaultSite(); - boards = databaseManager.runTaskSync(databaseManager.getDatabaseBoardManager().getBoards(defaultSite)); - if (boards.isEmpty()) { - update(false); + loadBoards(); + + fetchLimitedSitesTheirBoards(); + } + + public List getSavedBoards() { + return savedBoards; + } + + public void saveBoard(Board board) { + board.saved = true; + + board = databaseManager.runTaskSync(databaseManager.getDatabaseBoardManager().createOrUpdate(board)); + + loadBoards(); + } + + public void unsaveBoard(Board board) { + board.saved = false; + + board = databaseManager.runTaskSync(databaseManager.getDatabaseBoardManager().createOrUpdate(board)); + + loadBoards(); + } + + private void loadBoards() { + savedBoards.clear(); + savedBoards.addAll(databaseManager.runTaskSync(databaseManager.getDatabaseBoardManager().getSavedBoards())); + + EventBus.getDefault().post(new BoardsChangedMessage()); + } + + private void fetchLimitedSitesTheirBoards() { + List 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 appendBoards(Boards response) { + private void handleBoardsFetch(Site site, Boards boards) { + Logger.i(TAG, "Got boards for " + site.name()); + + databaseManager.runTask(databaseManager.getDatabaseBoardManager().createAll(boards.boards)); + } + + /*private void appendBoards(Boards response) { List boardsToAddWs = new ArrayList<>(); List boardsToAddNws = new ArrayList<>(); @@ -131,9 +179,9 @@ public class BoardManager { public void flushOrderAndSaved() { saveDatabase(); update(true); - } + }*/ - private void update(boolean notify) { + /*private void update(boolean notify) { savedBoards.clear(); savedBoards.addAll(filterSaved(boards)); synchronized (boardsByCode) { @@ -145,13 +193,13 @@ public class BoardManager { if (notify) { EventBus.getDefault().post(new BoardsChangedMessage()); } - } + }*/ - private void saveDatabase() { + /*private void saveDatabase() { databaseManager.runTask(databaseManager.getDatabaseBoardManager().setBoards(boards)); - } + }*/ - private List filterSaved(List all) { + /*private List filterSaved(List all) { List saved = new ArrayList<>(all.size()); for (int i = 0; i < all.size(); i++) { Board board = all.get(i); @@ -161,7 +209,7 @@ public class BoardManager { } Collections.sort(saved, ORDER_SORT); return saved; - } + }*/ public static class BoardsChangedMessage { } diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/orm/Board.java b/Clover/app/src/main/java/org/floens/chan/core/model/orm/Board.java index 21f8ac73..d21a9ffe 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/orm/Board.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/orm/Board.java @@ -27,18 +27,6 @@ import org.floens.chan.core.site.Site; @DatabaseTable(tableName = "board") public class Board implements SiteReference { - public Board() { - } - - public Board(Site site, String name, String code, boolean saved, boolean workSafe) { - this.siteId = site.id(); - this.site = site; - this.name = name; - this.code = code; - this.saved = saved; - this.workSafe = workSafe; - } - @DatabaseField(generatedId = true) public int id; @@ -50,9 +38,6 @@ public class Board implements SiteReference { */ public transient Site site; - /** - * {@code true} if this board appears in the dropdown, {@code false} otherwise. - */ @DatabaseField public boolean saved = false; @@ -62,12 +47,10 @@ public class Board implements SiteReference { @DatabaseField public int order; - // named key for legacy support - @DatabaseField(columnName = "key") + @DatabaseField(columnName = "key") // named key for legacy support public String name; - // named value for legacy support - @DatabaseField(columnName = "value") + @DatabaseField(columnName = "value") // named value for legacy support // TODO(sec) force filter this to ascii & numbers. public String code; @@ -142,6 +125,29 @@ public class Board implements SiteReference { @DatabaseField public String description; + @Deprecated // public, at least + public Board() { + } + + @Deprecated + public Board(Site site, String name, String code, boolean saved, boolean workSafe) { + this.siteId = site.id(); + this.site = site; + this.name = name; + this.code = code; + this.saved = saved; + this.workSafe = workSafe; + } + + public static Board fromSiteNameCode(Site site, String name, String code) { + Board board = new Board(); + board.siteId = site.id(); + board.site = site; + board.name = name; + board.code = code; + return board; + } + public boolean finish() { if (TextUtils.isEmpty(name) || TextUtils.isEmpty(code) || perPage < 0 || pages < 0) { return false; diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/BoardSetupPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/BoardSetupPresenter.java new file mode 100644 index 00000000..ff851a7a --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/BoardSetupPresenter.java @@ -0,0 +1,109 @@ +/* + * 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.presenter; + + +import org.floens.chan.core.manager.BoardManager; +import org.floens.chan.core.model.orm.Board; +import org.floens.chan.core.site.Site; +import org.floens.chan.core.site.Sites; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import static org.floens.chan.Chan.getGraph; + +public class BoardSetupPresenter { + private Callback callback; + + private final List sites = new ArrayList<>(); + + @Inject + BoardManager boardManager; + + private List savedBoards; + + @Inject + public BoardSetupPresenter() { + getGraph().inject(this); + } + + public void create(Callback callback) { + this.callback = callback; + + sites.addAll(Sites.allSites()); + + loadSavedBoards(); + callback.setSavedBoards(savedBoards); + } + + public void addFromSuggestion(BoardSuggestion suggestion) { + Board board = Board.fromSiteNameCode(suggestion.site, suggestion.key, suggestion.key); + + boardManager.saveBoard(board); + + loadSavedBoards(); + callback.setSavedBoards(savedBoards); + } + + public void move(int from, int to) { + Board item = savedBoards.remove(from); + savedBoards.add(to, item); + + callback.setSavedBoards(savedBoards); + } + + public void remove(int position) { + Board board = savedBoards.remove(position); + + boardManager.unsaveBoard(board); + + loadSavedBoards(); + callback.setSavedBoards(savedBoards); + } + + public List getSuggestionsForQuery(String query) { + List suggestions = new ArrayList<>(); + + for (Site site : sites) { + suggestions.add(new BoardSuggestion(query, site)); + } + + return suggestions; + } + + private void loadSavedBoards() { + savedBoards = new ArrayList<>(boardManager.getSavedBoards()); + } + + public interface Callback { + void setSavedBoards(List savedBoards); + } + + public static class BoardSuggestion { + public final String key; + public final Site site; + + public BoardSuggestion(String key, Site site) { + this.key = key; + this.site = site; + } + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/SetupPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/SiteSetupPresenter.java similarity index 94% rename from Clover/app/src/main/java/org/floens/chan/core/presenter/SetupPresenter.java rename to Clover/app/src/main/java/org/floens/chan/core/presenter/SiteSetupPresenter.java index ae8edb6d..cec53648 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/SetupPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/SiteSetupPresenter.java @@ -29,7 +29,7 @@ import javax.inject.Inject; import static org.floens.chan.Chan.getGraph; -public class SetupPresenter { +public class SiteSetupPresenter { @Inject SiteManager siteManager; @@ -38,14 +38,14 @@ public class SetupPresenter { private List sites = new ArrayList<>(); @Inject - public SetupPresenter() { + public SiteSetupPresenter() { getGraph().inject(this); } public void create(Callback callback) { this.callback = callback; - sites.addAll(Sites.ALL_SITES); + sites.addAll(Sites.allSites()); this.callback.setAddedSites(sites); @@ -80,7 +80,7 @@ public class SetupPresenter { private void siteAdded(Site site) { sites.clear(); - sites.addAll(Sites.ALL_SITES); + sites.addAll(Sites.allSites()); callback.setAddedSites(sites); callback.runSiteAddedAnimation(site); diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/Site.java b/Clover/app/src/main/java/org/floens/chan/core/site/Site.java index 991bd7d3..074cd8dc 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/Site.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/Site.java @@ -88,7 +88,7 @@ public interface Site { */ enum BoardsType { /** - * The site's boards are static, hard-coded in the site. + * The site's boards are static, there is no extra info for a board in the api. */ STATIC, diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/SiteIcon.java b/Clover/app/src/main/java/org/floens/chan/core/site/SiteIcon.java index d6f8547b..f375aa25 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/SiteIcon.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/SiteIcon.java @@ -46,6 +46,9 @@ public class SiteIcon { return siteIcon; } + private SiteIcon() { + } + public void get(SiteIconResult result) { if (assetPath != null) { Bitmap bitmap; diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/SiteManager.java b/Clover/app/src/main/java/org/floens/chan/core/site/SiteManager.java index 6cba35e4..7a9b2f61 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/SiteManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/SiteManager.java @@ -51,9 +51,9 @@ public class SiteManager { public void addSite(String url, SiteAddCallback callback) { SiteResolver.SiteResolverResult resolve = resolver.resolve(url); - Site site; + Class siteClass; if (resolve.match == SiteResolver.SiteResolverResult.Match.BUILTIN) { - site = instantiateSiteClass(resolve.builtinResult); + siteClass = resolve.builtinResult; } else if (resolve.match == SiteResolver.SiteResolverResult.Match.EXTERNAL) { callback.onSiteAddFailed("external todo"); return; @@ -62,11 +62,16 @@ public class SiteManager { return; } + addSiteFromClass(siteClass, callback); + } + + public void addSiteFromClass(Class siteClass, SiteAddCallback callback) { + Site site = instantiateSiteClass(siteClass); site = createNewSite(site); - List newAllSites = new ArrayList<>(Sites.ALL_SITES); + List newAllSites = new ArrayList<>(Sites.allSites()); newAllSites.add(site); - Sites.initialize(newAllSites); + setAvailableSites(newAllSites); callback.onSiteAdded(site); } @@ -91,7 +96,11 @@ public class SiteManager { sites.add(site); } - Sites.initialize(sites); + setAvailableSites(sites); + } + + private void setAvailableSites(List newAllSites) { + Sites.initialize(newAllSites); } private List loadSitesFromDatabase() { diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/Sites.java b/Clover/app/src/main/java/org/floens/chan/core/site/Sites.java index b816b908..673d6b9c 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/Sites.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/Sites.java @@ -3,8 +3,10 @@ package org.floens.chan.core.site; import android.util.SparseArray; import org.floens.chan.core.site.sites.chan4.Chan4; +import org.floens.chan.core.site.sites.chan8.Chan8; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class Sites { @@ -15,15 +17,28 @@ public class Sites { // This differs from the Site.id() id, that id is used for site instance linking, this is just to // find the correct class to use. SITE_CLASSES.put(0, Chan4.class); + + SITE_CLASSES.put(1, Chan8.class); } public static final List RESOLVABLES = new ArrayList<>(); static { RESOLVABLES.add(Chan4.RESOLVABLE); + RESOLVABLES.add(Chan8.RESOLVABLE); } - public static final List ALL_SITES = new ArrayList<>(); + private static List ALL_SITES = Collections.unmodifiableList(new ArrayList()); + + /** + * Return all sites known in the system. + *

This list is immutable. Changes to the known sites cause this function to return a new immutable list + * with the site changes. + * @return list of sites known in the system. + */ + public static List allSites() { + return ALL_SITES; + } @Deprecated private static Site defaultSite; @@ -46,7 +61,6 @@ public class Sites { static void initialize(List sites) { Sites.defaultSite = sites.isEmpty() ? null : sites.get(0); - Sites.ALL_SITES.clear(); - Sites.ALL_SITES.addAll(sites); + Sites.ALL_SITES = Collections.unmodifiableList(new ArrayList<>(sites)); } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReaderRequest.java b/Clover/app/src/main/java/org/floens/chan/core/site/common/ChanReaderRequest.java similarity index 97% rename from Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReaderRequest.java rename to Clover/app/src/main/java/org/floens/chan/core/site/common/ChanReaderRequest.java index e7888062..700ce062 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReaderRequest.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/common/ChanReaderRequest.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.core.site.sites.chan4; +package org.floens.chan.core.site.common; import android.util.JsonReader; @@ -56,8 +56,8 @@ import static org.floens.chan.Chan.getGraph; * This class is highly multithreaded, take good care to not access models that are to be only * changed on the main thread. */ -public class Chan4ReaderRequest extends JsonReaderRequest { - private static final String TAG = "Chan4ReaderRequest"; +public class ChanReaderRequest extends JsonReaderRequest { + private static final String TAG = "ChanReaderRequest"; private static final boolean LOG_TIMING = false; private static final int THREAD_COUNT; @@ -85,7 +85,7 @@ public class Chan4ReaderRequest extends JsonReaderRequest { private List filters; private long startLoad; - public Chan4ReaderRequest(ChanLoaderRequestParams request) { + public ChanReaderRequest(ChanLoaderRequestParams request) { super(getChanUrl(request.loadable).toString(), request.listener, request.errorListener); getGraph().inject(this); @@ -360,7 +360,7 @@ public class Chan4ReaderRequest extends JsonReaderRequest { builder.board(loadable.board); // File - long fileId = 0; + String fileId = null; String fileExt = null; int fileWidth = 0; int fileHeight = 0; @@ -397,7 +397,7 @@ public class Chan4ReaderRequest extends JsonReaderRequest { builder.comment(reader.nextString()); break; case "tim": - fileId = reader.nextLong(); + fileId = reader.nextString(); break; case "time": builder.setUnixTimestampSeconds(reader.nextLong()); @@ -491,9 +491,9 @@ public class Chan4ReaderRequest extends JsonReaderRequest { } SiteEndpoints endpoints = loadable.getSite().endpoints(); - if (fileId != 0 && fileName != null && fileExt != null) { + if (fileId != null && fileName != null && fileExt != null) { Map hack = new HashMap<>(2); - hack.put("tim", String.valueOf(fileId)); + hack.put("tim", fileId); hack.put("ext", fileExt); builder.image(new PostImage.Builder() .originalName(String.valueOf(fileId)) diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/PostParseCallable.java b/Clover/app/src/main/java/org/floens/chan/core/site/common/PostParseCallable.java similarity index 97% rename from Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/PostParseCallable.java rename to Clover/app/src/main/java/org/floens/chan/core/site/common/PostParseCallable.java index c8df3f26..ebc851c8 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/PostParseCallable.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/common/PostParseCallable.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.core.site.sites.chan4; +package org.floens.chan.core.site.common; import org.floens.chan.chan.ChanParser; import org.floens.chan.core.database.DatabaseSavedReplyManager; @@ -27,6 +27,7 @@ import java.util.List; import java.util.concurrent.Callable; // Called concurrently to parse the post html and the filters on it +// Belongs to ChanReaderRequest class PostParseCallable implements Callable { private static final String TAG = "PostParseCallable"; diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java index e53ab491..2454c917 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java @@ -41,6 +41,7 @@ import org.floens.chan.core.site.SiteBase; import org.floens.chan.core.site.SiteEndpoints; import org.floens.chan.core.site.SiteIcon; import org.floens.chan.core.site.SiteRequestModifier; +import org.floens.chan.core.site.common.ChanReaderRequest; import org.floens.chan.core.site.http.DeleteRequest; import org.floens.chan.core.site.http.HttpCall; import org.floens.chan.core.site.http.HttpCallManager; @@ -376,7 +377,7 @@ public class Chan4 extends SiteBase { @Override public Board board(String code) { - List allBoards = getGraph().get(BoardManager.class).getAllBoards(); + List allBoards = getGraph().get(BoardManager.class).getSavedBoards(); for (Board board : allBoards) { if (board.code.equals(code)) { return board; @@ -403,7 +404,7 @@ public class Chan4 extends SiteBase { @Override public ChanLoaderRequest loaderRequest(ChanLoaderRequestParams request) { - return new ChanLoaderRequest(new Chan4ReaderRequest(request)); + return new ChanLoaderRequest(new ChanReaderRequest(request)); } @Override diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan8/Chan8.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan8/Chan8.java new file mode 100644 index 00000000..0ea11daa --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan8/Chan8.java @@ -0,0 +1,254 @@ +/* + * 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.site.sites.chan8; + + +import android.support.annotation.Nullable; +import android.webkit.WebView; + +import org.floens.chan.chan.ChanLoaderRequest; +import org.floens.chan.chan.ChanLoaderRequestParams; +import org.floens.chan.core.model.Post; +import org.floens.chan.core.model.orm.Board; +import org.floens.chan.core.model.orm.Loadable; +import org.floens.chan.core.site.Resolvable; +import org.floens.chan.core.site.Site; +import org.floens.chan.core.site.SiteAuthentication; +import org.floens.chan.core.site.SiteBase; +import org.floens.chan.core.site.SiteEndpoints; +import org.floens.chan.core.site.SiteIcon; +import org.floens.chan.core.site.SiteRequestModifier; +import org.floens.chan.core.site.common.ChanReaderRequest; +import org.floens.chan.core.site.http.DeleteRequest; +import org.floens.chan.core.site.http.HttpCall; +import org.floens.chan.core.site.http.LoginRequest; +import org.floens.chan.core.site.http.Reply; + +import java.util.Locale; +import java.util.Map; + +import okhttp3.HttpUrl; +import okhttp3.Request; + +public class Chan8 extends SiteBase { + public static final Resolvable RESOLVABLE = new Resolvable() { + @Override + public ResolveResult resolve(String value) { + if (value.equals("8chan")) { + return ResolveResult.NAME_MATCH; + } else if (value.equals("https://8ch.net/")) { + return ResolveResult.FULL_MATCH; + } else { + return ResolveResult.NO; + } + } + + @Override + public Class getSiteClass() { + return Chan8.class; + } + }; + + private final SiteEndpoints endpoints = new SiteEndpoints() { + private final HttpUrl root = new HttpUrl.Builder() + .scheme("https") + .host("8ch.net") + .build(); + + private final HttpUrl media = new HttpUrl.Builder() + .scheme("https") + .host("media.8ch.net") + .build(); + + @Override + public HttpUrl catalog(Board board) { + return root.newBuilder() + .addPathSegment(board.code) + .addPathSegment("catalog.json") + .build(); + } + + @Override + public HttpUrl thread(Board board, Loadable loadable) { + return root.newBuilder() + .addPathSegment(board.code) + .addPathSegment("res") + .addPathSegment(loadable.no + ".json") + .build(); + } + + @Override + public HttpUrl imageUrl(Post.Builder post, Map arg) { + return root.newBuilder() + .addPathSegment("file_store") + .addPathSegment(arg.get("tim") + "." + arg.get("ext")) + .build(); + } + + @Override + public HttpUrl thumbnailUrl(Post.Builder post, boolean spoiler, Map arg) { + return root.newBuilder() + .addPathSegment("file_store") + .addPathSegment("thumb") + .addPathSegment(arg.get("tim") + "." + arg.get("ext")) + .build(); + } + + @Override + public HttpUrl icon(Post.Builder post, String icon, Map arg) { + HttpUrl.Builder stat = root.newBuilder().addPathSegment("static"); + + switch (icon) { + case "country": + stat.addPathSegment("flags"); + stat.addPathSegment(arg.get("country_code").toLowerCase(Locale.ENGLISH) + ".png"); + break; + } + + return stat.build(); + } + + @Override + public HttpUrl boards() { + return null; + } + + @Override + public HttpUrl reply(Loadable loadable) { + return null; + } + + @Override + public HttpUrl delete(Post post) { + return null; + } + + @Override + public HttpUrl report(Post post) { + return null; + } + + @Override + public HttpUrl login() { + return null; + } + }; + + private SiteRequestModifier siteRequestModifier = new SiteRequestModifier() { + @Override + public void modifyHttpCall(HttpCall httpCall, Request.Builder requestBuilder) { + } + + @SuppressWarnings("deprecation") + @Override + public void modifyWebView(WebView webView) { + } + }; + + private SiteAuthentication authentication = new SiteAuthentication() { + @Override + public boolean requireAuthentication(AuthenticationRequestType type) { + return false; + } + }; + + @Override + public String name() { + return "8chan"; + } + + @Override + public SiteIcon icon() { + return SiteIcon.fromAssets("icons/8chan.png"); + } + + @Override + public boolean feature(Feature feature) { + return false; + } + + @Override + public boolean boardFeature(BoardFeature boardFeature, Board board) { + return false; + } + + @Override + public SiteEndpoints endpoints() { + return endpoints; + } + + @Override + public SiteRequestModifier requestModifier() { + return siteRequestModifier; + } + + @Override + public SiteAuthentication authentication() { + return authentication; + } + + @Override + public BoardsType boardsType() { + return BoardsType.INFINITE; + } + + @Override + public String desktopUrl(Loadable loadable, @Nullable Post post) { + return "https://8ch.net/"; + } + + @Override + public void boards(BoardsListener boardsListener) { + } + + @Override + public Board board(String code) { + return null; + } + + @Override + public ChanLoaderRequest loaderRequest(ChanLoaderRequestParams request) { + return new ChanLoaderRequest(new ChanReaderRequest(request)); + } + + @Override + public void post(Reply reply, PostListener postListener) { + } + + @Override + public void delete(DeleteRequest deleteRequest, DeleteListener deleteListener) { + } + + @Override + public void login(LoginRequest loginRequest, LoginListener loginListener) { + } + + @Override + public void logout() { + } + + @Override + public boolean isLoggedIn() { + return false; + } + + @Override + public LoginRequest getLoginDetails() { + return null; + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java index e4a035b2..c0f3ed90 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java @@ -44,7 +44,6 @@ import org.floens.chan.R; import org.floens.chan.controller.Controller; import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.model.orm.Board; -import org.floens.chan.ui.drawable.ThumbDrawable; import org.floens.chan.ui.helper.BoardHelper; import org.floens.chan.ui.toolbar.ToolbarMenu; import org.floens.chan.ui.toolbar.ToolbarMenuItem; @@ -182,7 +181,8 @@ public class BoardEditController extends Controller implements View.OnClickListe boards.get(i).order = i; } - boardManager.flushOrderAndSaved(); + // TODO(multisite) +// boardManager.flushOrderAndSaved(); } @Override @@ -243,7 +243,7 @@ public class BoardEditController extends Controller implements View.OnClickListe } // Normal add - List all = boardManager.getAllBoards(); + List all = boardManager.getSavedBoards(); for (Board board : all) { if (board.code.equals(value)) { board.saved = true; @@ -385,7 +385,7 @@ public class BoardEditController extends Controller implements View.OnClickListe } }*/ List s = new ArrayList<>(); - for (Board b : boardManager.getAllBoards()) { + for (Board b : boardManager.getSavedBoards()) { if (!haveBoard(b.code)/* && (showUnsafe || b.workSafe)*/) { s.add(b); } @@ -454,7 +454,6 @@ public class BoardEditController extends Controller implements View.OnClickListe thumb = (ImageView) itemView.findViewById(R.id.thumb); text = (TextView) itemView.findViewById(R.id.text); description = (TextView) itemView.findViewById(R.id.description); - thumb.setImageDrawable(new ThumbDrawable()); thumb.setOnTouchListener(new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java index 6cc1d080..6e92acf2 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java @@ -19,34 +19,49 @@ package org.floens.chan.ui.controller; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; +import android.widget.AdapterView; import android.widget.AutoCompleteTextView; +import android.widget.BaseAdapter; +import android.widget.Filter; +import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; import org.floens.chan.R; import org.floens.chan.controller.Controller; +import org.floens.chan.core.model.orm.Board; +import org.floens.chan.core.presenter.BoardSetupPresenter; +import org.floens.chan.core.site.SiteIcon; -import java.io.IOException; import java.util.ArrayList; import java.util.List; -import static org.floens.chan.utils.AndroidUtils.getAppContext; -import static org.floens.chan.utils.AndroidUtils.getRes; +import javax.inject.Inject; + +import static org.floens.chan.Chan.getGraph; +import static org.floens.chan.ui.helper.PostHelper.formatBoardCodeAndName; +import static org.floens.chan.utils.AndroidUtils.getAttrColor; + +public class BoardSetupController extends Controller implements View.OnClickListener, AdapterView.OnItemClickListener, BoardSetupPresenter.Callback { + @Inject + BoardSetupPresenter presenter; -public class BoardSetupController extends Controller implements View.OnClickListener { private AutoCompleteTextView code; private RecyclerView savedBoardsRecycler; - private SavedBoardsAdapter adapter; + private SuggestBoardsAdapter suggestAdapter; + + private SavedBoardsAdapter savedAdapter; + private ItemTouchHelper itemTouchHelper; public BoardSetupController(Context context) { super(context); @@ -56,71 +71,146 @@ public class BoardSetupController extends Controller implements View.OnClickList public void onCreate() { super.onCreate(); + getGraph().inject(this); + view = inflateRes(R.layout.controller_board_setup); navigationItem.setTitle(R.string.saved_boards_title); + navigationItem.swipeable = false; code = (AutoCompleteTextView) view.findViewById(R.id.code); + code.setOnItemClickListener(this); savedBoardsRecycler = (RecyclerView) view.findViewById(R.id.boards_recycler); savedBoardsRecycler.setLayoutManager(new LinearLayoutManager(context)); - adapter = new SavedBoardsAdapter(); - savedBoardsRecycler.setAdapter(adapter); - - List savedBoards = new ArrayList<>(); - for (int board = 0; board < 5; board++) { - savedBoards.add(new SavedBoard("foo - " + board, board)); - } - - adapter.setSavedBoards(savedBoards); - - List foo = new ArrayList() {{ - add("foo"); - add("foo"); - add("foo"); - add("foo"); - add("foo"); - add("foo"); - add("foo"); - add("foo"); - add("foo"); - add("foo"); - add("foo"); - add("foo"); - add("foo"); - add("foo"); - add("bar"); - add("baz"); - }}; - - ArrayAdapter adapter = new ArrayAdapter<>(context, android.R.layout.simple_dropdown_item_1line, foo); - code.setAdapter(adapter); + savedAdapter = new SavedBoardsAdapter(); + savedBoardsRecycler.setAdapter(savedAdapter); + + itemTouchHelper = new ItemTouchHelper(touchHelperCallback); + itemTouchHelper.attachToRecyclerView(savedBoardsRecycler); + + suggestAdapter = new SuggestBoardsAdapter(); + + code.setAdapter(suggestAdapter); code.setThreshold(1); + + presenter.create(this); + } + + private ItemTouchHelper.SimpleCallback touchHelperCallback = new ItemTouchHelper.SimpleCallback( + ItemTouchHelper.UP | ItemTouchHelper.DOWN, + ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT + ) { + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + int from = viewHolder.getAdapterPosition(); + int to = target.getAdapterPosition(); + + presenter.move(from, to); + + return true; + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { + int position = viewHolder.getAdapterPosition(); + + presenter.remove(position); + } + }; + + @Override + public void setSavedBoards(List savedBoards) { + savedAdapter.setSavedBoards(savedBoards); } @Override public void onClick(View v) { } - private class SavedBoard { - private String title; - private int id; + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + BoardSetupPresenter.BoardSuggestion suggestion = suggestAdapter.getSuggestion(position); + + presenter.addFromSuggestion(suggestion); + } + + private class SuggestBoardsAdapter extends BaseAdapter implements Filterable { + private List suggestions = new ArrayList<>(); - public SavedBoard(String title, int id) { - this.title = title; - this.id = id; + @Override + public int getCount() { + return suggestions.size(); + } + + @Override + public String getItem(int position) { + return getSuggestion(position).key; + } + + public BoardSetupPresenter.BoardSuggestion getSuggestion(int position) { + return suggestions.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = LayoutInflater.from(context).inflate(R.layout.cell_board_suggestion, parent, false); + + final ImageView image = (ImageView) view.findViewById(R.id.image); + TextView text = (TextView) view.findViewById(R.id.text); + + BoardSetupPresenter.BoardSuggestion suggestion = getSuggestion(position); + + final SiteIcon icon = suggestion.site.icon(); + icon.get(new SiteIcon.SiteIconResult() { + @Override + public void onSiteIcon(SiteIcon siteIcon, Drawable icon) { + // TODO: don't if recycled + image.setImageDrawable(icon); + } + }); + + text.setText(suggestion.site.name() + " \u2013 /" + suggestion.key + "/"); + + return view; + } + + @Override + public Filter getFilter() { + return new Filter() { + @Override + protected FilterResults performFiltering(CharSequence constraint) { + // Invoked on a worker thread, do not use. + return null; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + suggestions.clear(); + + if (constraint != null) { + suggestions.addAll(presenter.getSuggestionsForQuery(constraint.toString())); + } + + notifyDataSetChanged(); + } + }; } } private class SavedBoardsAdapter extends RecyclerView.Adapter { - private List savedBoards = new ArrayList<>(); + private List savedBoards; public SavedBoardsAdapter() { setHasStableIds(true); } - private void setSavedBoards(List savedBoards) { - this.savedBoards.clear(); - this.savedBoards.addAll(savedBoards); + private void setSavedBoards(List savedBoards) { + this.savedBoards = savedBoards; notifyDataSetChanged(); } @@ -136,7 +226,7 @@ public class BoardSetupController extends Controller implements View.OnClickList @Override public void onBindViewHolder(SavedBoardCell holder, int position) { - SavedBoard savedBoard = savedBoards.get(position); + Board savedBoard = savedBoards.get(position); holder.setSavedBoard(savedBoard); } @@ -149,30 +239,43 @@ public class BoardSetupController extends Controller implements View.OnClickList private class SavedBoardCell extends RecyclerView.ViewHolder { private ImageView image; private TextView text; + private SiteIcon siteIcon; + private ImageView reorder; public SavedBoardCell(View itemView) { super(itemView); image = (ImageView) itemView.findViewById(R.id.image); text = (TextView) itemView.findViewById(R.id.text); + reorder = (ImageView) itemView.findViewById(R.id.reorder); + + Drawable drawable = DrawableCompat.wrap(context.getResources().getDrawable(R.drawable.ic_reorder_black_24dp)).mutate(); + DrawableCompat.setTint(drawable, getAttrColor(context, R.attr.text_color_hint)); + reorder.setImageDrawable(drawable); + + reorder.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + itemTouchHelper.startDrag(SavedBoardCell.this); + } + return false; + } + }); } - public void setSavedBoard(SavedBoard savedBoard) { - Bitmap bitmap; - try { - BitmapFactory.Options opts = new BitmapFactory.Options(); - opts.inScaled = false; - bitmap = BitmapFactory.decodeStream(getAppContext().getAssets().open("icons/4chan.png"), null, opts); - } catch (IOException e) { - throw new RuntimeException(e); - } - - BitmapDrawable drawable = new BitmapDrawable(getRes(), bitmap); - drawable = (BitmapDrawable) drawable.mutate(); - drawable.getPaint().setFilterBitmap(false); - - image.setImageDrawable(drawable); - text.setText(savedBoard.title); + public void setSavedBoard(Board savedBoard) { + siteIcon = savedBoard.site.icon(); + siteIcon.get(new SiteIcon.SiteIconResult() { + @Override + public void onSiteIcon(SiteIcon siteIcon, Drawable icon) { + if (SavedBoardCell.this.siteIcon == siteIcon) { + image.setImageDrawable(icon); + } + } + }); + + text.setText(formatBoardCodeAndName(savedBoard)); } } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java index 06b5c6b6..e75204ff 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java @@ -30,6 +30,7 @@ import android.widget.TextView; import org.floens.chan.R; import org.floens.chan.chan.ChanUrls; +import org.floens.chan.controller.Controller; import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.model.orm.Board; @@ -235,7 +236,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte if (item instanceof FloatingMenuItemBoard) { loadBoard(((FloatingMenuItemBoard) item).board); } else { - BoardEditController boardEditController = new BoardEditController(context); + Controller boardEditController = new BoardSetupController(context); if (doubleNavigationController != null) { doubleNavigationController.pushController(boardEditController); } else { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java index 3cc1d5ec..a1807457 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java @@ -45,7 +45,7 @@ import android.widget.TextView; import org.floens.chan.R; import org.floens.chan.controller.transition.FadeInTransition; -import org.floens.chan.core.presenter.SetupPresenter; +import org.floens.chan.core.presenter.SiteSetupPresenter; import org.floens.chan.core.site.Site; import org.floens.chan.core.site.SiteIcon; import org.floens.chan.ui.animation.AnimationUtils; @@ -53,10 +53,16 @@ import org.floens.chan.ui.animation.AnimationUtils; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + +import static org.floens.chan.Chan.getGraph; import static org.floens.chan.utils.AndroidUtils.dp; import static org.floens.chan.utils.AndroidUtils.getAttrColor; -public class SiteSetupController extends StyledToolbarNavigationController implements View.OnClickListener, SetupPresenter.Callback { +public class SiteSetupController extends StyledToolbarNavigationController implements View.OnClickListener, SiteSetupPresenter.Callback { + @Inject + SiteSetupPresenter presenter; + private EditText url; private View urlSubmit; private View spinner; @@ -64,8 +70,6 @@ public class SiteSetupController extends StyledToolbarNavigationController imple private boolean blocked = false; - private SetupPresenter presenter; - private RecyclerView sitesRecyclerview; private SitesAdapter sitesAdapter; private List sites = new ArrayList<>(); @@ -78,6 +82,8 @@ public class SiteSetupController extends StyledToolbarNavigationController imple public void onCreate() { super.onCreate(); + getGraph().inject(this); + view = inflateRes(R.layout.controller_site_setup); navigationItem.setTitle(R.string.setup_title); @@ -90,8 +96,6 @@ public class SiteSetupController extends StyledToolbarNavigationController imple next = (Button) view.findViewById(R.id.next_button); next.setOnClickListener(this); - presenter = new SetupPresenter(); - sitesAdapter = new SitesAdapter(); sitesRecyclerview.setAdapter(sitesAdapter); @@ -106,6 +110,7 @@ public class SiteSetupController extends StyledToolbarNavigationController imple presenter.onUrlSubmitClicked(url.getText().toString()); } else if (v == next) { presenter.onNextClicked(); +// navigationController.popController(false); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/drawable/ThumbDrawable.java b/Clover/app/src/main/java/org/floens/chan/ui/drawable/ThumbDrawable.java deleted file mode 100644 index f03251b8..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/drawable/ThumbDrawable.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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.ui.drawable; - -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PixelFormat; -import android.graphics.drawable.Drawable; - -import static org.floens.chan.utils.AndroidUtils.dp; - -public class ThumbDrawable extends Drawable { - private Paint paint = new Paint(); - private Path path = new Path(); - private int width; - private int height; - - public ThumbDrawable() { - width = dp(40); - height = dp(40); - - paint.setStrokeWidth(dp(2)); - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeCap(Paint.Cap.BUTT); - paint.setColor(0xff757575); - - path.reset(); - for (int i = 0; i < 3; i++) { - int top = (int) (getMinimumHeight() / 2f + (i - 1) * dp(6)); - path.moveTo(dp(8), top); - path.lineTo(getMinimumWidth() - dp(8), top); - } - path.close(); - } - - @Override - public void draw(Canvas canvas) { - canvas.drawPath(path, paint); - } - - @Override - public int getIntrinsicWidth() { - return width; - } - - @Override - public int getIntrinsicHeight() { - return height; - } - - @Override - public void setAlpha(int alpha) { - paint.setAlpha(alpha); - } - - @Override - public void setColorFilter(ColorFilter cf) { - paint.setColorFilter(cf); - } - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/helper/PostHelper.java b/Clover/app/src/main/java/org/floens/chan/ui/helper/PostHelper.java index 28938ef6..376da0d5 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/helper/PostHelper.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/helper/PostHelper.java @@ -25,8 +25,9 @@ import android.text.TextUtils; import android.text.style.ImageSpan; import org.floens.chan.R; -import org.floens.chan.core.model.orm.Loadable; import org.floens.chan.core.model.Post; +import org.floens.chan.core.model.orm.Board; +import org.floens.chan.core.model.orm.Loadable; import org.floens.chan.utils.AndroidUtils; import java.text.SimpleDateFormat; @@ -86,6 +87,14 @@ public class PostHelper { } } + public static String formatSiteAndBoardName(Board board) { + return board.site.name() + " \u2013 /" + board.name + "/"; + } + + public static String formatBoardCodeAndName(Board board) { + return "/" + board.code + "/ \u2013 " + board.name; + } + private static SimpleDateFormat dateFormat = new SimpleDateFormat("LL/dd/yy(EEE)HH:mm:ss", Locale.US); private static Date tmpDate = new Date(); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java index 3fa352ba..e7a3b706 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java @@ -241,7 +241,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener { final Spinner spinner = (Spinner) selectLayout.findViewById(R.id.progress); - final List allSites = Sites.ALL_SITES; + final List allSites = Sites.allSites(); final Site[] selectedSite = {allSites.get(0)}; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadSlidingPaneLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadSlidingPaneLayout.java index 05cd774f..00ab2c09 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadSlidingPaneLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadSlidingPaneLayout.java @@ -21,10 +21,12 @@ import android.content.Context; import android.os.Parcelable; import android.support.v4.widget.SlidingPaneLayout; import android.util.AttributeSet; +import android.view.View; import android.view.ViewGroup; import org.floens.chan.R; import org.floens.chan.ui.controller.ThreadSlideController; +import org.floens.chan.utils.AndroidUtils; import static org.floens.chan.utils.AndroidUtils.dp; @@ -53,6 +55,21 @@ public class ThreadSlidingPaneLayout extends SlidingPaneLayout { rightPane = (ViewGroup) findViewById(R.id.right_pane); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + // Forces a relayout after it has already been layed out, because SlidingPaneLayout sucks and otherwise + // gives the children too much room until they request a relayout. + AndroidUtils.waitForLayout(this, new AndroidUtils.OnMeasuredCallback() { + @Override + public boolean onMeasured(View view) { + requestLayout(); + return false; + } + }); + } + public void setThreadSlideController(ThreadSlideController threadSlideController) { this.threadSlideController = threadSlideController; } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java index ab2a0f9f..2a042c50 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java @@ -100,7 +100,6 @@ public class LoadView extends FrameLayout { public View setView(View newView, boolean animate) { if (newView == null) { FrameLayout progressBar = new FrameLayout(getContext()); - progressBar.setVisibility(View.GONE); progressBar.addView(new ProgressBar(getContext()), new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER)); newView = progressBar; } diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_reorder_black_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_reorder_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..142d71505c8c1a4855394adf3a392990224b3ecf GIT binary patch literal 107 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K;k*AAeNCjguLt~%;myCqei3UE2 xXAG?WCo@WCqzOEYJIu0pqnT7TjNo!(WLRe8?4Thc6$sSL;OXk;vd$@?2>^ks8BYKJ literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_reorder_black_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_reorder_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..d18997cd4833993ac76fe0373ae0b07eed06adf1 GIT binary patch literal 82 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1NlzEYkP60RiIWfP6Ft_kojSNe e-tDhJ5hH_ZdVgU6m&#+HA_h-aKbLh*2~7agcNFyi literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_reorder_black_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_reorder_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..0b080a18715edfcda140aed629af49df20a337a9 GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}t9Zwg>kch)?FB=LnFmNz${AJE7 yXCby@2~%AbQ%$h9@C^13%#42=80-ZY^s?9ex4rLXcYi8SAA_f>pUXO@geCw|yc$0M literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_reorder_black_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_reorder_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..0a66529bfeac1f43a44606a11b23344a7864c141 GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xf3?%cF6R(1{pgYCW9j{U8QwIH3Iu6{1-oD!M< D>%<$U literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_reorder_black_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_reorder_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..56a5bc8ba3ed86b692dd7fd3e4dbc5cdd3857d1e GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeK3?y%aJ*@^(YymzYu0R?HmZtAK52P4Ng8YIR z9G=}s19D_MT^vIy7?T;A0uTI`=d!yn!8!eZ&#&|=_KO)9KAOpW$O(U+0n+U0>gTe~ HDWM4f9he}? literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/layout/cell_board_edit.xml b/Clover/app/src/main/res/layout/cell_board_edit.xml index 52a498c5..09ab76f4 100644 --- a/Clover/app/src/main/res/layout/cell_board_edit.xml +++ b/Clover/app/src/main/res/layout/cell_board_edit.xml @@ -15,7 +15,8 @@ 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 . --> -. android:paddingLeft="8dp" android:paddingRight="8dp" android:scaleType="center" - tools:ignore="ContentDescription" /> + android:src="@drawable/ic_reorder_black_24dp" + tools:ignore="ContentDescription"/> . android:paddingTop="8dp" android:singleLine="true" android:textColor="?text_color_primary" - android:textSize="14sp" /> + android:textSize="14sp"/> . android:paddingLeft="16dp" android:paddingRight="16dp" android:textColor="?text_color_secondary" - android:textSize="12sp" /> + android:textSize="12sp"/> diff --git a/Clover/app/src/main/res/layout/cell_board_suggestion.xml b/Clover/app/src/main/res/layout/cell_board_suggestion.xml new file mode 100644 index 00000000..82aa841a --- /dev/null +++ b/Clover/app/src/main/res/layout/cell_board_suggestion.xml @@ -0,0 +1,42 @@ + + + + + + + + diff --git a/Clover/app/src/main/res/layout/cell_saved_board.xml b/Clover/app/src/main/res/layout/cell_saved_board.xml index 82aa841a..c32e3a41 100644 --- a/Clover/app/src/main/res/layout/cell_saved_board.xml +++ b/Clover/app/src/main/res/layout/cell_saved_board.xml @@ -39,4 +39,11 @@ along with this program. If not, see . android:textColor="?text_color_primary" android:textSize="14sp"/> + + diff --git a/Clover/app/src/main/res/layout/controller_board_setup.xml b/Clover/app/src/main/res/layout/controller_board_setup.xml index 774e2783..cb5deac5 100644 --- a/Clover/app/src/main/res/layout/controller_board_setup.xml +++ b/Clover/app/src/main/res/layout/controller_board_setup.xml @@ -20,7 +20,8 @@ along with this program. If not, see . xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="?attr/backcolor"> + android:background="?attr/backcolor" + android:clipChildren="false"> . android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="0dp" + android:clipChildren="false" + android:clipToPadding="false" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" diff --git a/docs/database.txt b/docs/database.txt index c59f9576..02ad0630 100644 --- a/docs/database.txt +++ b/docs/database.txt @@ -67,6 +67,7 @@ ALTER TABLE loadable ADD COLUMN lastLoaded default -1; Changes in version 22: +CREATE TABLE `site` (`configuration` VARCHAR , `id` INTEGER PRIMARY KEY AUTOINCREMENT , `userSettings` VARCHAR ); ALTER TABLE loadable ADD COLUMN site INTEGER default 0; ALTER TABLE board ADD COLUMN site INTEGER default 0; ALTER TABLE savedreply ADD COLUMN site INTEGER default 0;