Work on abstracting the html parser.

multisite
Floens 8 years ago
parent 08df656d05
commit ede3e69f5c
  1. 10
      Clover/app/src/main/java/org/floens/chan/chan/ChanLoader.java
  2. 7
      Clover/app/src/main/java/org/floens/chan/chan/ChanLoaderRequestParams.java
  3. 13
      Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java
  4. 5
      Clover/app/src/main/java/org/floens/chan/core/site/Site.java
  5. 12
      Clover/app/src/main/java/org/floens/chan/core/site/common/ChanReader.java
  6. 63
      Clover/app/src/main/java/org/floens/chan/core/site/common/ChanReaderProcessingQueue.java
  7. 276
      Clover/app/src/main/java/org/floens/chan/core/site/common/ChanReaderRequest.java
  8. 242
      Clover/app/src/main/java/org/floens/chan/core/site/common/FutabaChanReader.java
  9. 9
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java
  10. 14
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan8/Chan8.java

@ -25,8 +25,10 @@ import com.android.volley.VolleyError;
import org.floens.chan.core.exception.ChanLoaderException;
import org.floens.chan.core.model.ChanThread;
import org.floens.chan.core.model.orm.Loadable;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.orm.Loadable;
import org.floens.chan.core.site.common.ChanReader;
import org.floens.chan.core.site.common.ChanReaderRequest;
import org.floens.chan.ui.helper.PostHelper;
import org.floens.chan.utils.AndroidUtils;
import org.floens.chan.utils.Logger;
@ -235,7 +237,11 @@ public class ChanLoader implements Response.ErrorListener, Response.Listener<Cha
List<Post> cached = thread == null ? new ArrayList<Post>() : thread.posts;
request = loadable.getSite().loaderRequest(new ChanLoaderRequestParams(loadable, cached, this, this));
ChanReader chanReader = loadable.getSite().chanReader();
ChanLoaderRequestParams requestParams = new ChanLoaderRequestParams(loadable, chanReader, cached, this, this);
ChanReaderRequest readerRequest = new ChanReaderRequest(requestParams);
request = new ChanLoaderRequest(readerRequest);
volleyRequestQueue.add(request.getVolleyRequest());

@ -20,8 +20,9 @@ package org.floens.chan.chan;
import com.android.volley.Response;
import org.floens.chan.core.model.orm.Loadable;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.orm.Loadable;
import org.floens.chan.core.site.common.ChanReader;
import java.util.List;
@ -34,6 +35,8 @@ public class ChanLoaderRequestParams {
*/
public final Loadable loadable;
public final ChanReader chanReader;
/**
* Cached Post objects from previous loads, or an empty list.
*/
@ -50,11 +53,13 @@ public class ChanLoaderRequestParams {
public final Response.ErrorListener errorListener;
public ChanLoaderRequestParams(Loadable loadable,
ChanReader chanReader,
List<Post> cached,
Response.Listener<ChanLoaderResponse> listener,
Response.ErrorListener errorListener) {
this.loadable = loadable;
this.chanReader = chanReader;
this.cached = cached;
this.listener = listener;
this.errorListener = errorListener;

@ -75,6 +75,19 @@ public class BoardManager {
fetchLimitedSitesTheirBoards();
}
public Board getForCode(Site site, String code) {
if (site.boardsType() == Site.BoardsType.DYNAMIC) {
for (Board board : getSavedBoards()) {
if (board.site == site && board.code.equals(code)) {
return board;
}
}
return null;
} else {
return Board.fromSiteNameCode(site, code, code);
}
}
public List<Board> getSavedBoards() {
return savedBoards;
}

@ -19,13 +19,12 @@ package org.floens.chan.core.site;
import android.support.annotation.Nullable;
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.json.site.SiteConfig;
import org.floens.chan.core.model.json.site.SiteUserSettings;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Loadable;
import org.floens.chan.core.site.common.ChanReader;
import org.floens.chan.core.site.http.DeleteRequest;
import org.floens.chan.core.site.http.DeleteResponse;
import org.floens.chan.core.site.http.HttpCall;
@ -145,7 +144,7 @@ public interface Site {
void onBoardNonexistent();
}
ChanLoaderRequest loaderRequest(ChanLoaderRequestParams request);
ChanReader chanReader();
void post(Reply reply, PostListener postListener);

@ -0,0 +1,12 @@
package org.floens.chan.core.site.common;
import android.util.JsonReader;
public interface ChanReader {
void loadThread(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception;
void loadCatalog(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception;
void readPostObject(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception;
}

@ -0,0 +1,63 @@
package org.floens.chan.core.site.common;
import android.annotation.SuppressLint;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.orm.Loadable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class ChanReaderProcessingQueue {
@SuppressLint("UseSparseArrays")
private Map<Integer, Post> cachedByNo = new HashMap<>();
private Loadable loadable;
private List<Post> toReuse = new ArrayList<>();
private List<Post.Builder> toParse = new ArrayList<>();
private Post.Builder op;
public ChanReaderProcessingQueue(List<Post> toReuse, Loadable loadable) {
this.loadable = loadable;
for (int i = 0; i < toReuse.size(); i++) {
Post cache = toReuse.get(i);
cachedByNo.put(cache.no, cache);
}
}
public Post getCachedPost(int no) {
return cachedByNo.get(no);
}
public void addForReuse(Post post) {
toReuse.add(post);
}
public void addForParse(Post.Builder postBuilder) {
toParse.add(postBuilder);
}
public void setOp(Post.Builder op) {
this.op = op;
}
public Loadable getLoadable() {
return loadable;
}
List<Post> getToReuse() {
return toReuse;
}
List<Post.Builder> getToParse() {
return toParse;
}
Post.Builder getOp() {
return op;
}
}

@ -25,15 +25,11 @@ import org.floens.chan.chan.ChanParser;
import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.database.DatabaseSavedReplyManager;
import org.floens.chan.core.manager.FilterEngine;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.orm.Filter;
import org.floens.chan.core.model.orm.Loadable;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostHttpIcon;
import org.floens.chan.core.model.PostImage;
import org.floens.chan.core.net.JsonReaderRequest;
import org.floens.chan.core.site.SiteEndpoints;
import org.floens.chan.utils.Time;
import org.jsoup.parser.Parser;
import java.util.ArrayList;
import java.util.HashMap;
@ -79,7 +75,7 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
private Loadable loadable;
private List<Post> cached;
private Post.Builder op;
private ChanReader reader;
private DatabaseSavedReplyManager databaseSavedReplyManager;
private List<Filter> filters;
@ -92,6 +88,7 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
// Copy the loadable and cached list. The cached array may changed/cleared by other threads.
loadable = request.loadable.copy();
cached = new ArrayList<>(request.cached);
reader = request.chanReader;
filters = new ArrayList<>();
List<Filter> enabledFilters = filterEngine.getEnabledFilters();
@ -152,18 +149,12 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
long load = Time.startTiming();
ProcessingQueue processing = new ProcessingQueue();
Map<Integer, Post> cachedByNo = new HashMap<>();
for (int i = 0; i < cached.size(); i++) {
Post cache = cached.get(i);
cachedByNo.put(cache.no, cache);
}
ChanReaderProcessingQueue processing = new ChanReaderProcessingQueue(cached, loadable);
if (loadable.isThreadMode()) {
loadThread(reader, processing, cachedByNo);
this.reader.loadThread(reader, processing);
} else if (loadable.isCatalogMode()) {
loadCatalog(reader, processing, cachedByNo);
this.reader.loadCatalog(reader, processing);
} else {
throw new IllegalArgumentException("Unknown mode");
}
@ -173,20 +164,23 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
}
List<Post> list = parsePosts(processing);
return processPosts(list);
return processPosts(processing.getOp(), list);
}
// Concurrently parses the new posts with an executor
private List<Post> parsePosts(ProcessingQueue queue) throws InterruptedException, ExecutionException {
private List<Post> parsePosts(ChanReaderProcessingQueue queue) throws InterruptedException, ExecutionException {
long parsePosts = Time.startTiming();
List<Post> total = new ArrayList<>();
total.addAll(queue.cached);
List<Post> cached = queue.getToReuse();
total.addAll(cached);
List<Post.Builder> toParse = queue.getToParse();
List<Callable<Post>> tasks = new ArrayList<>(queue.toParse.size());
for (int i = 0; i < queue.toParse.size(); i++) {
Post.Builder post = queue.toParse.get(i);
List<Callable<Post>> tasks = new ArrayList<>(toParse.size());
for (int i = 0; i < toParse.size(); i++) {
Post.Builder post = toParse.get(i);
tasks.add(new PostParseCallable(filterEngine, filters, databaseSavedReplyManager, post, chanParser));
}
@ -208,8 +202,8 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
return total;
}
private ChanLoaderResponse processPosts(List<Post> serverPosts) throws Exception {
ChanLoaderResponse response = new ChanLoaderResponse(op, new ArrayList<Post>(serverPosts.size()));
private ChanLoaderResponse processPosts(Post.Builder op, List<Post> allPost) throws Exception {
ChanLoaderResponse response = new ChanLoaderResponse(op, new ArrayList<Post>(allPost.size()));
List<Post> cachedPosts = new ArrayList<>();
List<Post> newPosts = new ArrayList<>();
@ -225,8 +219,8 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
}
Map<Integer, Post> serverPostsByNo = new HashMap<>();
for (int i = 0; i < serverPosts.size(); i++) {
Post post = serverPosts.get(i);
for (int i = 0; i < allPost.size(); i++) {
Post post = allPost.get(i);
serverPostsByNo.put(post.no, post);
}
@ -243,8 +237,8 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
long newCheck = Time.startTiming();
// If there's a post in the list from the server, that's not in the cached list, add it.
for (int i = 0; i < serverPosts.size(); i++) {
Post serverPost = serverPosts.get(i);
for (int i = 0; i < allPost.size(); i++) {
Post serverPost = allPost.get(i);
if (!cachedPostsByNo.containsKey(serverPost.no)) {
newPosts.add(serverPost);
}
@ -253,7 +247,7 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
Time.endTiming("New check", newCheck);
}
} else {
newPosts.addAll(serverPosts);
newPosts.addAll(allPost);
}
List<Post> allPosts = new ArrayList<>(cachedPosts.size() + newPosts.size());
@ -308,230 +302,4 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
return response;
}
private void loadThread(JsonReader reader, ProcessingQueue queue, Map<Integer, Post> cachedByNo) throws Exception {
reader.beginObject();
// Page object
while (reader.hasNext()) {
String key = reader.nextName();
if (key.equals("posts")) {
reader.beginArray();
// Thread array
while (reader.hasNext()) {
// Thread object
readPostObject(reader, queue, cachedByNo);
}
reader.endArray();
} else {
reader.skipValue();
}
}
reader.endObject();
}
private void loadCatalog(JsonReader reader, ProcessingQueue queue, Map<Integer, Post> cachedByNo) throws Exception {
reader.beginArray(); // Array of pages
while (reader.hasNext()) {
reader.beginObject(); // Page object
while (reader.hasNext()) {
if (reader.nextName().equals("threads")) {
reader.beginArray(); // Threads array
while (reader.hasNext()) {
readPostObject(reader, queue, cachedByNo);
}
reader.endArray();
} else {
reader.skipValue();
}
}
reader.endObject();
}
reader.endArray();
}
private void readPostObject(JsonReader reader, ProcessingQueue queue, Map<Integer, Post> cachedByNo) throws Exception {
Post.Builder builder = new Post.Builder();
builder.board(loadable.board);
// File
String fileId = null;
String fileExt = null;
int fileWidth = 0;
int fileHeight = 0;
long fileSize = 0;
boolean fileSpoiler = false;
String fileName = null;
// Country flag
String countryCode = null;
String trollCountryCode = null;
String countryName = null;
// 4chan pass leaf
int since4pass = 0;
reader.beginObject();
while (reader.hasNext()) {
String key = reader.nextName();
switch (key) {
case "no":
builder.id(reader.nextInt());
break;
/*case "now":
post.date = reader.nextString();
break;*/
case "sub":
builder.subject(reader.nextString());
break;
case "name":
builder.name(reader.nextString());
break;
case "com":
builder.comment(reader.nextString());
break;
case "tim":
fileId = reader.nextString();
break;
case "time":
builder.setUnixTimestampSeconds(reader.nextLong());
break;
case "ext":
fileExt = reader.nextString().replace(".", "");
break;
case "w":
fileWidth = reader.nextInt();
break;
case "h":
fileHeight = reader.nextInt();
break;
case "fsize":
fileSize = reader.nextLong();
break;
case "filename":
fileName = reader.nextString();
break;
case "trip":
builder.tripcode(reader.nextString());
break;
case "country":
countryCode = reader.nextString();
break;
case "troll_country":
trollCountryCode = reader.nextString();
break;
case "country_name":
countryName = reader.nextString();
break;
case "spoiler":
fileSpoiler = reader.nextInt() == 1;
break;
case "resto":
int opId = reader.nextInt();
builder.op(opId == 0);
builder.opId(opId);
break;
case "sticky":
builder.sticky(reader.nextInt() == 1);
break;
case "closed":
builder.closed(reader.nextInt() == 1);
break;
case "archived":
builder.archived(reader.nextInt() == 1);
break;
case "replies":
builder.replies(reader.nextInt());
break;
case "images":
builder.images(reader.nextInt());
break;
case "unique_ips":
builder.uniqueIps(reader.nextInt());
break;
case "id":
builder.posterId(reader.nextString());
break;
case "capcode":
builder.moderatorCapcode(reader.nextString());
break;
case "since4pass":
since4pass = reader.nextInt();
break;
default:
// Unknown/ignored key
reader.skipValue();
break;
}
}
reader.endObject();
if (builder.op) {
// Update OP fields later on the main thread
op = new Post.Builder();
op.closed(builder.closed);
op.archived(builder.archived);
op.sticky(builder.sticky);
op.replies(builder.replies);
op.images(builder.images);
op.uniqueIps(builder.uniqueIps);
}
Post cached = cachedByNo.get(builder.id);
if (cached != null) {
// Id is known, use the cached post object.
queue.cached.add(cached);
return;
}
SiteEndpoints endpoints = loadable.getSite().endpoints();
if (fileId != null && fileName != null && fileExt != null) {
Map<String, String> hack = new HashMap<>(2);
hack.put("tim", fileId);
hack.put("ext", fileExt);
builder.image(new PostImage.Builder()
.originalName(String.valueOf(fileId))
.thumbnailUrl(endpoints.thumbnailUrl(builder, fileSpoiler, hack))
.imageUrl(endpoints.imageUrl(builder, hack))
.filename(Parser.unescapeEntities(fileName, false))
.extension(fileExt)
.imageWidth(fileWidth)
.imageHeight(fileHeight)
.spoiler(fileSpoiler)
.size(fileSize)
.build());
}
if (countryCode != null && countryName != null) {
Map<String, String> arg = new HashMap<>(1);
arg.put("country_code", countryCode);
HttpUrl countryUrl = endpoints.icon(builder, "country", arg);
builder.addHttpIcon(new PostHttpIcon(countryUrl, countryName));
}
if (trollCountryCode != null && countryName != null) {
Map<String, String> arg = new HashMap<>(1);
arg.put("troll_country_code", trollCountryCode);
HttpUrl countryUrl = endpoints.icon(builder, "troll_country", arg);
builder.addHttpIcon(new PostHttpIcon(countryUrl, countryName));
}
if (since4pass != 0) {
HttpUrl iconUrl = endpoints.icon(builder, "since4pass", null);
builder.addHttpIcon(new PostHttpIcon(iconUrl, String.valueOf(since4pass)));
}
queue.toParse.add(builder);
}
private static class ProcessingQueue {
public List<Post> cached = new ArrayList<>();
public List<Post.Builder> toParse = new ArrayList<>();
}
}

@ -0,0 +1,242 @@
package org.floens.chan.core.site.common;
import android.util.JsonReader;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostHttpIcon;
import org.floens.chan.core.model.PostImage;
import org.floens.chan.core.site.SiteEndpoints;
import org.jsoup.parser.Parser;
import java.util.HashMap;
import java.util.Map;
import okhttp3.HttpUrl;
public class FutabaChanReader implements ChanReader {
@Override
public void loadThread(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception {
reader.beginObject();
// Page object
while (reader.hasNext()) {
String key = reader.nextName();
if (key.equals("posts")) {
reader.beginArray();
// Thread array
while (reader.hasNext()) {
// Thread object
readPostObject(reader, queue);
}
reader.endArray();
} else {
reader.skipValue();
}
}
reader.endObject();
}
@Override
public void loadCatalog(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception {
reader.beginArray(); // Array of pages
while (reader.hasNext()) {
reader.beginObject(); // Page object
while (reader.hasNext()) {
if (reader.nextName().equals("threads")) {
reader.beginArray(); // Threads array
while (reader.hasNext()) {
readPostObject(reader, queue);
}
reader.endArray();
} else {
reader.skipValue();
}
}
reader.endObject();
}
reader.endArray();
}
@Override
public void readPostObject(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception {
Post.Builder builder = new Post.Builder();
builder.board(queue.getLoadable().board);
// File
String fileId = null;
String fileExt = null;
int fileWidth = 0;
int fileHeight = 0;
long fileSize = 0;
boolean fileSpoiler = false;
String fileName = null;
// Country flag
String countryCode = null;
String trollCountryCode = null;
String countryName = null;
// 4chan pass leaf
int since4pass = 0;
reader.beginObject();
while (reader.hasNext()) {
String key = reader.nextName();
switch (key) {
case "no":
builder.id(reader.nextInt());
break;
/*case "now":
post.date = reader.nextString();
break;*/
case "sub":
builder.subject(reader.nextString());
break;
case "name":
builder.name(reader.nextString());
break;
case "com":
builder.comment(reader.nextString());
break;
case "tim":
fileId = reader.nextString();
break;
case "time":
builder.setUnixTimestampSeconds(reader.nextLong());
break;
case "ext":
fileExt = reader.nextString().replace(".", "");
break;
case "w":
fileWidth = reader.nextInt();
break;
case "h":
fileHeight = reader.nextInt();
break;
case "fsize":
fileSize = reader.nextLong();
break;
case "filename":
fileName = reader.nextString();
break;
case "trip":
builder.tripcode(reader.nextString());
break;
case "country":
countryCode = reader.nextString();
break;
case "troll_country":
trollCountryCode = reader.nextString();
break;
case "country_name":
countryName = reader.nextString();
break;
case "spoiler":
fileSpoiler = reader.nextInt() == 1;
break;
case "resto":
int opId = reader.nextInt();
builder.op(opId == 0);
builder.opId(opId);
break;
case "sticky":
builder.sticky(reader.nextInt() == 1);
break;
case "closed":
builder.closed(reader.nextInt() == 1);
break;
case "archived":
builder.archived(reader.nextInt() == 1);
break;
case "replies":
builder.replies(reader.nextInt());
break;
case "images":
builder.images(reader.nextInt());
break;
case "unique_ips":
builder.uniqueIps(reader.nextInt());
break;
case "id":
builder.posterId(reader.nextString());
break;
case "capcode":
builder.moderatorCapcode(reader.nextString());
break;
case "since4pass":
since4pass = reader.nextInt();
break;
default:
// Unknown/ignored key
reader.skipValue();
break;
}
}
reader.endObject();
if (builder.op) {
// Update OP fields later on the main thread
Post.Builder op = new Post.Builder();
op.closed(builder.closed);
op.archived(builder.archived);
op.sticky(builder.sticky);
op.replies(builder.replies);
op.images(builder.images);
op.uniqueIps(builder.uniqueIps);
queue.setOp(op);
}
Post cached = queue.getCachedPost(builder.id);
if (cached != null) {
// Id is known, use the cached post object.
queue.addForReuse(cached);
return;
}
SiteEndpoints endpoints = queue.getLoadable().getSite().endpoints();
if (fileId != null && fileName != null && fileExt != null) {
Map<String, String> hack = new HashMap<>(2);
hack.put("tim", fileId);
hack.put("ext", fileExt);
builder.image(new PostImage.Builder()
.originalName(String.valueOf(fileId))
.thumbnailUrl(endpoints.thumbnailUrl(builder, fileSpoiler, hack))
.imageUrl(endpoints.imageUrl(builder, hack))
.filename(Parser.unescapeEntities(fileName, false))
.extension(fileExt)
.imageWidth(fileWidth)
.imageHeight(fileHeight)
.spoiler(fileSpoiler)
.size(fileSize)
.build());
}
if (countryCode != null && countryName != null) {
Map<String, String> arg = new HashMap<>(1);
arg.put("country_code", countryCode);
HttpUrl countryUrl = endpoints.icon(builder, "country", arg);
builder.addHttpIcon(new PostHttpIcon(countryUrl, countryName));
}
if (trollCountryCode != null && countryName != null) {
Map<String, String> arg = new HashMap<>(1);
arg.put("troll_country_code", trollCountryCode);
HttpUrl countryUrl = endpoints.icon(builder, "troll_country", arg);
builder.addHttpIcon(new PostHttpIcon(countryUrl, countryName));
}
if (since4pass != 0) {
HttpUrl iconUrl = endpoints.icon(builder, "since4pass", null);
builder.addHttpIcon(new PostHttpIcon(iconUrl, String.valueOf(since4pass)));
}
queue.addForParse(builder);
}
}

@ -26,8 +26,6 @@ import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import org.floens.chan.chan.ChanLoaderRequest;
import org.floens.chan.chan.ChanLoaderRequestParams;
import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.orm.Board;
@ -41,7 +39,8 @@ 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.common.ChanReader;
import org.floens.chan.core.site.common.FutabaChanReader;
import org.floens.chan.core.site.http.DeleteRequest;
import org.floens.chan.core.site.http.HttpCall;
import org.floens.chan.core.site.http.HttpCallManager;
@ -403,8 +402,8 @@ public class Chan4 extends SiteBase {
}
@Override
public ChanLoaderRequest loaderRequest(ChanLoaderRequestParams request) {
return new ChanLoaderRequest(new ChanReaderRequest(request));
public ChanReader chanReader() {
return new FutabaChanReader();
}
@Override

@ -21,8 +21,7 @@ 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.manager.BoardManager;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Loadable;
@ -33,7 +32,8 @@ 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.common.ChanReader;
import org.floens.chan.core.site.common.FutabaChanReader;
import org.floens.chan.core.site.http.DeleteRequest;
import org.floens.chan.core.site.http.HttpCall;
import org.floens.chan.core.site.http.LoginRequest;
@ -45,6 +45,8 @@ import java.util.Map;
import okhttp3.HttpUrl;
import okhttp3.Request;
import static org.floens.chan.Chan.getGraph;
public class Chan8 extends SiteBase {
public static final Resolvable RESOLVABLE = new Resolvable() {
@Override
@ -218,12 +220,12 @@ public class Chan8 extends SiteBase {
@Override
public Board board(String code) {
return null;
return getGraph().get(BoardManager.class).getForCode(this, code);
}
@Override
public ChanLoaderRequest loaderRequest(ChanLoaderRequestParams request) {
return new ChanLoaderRequest(new ChanReaderRequest(request));
public ChanReader chanReader() {
return new FutabaChanReader();
}
@Override

Loading…
Cancel
Save