Hardcode another site, board adding and removing from the new editor.

multisite
Floens 8 years ago
parent 345c22de7f
commit 08df656d05
  1. BIN
      Clover/app/src/main/assets/icons/8chan.png
  2. 42
      Clover/app/src/main/java/org/floens/chan/core/database/DatabaseBoardManager.java
  3. 39
      Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java
  4. 9
      Clover/app/src/main/java/org/floens/chan/core/database/DatabaseManager.java
  5. 14
      Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java
  6. 86
      Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java
  7. 44
      Clover/app/src/main/java/org/floens/chan/core/model/orm/Board.java
  8. 109
      Clover/app/src/main/java/org/floens/chan/core/presenter/BoardSetupPresenter.java
  9. 8
      Clover/app/src/main/java/org/floens/chan/core/presenter/SiteSetupPresenter.java
  10. 2
      Clover/app/src/main/java/org/floens/chan/core/site/Site.java
  11. 3
      Clover/app/src/main/java/org/floens/chan/core/site/SiteIcon.java
  12. 19
      Clover/app/src/main/java/org/floens/chan/core/site/SiteManager.java
  13. 20
      Clover/app/src/main/java/org/floens/chan/core/site/Sites.java
  14. 16
      Clover/app/src/main/java/org/floens/chan/core/site/common/ChanReaderRequest.java
  15. 3
      Clover/app/src/main/java/org/floens/chan/core/site/common/PostParseCallable.java
  16. 5
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java
  17. 254
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan8/Chan8.java
  18. 9
      Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java
  19. 237
      Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java
  20. 3
      Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java
  21. 17
      Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java
  22. 82
      Clover/app/src/main/java/org/floens/chan/ui/drawable/ThumbDrawable.java
  23. 11
      Clover/app/src/main/java/org/floens/chan/ui/helper/PostHelper.java
  24. 2
      Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java
  25. 17
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadSlidingPaneLayout.java
  26. 1
      Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java
  27. BIN
      Clover/app/src/main/res/drawable-hdpi/ic_reorder_black_24dp.png
  28. BIN
      Clover/app/src/main/res/drawable-mdpi/ic_reorder_black_24dp.png
  29. BIN
      Clover/app/src/main/res/drawable-xhdpi/ic_reorder_black_24dp.png
  30. BIN
      Clover/app/src/main/res/drawable-xxhdpi/ic_reorder_black_24dp.png
  31. BIN
      Clover/app/src/main/res/drawable-xxxhdpi/ic_reorder_black_24dp.png
  32. 10
      Clover/app/src/main/res/layout/cell_board_edit.xml
  33. 42
      Clover/app/src/main/res/layout/cell_board_suggestion.xml
  34. 7
      Clover/app/src/main/res/layout/cell_saved_board.xml
  35. 5
      Clover/app/src/main/res/layout/controller_board_setup.xml
  36. 1
      docs/database.txt

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

@ -20,14 +20,18 @@ public class DatabaseBoardManager {
this.helper = helper;
}
/**
* Save the boards listed in the database.<br>
* 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<Void> setBoards(final List<Board> boards) {
public Callable<Board> createOrUpdate(final Board board) {
return new Callable<Board>() {
@Override
public Board call() throws Exception {
helper.boardsDao.createOrUpdate(board);
return board;
}
};
}
public Callable<Void> createAll(final List<Board> boards) {
return new Callable<Void>() {
@Override
public Void call() throws Exception {
@ -62,6 +66,28 @@ public class DatabaseBoardManager {
};
}
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>> getAllBoards() {
return new Callable<List<Board>>() {
@Override

@ -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<Pin, Integer> pinDao;
public Dao<Loadable, Integer> 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() {

@ -281,6 +281,15 @@ public class DatabaseManager {
}
private <T> Future<T> executeTask(final Callable<T> taskCallable, final TaskResult<T> taskResult) {
if (helper.isUpgrading) {
try {
taskCallable.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
return backgroundExecutor.submit(new Callable<T>() {
@Override
public T call() {

@ -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,
},

@ -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.
* <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,
* 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.
* <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.
* <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.
*/
@Singleton
public class BoardManager {
@ -57,24 +63,66 @@ public class BoardManager {
};
private final DatabaseManager databaseManager;
private final Site defaultSite;
private final List<Board> boards;
private final List<Board> savedBoards = new ArrayList<>();
private final Map<String, Board> 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<Board> 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<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 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<Board> boardsToAddWs = new ArrayList<>();
List<Board> 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<Board> filterSaved(List<Board> all) {
/*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);
@ -161,7 +209,7 @@ public class BoardManager {
}
Collections.sort(saved, ORDER_SORT);
return saved;
}
}*/
public static class BoardsChangedMessage {
}

@ -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;

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Site> sites = new ArrayList<>();
@Inject
BoardManager boardManager;
private List<Board> 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<BoardSuggestion> getSuggestionsForQuery(String query) {
List<BoardSuggestion> 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<Board> 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;
}
}
}

@ -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<Site> 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);

@ -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,

@ -46,6 +46,9 @@ public class SiteIcon {
return siteIcon;
}
private SiteIcon() {
}
public void get(SiteIconResult result) {
if (assetPath != null) {
Bitmap bitmap;

@ -51,9 +51,9 @@ public class SiteManager {
public void addSite(String url, SiteAddCallback callback) {
SiteResolver.SiteResolverResult resolve = resolver.resolve(url);
Site site;
Class<? extends Site> 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<? extends Site> siteClass, SiteAddCallback callback) {
Site site = instantiateSiteClass(siteClass);
site = createNewSite(site);
List<Site> newAllSites = new ArrayList<>(Sites.ALL_SITES);
List<Site> 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<Site> newAllSites) {
Sites.initialize(newAllSites);
}
private List<Site> loadSitesFromDatabase() {

@ -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<Resolvable> RESOLVABLES = new ArrayList<>();
static {
RESOLVABLES.add(Chan4.RESOLVABLE);
RESOLVABLES.add(Chan8.RESOLVABLE);
}
public static final List<Site> ALL_SITES = new ArrayList<>();
private static List<Site> ALL_SITES = Collections.unmodifiableList(new ArrayList<Site>());
/**
* Return all sites known in the system.
* <p>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<Site> allSites() {
return ALL_SITES;
}
@Deprecated
private static Site defaultSite;
@ -46,7 +61,6 @@ public class Sites {
static void initialize(List<Site> 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));
}
}

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<ChanLoaderResponse> {
private static final String TAG = "Chan4ReaderRequest";
public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
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<ChanLoaderResponse> {
private List<Filter> 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<ChanLoaderResponse> {
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<ChanLoaderResponse> {
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<ChanLoaderResponse> {
}
SiteEndpoints endpoints = loadable.getSite().endpoints();
if (fileId != 0 && fileName != null && fileExt != null) {
if (fileId != null && fileName != null && fileExt != null) {
Map<String, String> 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))

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<Post> {
private static final String TAG = "PostParseCallable";

@ -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<Board> allBoards = getGraph().get(BoardManager.class).getAllBoards();
List<Board> 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

@ -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 <http://www.gnu.org/licenses/>.
*/
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<? extends Site> 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<String, String> 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<String, String> 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<String, String> 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;
}
}

@ -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<Board> all = boardManager.getAllBoards();
List<Board> 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<Board> 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) {

@ -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<SavedBoard> savedBoards = new ArrayList<>();
for (int board = 0; board < 5; board++) {
savedBoards.add(new SavedBoard("foo - " + board, board));
}
adapter.setSavedBoards(savedBoards);
List<String> foo = new ArrayList<String>() {{
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<String> 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<Board> 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<BoardSetupPresenter.BoardSuggestion> 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<SavedBoardCell> {
private List<SavedBoard> savedBoards = new ArrayList<>();
private List<Board> savedBoards;
public SavedBoardsAdapter() {
setHasStableIds(true);
}
private void setSavedBoards(List<SavedBoard> savedBoards) {
this.savedBoards.clear();
this.savedBoards.addAll(savedBoards);
private void setSavedBoards(List<Board> 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));
}
}
}

@ -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 {

@ -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<Site> 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);
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

@ -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();

@ -241,7 +241,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
final Spinner spinner = (Spinner) selectLayout.findViewById(R.id.progress);
final List<? extends Site> allSites = Sites.ALL_SITES;
final List<? extends Site> allSites = Sites.allSites();
final Site[] selectedSite = {allSites.get(0)};

@ -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;
}

@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

@ -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 <http://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -30,7 +31,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:scaleType="center"
tools:ignore="ContentDescription" />
android:src="@drawable/ic_reorder_black_24dp"
tools:ignore="ContentDescription"/>
<LinearLayout
android:layout_width="0dp"
@ -48,7 +50,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:paddingTop="8dp"
android:singleLine="true"
android:textColor="?text_color_primary"
android:textSize="14sp" />
android:textSize="14sp"/>
<TextView
android:id="@+id/description"
@ -59,7 +61,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textColor="?text_color_secondary"
android:textSize="12sp" />
android:textSize="12sp"/>
</LinearLayout>

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?><!--
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 <http://www.gnu.org/licenses/>.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?attr/backcolor"
android:orientation="horizontal">
<ImageView
android:id="@+id/image"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="8dp"
android:scaleType="fitCenter"/>
<TextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:maxLines="1"
android:textColor="?text_color_primary"
android:textSize="14sp"/>
</LinearLayout>

@ -39,4 +39,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:textColor="?text_color_primary"
android:textSize="14sp"/>
<ImageView
android:id="@+id/reorder"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="8dp"
android:scaleType="centerInside"/>
</LinearLayout>

@ -20,7 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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">
<TextView
android:id="@+id/introduction"
@ -75,6 +76,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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"

@ -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;

Loading…
Cancel
Save