work on 8ch parsing and abstracting the parser

multisite
Floens 8 years ago
parent 99ce139bf8
commit 5fc8c7b0d7
  1. 2
      Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java
  2. 4
      Clover/app/src/main/java/org/floens/chan/core/presenter/SiteSetupPresenter.java
  3. 25
      Clover/app/src/main/java/org/floens/chan/core/site/common/ChanParser.java
  4. 2
      Clover/app/src/main/java/org/floens/chan/core/site/common/ChanReader.java
  5. 6
      Clover/app/src/main/java/org/floens/chan/core/site/common/ChanReaderRequest.java
  6. 160
      Clover/app/src/main/java/org/floens/chan/core/site/common/DefaultFutabaChanParserHandler.java
  7. 184
      Clover/app/src/main/java/org/floens/chan/core/site/common/FutabaChanParser.java
  8. 36
      Clover/app/src/main/java/org/floens/chan/core/site/common/FutabaChanParserHandler.java
  9. 15
      Clover/app/src/main/java/org/floens/chan/core/site/common/FutabaChanReader.java
  10. 11
      Clover/app/src/main/java/org/floens/chan/core/site/common/PostParseCallable.java
  11. 4
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan8/Chan8.java
  12. 133
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan8/Chan8ParserHandler.java
  13. 6
      Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java
  14. 3
      Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java
  15. 5
      Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java
  16. 5
      Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java
  17. 2
      Clover/app/src/main/java/org/floens/chan/ui/theme/Theme.java

@ -7,7 +7,6 @@ import com.android.volley.toolbox.ImageLoader;
import org.floens.chan.ChanApplication; import org.floens.chan.ChanApplication;
import org.floens.chan.chan.ChanLoader; import org.floens.chan.chan.ChanLoader;
import org.floens.chan.chan.ChanParser;
import org.floens.chan.core.cache.FileCache; import org.floens.chan.core.cache.FileCache;
import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.manager.BoardManager;
@ -57,7 +56,6 @@ import dagger.Provides;
injects = { injects = {
// Context.class, // ApplicationContext // Context.class, // ApplicationContext
ChanParser.class,
BoardManager.class, BoardManager.class,
DatabaseManager.class, DatabaseManager.class,
ReplyManager.class, ReplyManager.class,

@ -21,6 +21,10 @@ public class SiteSetupPresenter {
} }
public void show() { public void show() {
setBoardCount(callback, site);
}
private void setBoardCount(Callback callback, Site site) {
callback.setBoardCount( callback.setBoardCount(
databaseManager.runTaskSync( databaseManager.runTaskSync(
databaseManager.getDatabaseBoardManager().getSiteSavedBoards(site) databaseManager.getDatabaseBoardManager().getSiteSavedBoards(site)

@ -0,0 +1,25 @@
/*
* 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.common;
import org.floens.chan.core.model.Post;
import org.floens.chan.ui.theme.Theme;
public interface ChanParser {
Post parse(Theme theme, Post.Builder builder);
}

@ -4,6 +4,8 @@ package org.floens.chan.core.site.common;
import android.util.JsonReader; import android.util.JsonReader;
public interface ChanReader { public interface ChanReader {
ChanParser getParser();
void loadThread(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception; void loadThread(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception;
void loadCatalog(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception; void loadCatalog(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception;

@ -21,7 +21,6 @@ import android.util.JsonReader;
import org.floens.chan.chan.ChanLoaderRequestParams; import org.floens.chan.chan.ChanLoaderRequestParams;
import org.floens.chan.chan.ChanLoaderResponse; import org.floens.chan.chan.ChanLoaderResponse;
import org.floens.chan.chan.ChanParser;
import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.database.DatabaseSavedReplyManager; import org.floens.chan.core.database.DatabaseSavedReplyManager;
import org.floens.chan.core.manager.FilterEngine; import org.floens.chan.core.manager.FilterEngine;
@ -70,9 +69,6 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
@Inject @Inject
FilterEngine filterEngine; FilterEngine filterEngine;
@Inject
ChanParser chanParser;
private Loadable loadable; private Loadable loadable;
private List<Post> cached; private List<Post> cached;
private ChanReader reader; private ChanReader reader;
@ -181,7 +177,7 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
List<Callable<Post>> tasks = new ArrayList<>(toParse.size()); List<Callable<Post>> tasks = new ArrayList<>(toParse.size());
for (int i = 0; i < toParse.size(); i++) { for (int i = 0; i < toParse.size(); i++) {
Post.Builder post = toParse.get(i); Post.Builder post = toParse.get(i);
tasks.add(new PostParseCallable(filterEngine, filters, databaseSavedReplyManager, post, chanParser)); tasks.add(new PostParseCallable(filterEngine, filters, databaseSavedReplyManager, post, reader));
} }
if (!tasks.isEmpty()) { if (!tasks.isEmpty()) {

@ -0,0 +1,160 @@
/*
* 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.common;
import android.graphics.Typeface;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostLinkable;
import org.floens.chan.ui.span.ForegroundColorSpanHashed;
import org.floens.chan.ui.theme.Theme;
import org.jsoup.nodes.Element;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DefaultFutabaChanParserHandler implements FutabaChanParserHandler {
private static final Pattern COLOR_PATTERN = Pattern.compile("color:#([0-9a-fA-F]*)");
@Override
public CharSequence handleParagraph(FutabaChanParser parser, Theme theme, Post.Builder post, CharSequence text, Element element) {
return text;
}
@Override
public CharSequence handleSpan(FutabaChanParser parser, Theme theme, Post.Builder post, Element span) {
SpannableString quote;
Set<String> classes = span.classNames();
if (classes.contains("deadlink")) {
quote = new SpannableString(span.text());
quote.setSpan(new ForegroundColorSpanHashed(theme.quoteColor), 0, quote.length(), 0);
quote.setSpan(new StrikethroughSpan(), 0, quote.length(), 0);
} else if (classes.contains("fortune")) {
// html looks like <span class="fortune" style="color:#0893e1"><br><br><b>Your fortune:</b>
// manually add these <br>
quote = new SpannableString("\n\n" + span.text());
String style = span.attr("style");
if (!TextUtils.isEmpty(style)) {
style = style.replace(" ", "");
// private static final Pattern COLOR_PATTERN = Pattern.compile("color:#([0-9a-fA-F]*)");
Matcher matcher = COLOR_PATTERN.matcher(style);
int hexColor = 0xff0000;
if (matcher.find()) {
String group = matcher.group(1);
if (!TextUtils.isEmpty(group)) {
try {
hexColor = Integer.parseInt(group, 16);
} catch (NumberFormatException ignored) {
}
}
}
if (hexColor >= 0 && hexColor <= 0xffffff) {
quote.setSpan(new ForegroundColorSpanHashed(0xff000000 + hexColor), 0, quote.length(), 0);
quote.setSpan(new StyleSpan(Typeface.BOLD), 0, quote.length(), 0);
}
}
} else if (classes.contains("abbr")) {
return null;
} else {
quote = new SpannableString(span.text());
quote.setSpan(new ForegroundColorSpanHashed(theme.inlineQuoteColor), 0, quote.length(), 0);
parser.detectLinks(theme, post, span.text(), quote);
}
return quote;
}
@Override
public Link getLink(FutabaChanParser parser, Theme theme, Post.Builder post, Element anchor) {
String href = anchor.attr("href");
Set<String> classes = anchor.classNames();
PostLinkable.Type t = null;
String key = null;
Object value = null;
if (classes.contains("quotelink")) {
if (href.contains("/thread/")) {
// link to another thread
PostLinkable.ThreadLink threadLink = null;
String[] slashSplit = href.split("/");
if (slashSplit.length == 4) {
String board = slashSplit[1];
String nums = slashSplit[3];
String[] numsSplitted = nums.split("#p");
if (numsSplitted.length == 2) {
try {
int tId = Integer.parseInt(numsSplitted[0]);
int pId = Integer.parseInt(numsSplitted[1]);
threadLink = new PostLinkable.ThreadLink(board, tId, pId);
} catch (NumberFormatException ignored) {
}
}
}
if (threadLink != null) {
t = PostLinkable.Type.THREAD;
key = anchor.text();
value = threadLink;
}
} else {
// normal quote
int id = -1;
String[] splitted = href.split("#p");
if (splitted.length == 2) {
try {
id = Integer.parseInt(splitted[1]);
} catch (NumberFormatException ignored) {
}
}
if (id >= 0) {
t = PostLinkable.Type.QUOTE;
key = anchor.text();
value = id;
}
}
} else {
// normal link
t = PostLinkable.Type.LINK;
key = anchor.text();
value = href;
}
if (t != null && key != null && value != null) {
Link link = new Link();
link.type = t;
link.key = key;
link.value = value;
return link;
} else {
return null;
}
}
}

@ -15,19 +15,17 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.floens.chan.chan; package org.floens.chan.core.site.common;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.style.BackgroundColorSpan; import android.text.style.BackgroundColorSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan; import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan; import android.text.style.UnderlineSpan;
import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.model.Post; import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostLinkable; import org.floens.chan.core.model.PostLinkable;
import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.settings.ChanSettings;
@ -54,34 +52,23 @@ import java.util.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Singleton;
import static org.floens.chan.utils.AndroidUtils.sp; import static org.floens.chan.utils.AndroidUtils.sp;
@Singleton public class FutabaChanParser implements ChanParser {
public class ChanParser { private static final String TAG = "FutabaChanParser";
private static final String TAG = "ChanParser";
private static final Pattern COLOR_PATTERN = Pattern.compile("color:#([0-9a-fA-F]*)");
private static final String SAVED_REPLY_SUFFIX = " (You)"; private static final String SAVED_REPLY_SUFFIX = " (You)";
private static final String OP_REPLY_SUFFIX = " (OP)"; private static final String OP_REPLY_SUFFIX = " (OP)";
@Inject
DatabaseManager databaseManager;
private final LinkExtractor linkExtractor = LinkExtractor.builder().linkTypes(EnumSet.of(LinkType.URL)).build(); private final LinkExtractor linkExtractor = LinkExtractor.builder().linkTypes(EnumSet.of(LinkType.URL)).build();
@Inject private FutabaChanParserHandler handler;
public ChanParser() {
}
public Post parse(Post.Builder post) { public FutabaChanParser(FutabaChanParserHandler handler) {
return parse(null, post); this.handler = handler;
} }
@Override
public Post parse(Theme theme, Post.Builder builder) { public Post parse(Theme theme, Post.Builder builder) {
if (theme == null) { if (theme == null) {
theme = ThemeHelper.getInstance().getTheme(); theme = ThemeHelper.getInstance().getTheme();
@ -240,56 +227,30 @@ public class ChanParser {
return spannable; return spannable;
} else { } else {
switch (node.nodeName()) { switch (node.nodeName()) {
case "p": {
List<Node> innerNodes = node.childNodes();
List<CharSequence> texts = new ArrayList<>(innerNodes.size() + 1);
for (Node innerNode : innerNodes) {
CharSequence nodeParsed = parseNode(theme, post, innerNode);
if (nodeParsed != null) {
texts.add(nodeParsed);
}
}
if (node.nextSibling() != null) {
texts.add("\n");
}
CharSequence res = TextUtils.concat(texts.toArray(new CharSequence[texts.size()]));
return handler.handleParagraph(this, theme, post, res, (Element) node);
}
case "br": { case "br": {
return "\n"; return "\n";
} }
case "span": { case "span": {
Element span = (Element) node; return handler.handleSpan(this, theme, post, (Element) node);
SpannableString quote;
Set<String> classes = span.classNames();
if (classes.contains("deadlink")) {
quote = new SpannableString(span.text());
quote.setSpan(new ForegroundColorSpanHashed(theme.quoteColor), 0, quote.length(), 0);
quote.setSpan(new StrikethroughSpan(), 0, quote.length(), 0);
} else if (classes.contains("fortune")) {
// html looks like <span class="fortune" style="color:#0893e1"><br><br><b>Your fortune:</b>
// manually add these <br>
quote = new SpannableString("\n\n" + span.text());
String style = span.attr("style");
if (!TextUtils.isEmpty(style)) {
style = style.replace(" ", "");
// private static final Pattern COLOR_PATTERN = Pattern.compile("color:#([0-9a-fA-F]*)");
Matcher matcher = COLOR_PATTERN.matcher(style);
int hexColor = 0xff0000;
if (matcher.find()) {
String group = matcher.group(1);
if (!TextUtils.isEmpty(group)) {
try {
hexColor = Integer.parseInt(group, 16);
} catch (NumberFormatException ignored) {
}
}
}
if (hexColor >= 0 && hexColor <= 0xffffff) {
quote.setSpan(new ForegroundColorSpanHashed(0xff000000 + hexColor), 0, quote.length(), 0);
quote.setSpan(new StyleSpan(Typeface.BOLD), 0, quote.length(), 0);
}
}
} else if (classes.contains("abbr")) {
return null;
} else {
quote = new SpannableString(span.text());
quote.setSpan(new ForegroundColorSpanHashed(theme.inlineQuoteColor), 0, quote.length(), 0);
detectLinks(theme, post, span.text(), quote);
}
return quote;
} }
case "table": { case "table": {
Element table = (Element) node; Element table = (Element) node;
@ -383,78 +344,33 @@ public class ChanParser {
} }
private CharSequence parseAnchor(Theme theme, Post.Builder post, Element anchor) { private CharSequence parseAnchor(Theme theme, Post.Builder post, Element anchor) {
String href = anchor.attr("href"); FutabaChanParserHandler.Link handlerLink = handler.getLink(this, theme, post, anchor);
Set<String> classes = anchor.classNames();
PostLinkable.Type t = null;
String key = null;
Object value = null;
if (classes.contains("quotelink")) {
if (href.contains("/thread/")) {
// link to another thread
PostLinkable.ThreadLink threadLink = null;
String[] slashSplit = href.split("/");
if (slashSplit.length == 4) {
String board = slashSplit[1];
String nums = slashSplit[3];
String[] numsSplitted = nums.split("#p");
if (numsSplitted.length == 2) {
try {
int tId = Integer.parseInt(numsSplitted[0]);
int pId = Integer.parseInt(numsSplitted[1]);
threadLink = new PostLinkable.ThreadLink(board, tId, pId);
} catch (NumberFormatException ignored) {
}
}
}
if (threadLink != null) { if (handlerLink != null) {
t = PostLinkable.Type.THREAD; SpannableString link = new SpannableString(handlerLink.key);
key = anchor.text() + " \u2192"; // arrow to the right PostLinkable pl = new PostLinkable(theme, handlerLink.key, handlerLink.value, handlerLink.type);
value = threadLink; link.setSpan(pl, 0, link.length(), 0);
} post.addLinkable(pl);
} else {
// normal quote
int id = -1;
String[] splitted = href.split("#p");
if (splitted.length == 2) {
try {
id = Integer.parseInt(splitted[1]);
} catch (NumberFormatException ignored) {
}
}
if (id >= 0) { if (handlerLink.type == PostLinkable.Type.THREAD) {
t = PostLinkable.Type.QUOTE; handlerLink.key += " \u2192"; // arrow to the right
key = anchor.text(); }
value = id;
post.addReplyTo(id);
// Append OP when its a reply to OP if (handlerLink.type == PostLinkable.Type.QUOTE) {
if (id == post.opId) { int postNo = (int) handlerLink.value;
key += OP_REPLY_SUFFIX; post.addReplyTo(postNo);
}
// Append You when it's a reply to an saved reply // Append OP when its a reply to OP
if (databaseManager.getDatabaseSavedReplyManager().isSaved(post.board.code, id)) { if (postNo == post.opId) {
key += SAVED_REPLY_SUFFIX; handlerLink.key += OP_REPLY_SUFFIX;
}
} }
}
} else {
// normal link
t = PostLinkable.Type.LINK;
key = anchor.text();
value = href;
}
if (t != null && key != null && value != null) { // Append You when it's a reply to an saved reply
SpannableString link = new SpannableString(key); // TODO(multisite)
PostLinkable pl = new PostLinkable(theme, key, value, t); /*if (databaseManager.getDatabaseSavedReplyManager().isSaved(post.board.code, id)) {
link.setSpan(pl, 0, link.length(), 0); key += SAVED_REPLY_SUFFIX;
post.addLinkable(pl); }*/
}
return link; return link;
} else { } else {
@ -462,10 +378,10 @@ public class ChanParser {
} }
} }
private void detectLinks(Theme theme, Post.Builder post, String text, SpannableString spannable) { public void detectLinks(Theme theme, Post.Builder post, String text, SpannableString spannable) {
// use autolink-java lib to detect links // use autolink-java lib to detect links
final Iterable<LinkSpan> links = linkExtractor.extractLinks(text); final Iterable<LinkSpan> links = linkExtractor.extractLinks(text);
for(final LinkSpan link : links) { for (final LinkSpan link : links) {
final String linkText = text.substring(link.getBeginIndex(), link.getEndIndex()); final String linkText = text.substring(link.getBeginIndex(), link.getEndIndex());
final PostLinkable pl = new PostLinkable(theme, linkText, linkText, PostLinkable.Type.LINK); final PostLinkable pl = new PostLinkable(theme, linkText, linkText, PostLinkable.Type.LINK);
spannable.setSpan(pl, link.getBeginIndex(), link.getEndIndex(), 0); spannable.setSpan(pl, link.getBeginIndex(), link.getEndIndex(), 0);

@ -0,0 +1,36 @@
/*
* 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.common;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostLinkable;
import org.floens.chan.ui.theme.Theme;
import org.jsoup.nodes.Element;
public interface FutabaChanParserHandler {
CharSequence handleParagraph(FutabaChanParser parser, Theme theme, Post.Builder post, CharSequence text, Element element);
CharSequence handleSpan(FutabaChanParser parser, Theme theme, Post.Builder post, Element span);
Link getLink(FutabaChanParser parser, Theme theme, Post.Builder post, Element anchor);
class Link {
public PostLinkable.Type type;
public String key;
public Object value;
}
}

@ -15,6 +15,21 @@ import java.util.Map;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
public class FutabaChanReader implements ChanReader { public class FutabaChanReader implements ChanReader {
private final ChanParser chanParser;
public FutabaChanReader() {
this.chanParser = new FutabaChanParser(new DefaultFutabaChanParserHandler());
}
public FutabaChanReader(ChanParser chanParser) {
this.chanParser = chanParser;
}
@Override
public ChanParser getParser() {
return chanParser;
}
@Override @Override
public void loadThread(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception { public void loadThread(JsonReader reader, ChanReaderProcessingQueue queue) throws Exception {
reader.beginObject(); reader.beginObject();

@ -17,7 +17,6 @@
*/ */
package org.floens.chan.core.site.common; package org.floens.chan.core.site.common;
import org.floens.chan.chan.ChanParser;
import org.floens.chan.core.database.DatabaseSavedReplyManager; import org.floens.chan.core.database.DatabaseSavedReplyManager;
import org.floens.chan.core.manager.FilterEngine; import org.floens.chan.core.manager.FilterEngine;
import org.floens.chan.core.model.orm.Filter; import org.floens.chan.core.model.orm.Filter;
@ -27,7 +26,7 @@ import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
// Called concurrently to parse the post html and the filters on it // Called concurrently to parse the post html and the filters on it
// Belongs to ChanReaderRequest // belong to ChanReaderRequest
class PostParseCallable implements Callable<Post> { class PostParseCallable implements Callable<Post> {
private static final String TAG = "PostParseCallable"; private static final String TAG = "PostParseCallable";
@ -35,18 +34,18 @@ class PostParseCallable implements Callable<Post> {
private List<Filter> filters; private List<Filter> filters;
private DatabaseSavedReplyManager savedReplyManager; private DatabaseSavedReplyManager savedReplyManager;
private Post.Builder post; private Post.Builder post;
private ChanParser parser; private ChanReader reader;
public PostParseCallable(FilterEngine filterEngine, public PostParseCallable(FilterEngine filterEngine,
List<Filter> filters, List<Filter> filters,
DatabaseSavedReplyManager savedReplyManager, DatabaseSavedReplyManager savedReplyManager,
Post.Builder post, Post.Builder post,
ChanParser parser) { ChanReader reader) {
this.filterEngine = filterEngine; this.filterEngine = filterEngine;
this.filters = filters; this.filters = filters;
this.savedReplyManager = savedReplyManager; this.savedReplyManager = savedReplyManager;
this.post = post; this.post = post;
this.parser = parser; this.reader = reader;
} }
@Override @Override
@ -60,7 +59,7 @@ class PostParseCallable implements Callable<Post> {
// Logger.e(TAG, "Incorrect data about post received for post " + post.no); // Logger.e(TAG, "Incorrect data about post received for post " + post.no);
// return null; // return null;
// } // }
return parser.parse(post); return reader.getParser().parse(null, post);
} }
private void processPostFilter(Post.Builder post) { private void processPostFilter(Post.Builder post) {

@ -33,6 +33,7 @@ import org.floens.chan.core.site.SiteEndpoints;
import org.floens.chan.core.site.SiteIcon; import org.floens.chan.core.site.SiteIcon;
import org.floens.chan.core.site.SiteRequestModifier; import org.floens.chan.core.site.SiteRequestModifier;
import org.floens.chan.core.site.common.ChanReader; import org.floens.chan.core.site.common.ChanReader;
import org.floens.chan.core.site.common.FutabaChanParser;
import org.floens.chan.core.site.common.FutabaChanReader; import org.floens.chan.core.site.common.FutabaChanReader;
import org.floens.chan.core.site.http.DeleteRequest; import org.floens.chan.core.site.http.DeleteRequest;
import org.floens.chan.core.site.http.HttpCall; import org.floens.chan.core.site.http.HttpCall;
@ -225,7 +226,8 @@ public class Chan8 extends SiteBase {
@Override @Override
public ChanReader chanReader() { public ChanReader chanReader() {
return new FutabaChanReader(); FutabaChanParser parser = new FutabaChanParser(new Chan8ParserHandler());
return new FutabaChanReader(parser);
} }
@Override @Override

@ -0,0 +1,133 @@
/*
* 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.text.SpannableString;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostLinkable;
import org.floens.chan.core.site.common.DefaultFutabaChanParserHandler;
import org.floens.chan.core.site.common.FutabaChanParser;
import org.floens.chan.ui.span.ForegroundColorSpanHashed;
import org.floens.chan.ui.theme.Theme;
import org.jsoup.nodes.Element;
import java.util.Set;
public class Chan8ParserHandler extends DefaultFutabaChanParserHandler {
@Override
public CharSequence handleParagraph(FutabaChanParser parser, Theme theme, Post.Builder post, CharSequence text, Element element) {
if (element.hasClass("quote")) {
SpannableString quote = new SpannableString(text);
quote.setSpan(new ForegroundColorSpanHashed(theme.inlineQuoteColor), 0, quote.length(), 0);
parser.detectLinks(theme, post, quote.toString(), quote);
return quote;
} else {
return text;
}
}
@Override
public CharSequence handleSpan(FutabaChanParser parser, Theme theme, Post.Builder post, Element span) {
SpannableString quote;
Set<String> classes = span.classNames();
if (classes.contains("abbr")) {
return null;
} else if (classes.contains("spoiler")) {
quote = new SpannableString(span.text());
PostLinkable pl = new PostLinkable(theme, span.text(), span.text(), PostLinkable.Type.SPOILER);
quote.setSpan(pl, 0, quote.length(), 0);
post.addLinkable(pl);
} else {
quote = new SpannableString(span.text());
quote.setSpan(new ForegroundColorSpanHashed(theme.inlineQuoteColor), 0, quote.length(), 0);
parser.detectLinks(theme, post, span.text(), quote);
}
return quote;
}
@Override
public Link getLink(FutabaChanParser parser, Theme theme, Post.Builder post, Element anchor) {
String href = anchor.attr("href");
PostLinkable.Type t = null;
String key = null;
Object value = null;
if (href.startsWith("/")) {
if (href.contains("/thread/")) {
// link to another thread
PostLinkable.ThreadLink threadLink = null;
String[] slashSplit = href.split("/");
if (slashSplit.length == 4) {
String board = slashSplit[1];
String nums = slashSplit[3];
String[] numsSplitted = nums.split("#p");
if (numsSplitted.length == 2) {
try {
int tId = Integer.parseInt(numsSplitted[0]);
int pId = Integer.parseInt(numsSplitted[1]);
threadLink = new PostLinkable.ThreadLink(board, tId, pId);
} catch (NumberFormatException ignored) {
}
}
}
if (threadLink != null) {
t = PostLinkable.Type.THREAD;
key = anchor.text();
value = threadLink;
}
} else {
// normal quote
int id = -1;
String[] splitted = href.split("#");
if (splitted.length == 2) {
try {
id = Integer.parseInt(splitted[1]);
} catch (NumberFormatException ignored) {
}
}
if (id >= 0) {
t = PostLinkable.Type.QUOTE;
key = anchor.text();
value = id;
}
}
} else {
// normal link
t = PostLinkable.Type.LINK;
key = anchor.text();
value = href;
}
if (t != null && key != null && value != null) {
Link link = new Link();
link.type = t;
link.key = key;
link.value = value;
return link;
} else {
return null;
}
}
}

@ -20,7 +20,6 @@ package org.floens.chan.ui.activity;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.net.Uri;
import android.nfc.NdefMessage; import android.nfc.NdefMessage;
import android.nfc.NfcAdapter; import android.nfc.NfcAdapter;
import android.nfc.NfcEvent; import android.nfc.NfcEvent;
@ -35,7 +34,6 @@ import android.view.ViewGroup;
import org.floens.chan.Chan; import org.floens.chan.Chan;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.chan.ChanHelper;
import org.floens.chan.controller.Controller; import org.floens.chan.controller.Controller;
import org.floens.chan.controller.NavigationController; import org.floens.chan.controller.NavigationController;
import org.floens.chan.core.database.DatabaseLoadableManager; import org.floens.chan.core.database.DatabaseLoadableManager;
@ -135,7 +133,7 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat
private void setupFromStateOrFreshLaunch(Bundle savedInstanceState) { private void setupFromStateOrFreshLaunch(Bundle savedInstanceState) {
boolean loadDefault = true; boolean loadDefault = true;
if (savedInstanceState != null) { /*if (savedInstanceState != null) {
// Restore the activity state from the previously saved state. // Restore the activity state from the previously saved state.
ChanState chanState = savedInstanceState.getParcelable(STATE_KEY); ChanState chanState = savedInstanceState.getParcelable(STATE_KEY);
if (chanState == null) { if (chanState == null) {
@ -177,7 +175,7 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat
.show(); .show();
} }
} }
} }*/
// Not from a state or from an url, launch the setup controller if no boards are setup up yet, // Not from a state or from an url, launch the setup controller if no boards are setup up yet,
// otherwise load the default saved board. // otherwise load the default saved board.

@ -58,10 +58,11 @@ public class SiteSetupController extends SettingsController implements SiteSetup
// Preferences // Preferences
populatePreferences(); populatePreferences();
buildPreferences();
// Presenter // Presenter
presenter.create(this, site); presenter.create(this, site);
buildPreferences();
} }
public void setSite(Site site) { public void setSite(Site site) {

@ -37,7 +37,8 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.chan.ChanParser; import org.floens.chan.core.site.common.DefaultFutabaChanParserHandler;
import org.floens.chan.core.site.common.FutabaChanParser;
import org.floens.chan.controller.Controller; import org.floens.chan.controller.Controller;
import org.floens.chan.core.model.orm.Board; import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Loadable; import org.floens.chan.core.model.orm.Loadable;
@ -250,7 +251,7 @@ public class ThemeSettingsController extends Controller implements View.OnClickL
"http://example.com/" + "http://example.com/" +
"<br>" + "<br>" +
"Phasellus consequat semper sodales. Donec dolor lectus, aliquet nec mollis vel, rutrum vel enim."); "Phasellus consequat semper sodales. Donec dolor lectus, aliquet nec mollis vel, rutrum vel enim.");
Post post = getGraph().get(ChanParser.class).parse(theme, builder); Post post = new FutabaChanParser(new DefaultFutabaChanParserHandler()).parse(theme, builder);
LinearLayout linearLayout = new LinearLayout(themeContext); LinearLayout linearLayout = new LinearLayout(themeContext);
linearLayout.setOrientation(LinearLayout.VERTICAL); linearLayout.setOrientation(LinearLayout.VERTICAL);

@ -168,7 +168,10 @@ public class SettingsController extends Controller implements AndroidUtils.OnMea
bottom.setText(bottomText); bottom.setText(bottomText);
} }
AnimationUtils.animateHeight(bottom, bottomText != null, ((View) view.getParent()).getWidth()); // This way of animating never works on textviews if they're just added.
if (bottom.getHeight() != 0) {
AnimationUtils.animateHeight(bottom, bottomText != null, ((View) view.getParent()).getWidth());
}
} else { } else {
bottom.setText(bottomText); bottom.setText(bottomText);
bottom.setVisibility(bottomText == null ? View.GONE : View.VISIBLE); bottom.setVisibility(bottomText == null ? View.GONE : View.VISIBLE);

@ -30,7 +30,7 @@ import org.floens.chan.utils.AndroidUtils;
/** /**
* A Theme<br> * A Theme<br>
* Used for setting the toolbar color, and passed around {@link org.floens.chan.chan.ChanParser} to give the spans the correct color.<br> * Used for setting the toolbar color, and passed around {@link org.floens.chan.core.site.common.ChanParser} to give the spans the correct color.<br>
* Technically should the parser not do UI, but it is important that the spans do not get created on an UI thread for performance. * Technically should the parser not do UI, but it is important that the spans do not get created on an UI thread for performance.
*/ */
public class Theme { public class Theme {

Loading…
Cancel
Save