From f8c182bba524ea1d74f3d7ae1527b3ee47db5a96 Mon Sep 17 00:00:00 2001 From: Floens Date: Thu, 5 Jan 2017 00:09:21 +0100 Subject: [PATCH] Make http calls abstract, call through Site --- .../java/org/floens/chan/chan/ChanUrls.java | 12 --- .../org/floens/chan/core/di/ChanGraph.java | 13 ++- .../core/{http => manager}/ReplyManager.java | 43 +------- .../chan/core/model/BoardReference.java | 28 +++++ .../org/floens/chan/core/model/Loadable.java | 7 +- .../chan/core/presenter/ReplyPresenter.java | 44 ++++---- .../chan/core/presenter/ThreadPresenter.java | 27 +++-- .../java/org/floens/chan/core/site/Site.java | 70 +++++++++++- .../floens/chan/core/site/SiteEndpoints.java | 25 ++++- .../chan/core/site/http/DeleteRequest.java | 37 +++++++ .../chan/core/site/http/DeleteResponse.java | 24 +++++ .../chan/core/{ => site}/http/HttpCall.java | 66 ++++++++---- .../chan/core/site/http/HttpCallManager.java | 63 +++++++++++ .../chan/core/site/http/LoginRequest.java | 33 ++++++ .../chan/core/site/http/LoginResponse.java | 27 +++++ .../chan/core/{model => site/http}/Reply.java | 16 +-- .../chan/core/site/http/ReplyResponse.java | 42 ++++++++ .../chan/core/site/sites/chan4/Chan4.java | 102 +++++++++++++++++- .../sites/chan4/Chan4DeleteHttpCall.java} | 33 +++--- .../sites/chan4/Chan4PassHttpCall.java} | 37 ++++--- .../chan4}/Chan4ReaderRequest.java | 2 +- .../sites/chan4/Chan4ReplyHttpCall.java} | 55 +++++----- .../chan4}/PostParseCallable.java | 2 +- .../ui/controller/PassSettingsController.java | 46 ++++---- .../chan/ui/controller/ReportController.java | 29 +++-- .../chan/ui/helper/ImagePickDelegate.java | 4 +- .../floens/chan/ui/layout/ReplyLayout.java | 2 +- 27 files changed, 672 insertions(+), 217 deletions(-) rename Clover/app/src/main/java/org/floens/chan/core/{http => manager}/ReplyManager.java (61%) create mode 100644 Clover/app/src/main/java/org/floens/chan/core/model/BoardReference.java create mode 100644 Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteRequest.java create mode 100644 Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteResponse.java rename Clover/app/src/main/java/org/floens/chan/core/{ => site}/http/HttpCall.java (62%) create mode 100644 Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCallManager.java create mode 100644 Clover/app/src/main/java/org/floens/chan/core/site/http/LoginRequest.java create mode 100644 Clover/app/src/main/java/org/floens/chan/core/site/http/LoginResponse.java rename Clover/app/src/main/java/org/floens/chan/core/{model => site/http}/Reply.java (79%) create mode 100644 Clover/app/src/main/java/org/floens/chan/core/site/http/ReplyResponse.java rename Clover/app/src/main/java/org/floens/chan/core/{http/DeleteHttpCall.java => site/sites/chan4/Chan4DeleteHttpCall.java} (63%) rename Clover/app/src/main/java/org/floens/chan/core/{http/PassHttpCall.java => site/sites/chan4/Chan4PassHttpCall.java} (73%) rename Clover/app/src/main/java/org/floens/chan/core/site/{loaders => sites/chan4}/Chan4ReaderRequest.java (99%) rename Clover/app/src/main/java/org/floens/chan/core/{http/ReplyHttpCall.java => site/sites/chan4/Chan4ReplyHttpCall.java} (69%) rename Clover/app/src/main/java/org/floens/chan/core/site/{loaders => sites/chan4}/PostParseCallable.java (98%) diff --git a/Clover/app/src/main/java/org/floens/chan/chan/ChanUrls.java b/Clover/app/src/main/java/org/floens/chan/chan/ChanUrls.java index 3c4a08d8..b1526748 100644 --- a/Clover/app/src/main/java/org/floens/chan/chan/ChanUrls.java +++ b/Clover/app/src/main/java/org/floens/chan/chan/ChanUrls.java @@ -24,14 +24,6 @@ public class ChanUrls { return "6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc"; } - public static String getReplyUrl(String board) { - return "https://sys.4chan.org/" + board + "/post"; - } - - public static String getDeleteUrl(String board) { - return "https://sys.4chan.org/" + board + "/imgboard.php"; - } - public static String getBoardUrlDesktop(String board) { return scheme() + "://boards.4chan.org/" + board + "/"; } @@ -52,10 +44,6 @@ public class ChanUrls { return "https://sys.4chan.org/auth"; } - public static String getReportDomain() { - return "https://sys.4chan.org/"; - } - public static String[] getReportCookies(String passId) { return new String[]{"pass_enabled=1;", "pass_id=" + passId + ";"}; } diff --git a/Clover/app/src/main/java/org/floens/chan/core/di/ChanGraph.java b/Clover/app/src/main/java/org/floens/chan/core/di/ChanGraph.java index c9e5f77b..47d6b325 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/di/ChanGraph.java +++ b/Clover/app/src/main/java/org/floens/chan/core/di/ChanGraph.java @@ -8,16 +8,18 @@ import org.floens.chan.chan.ChanLoader; import org.floens.chan.chan.ChanParser; import org.floens.chan.core.cache.FileCache; import org.floens.chan.core.database.DatabaseManager; -import org.floens.chan.core.http.ReplyManager; +import org.floens.chan.core.manager.ReplyManager; import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.manager.FilterEngine; import org.floens.chan.core.manager.WatchManager; -import org.floens.chan.core.site.loaders.Chan4ReaderRequest; +import org.floens.chan.core.site.http.HttpCallManager; +import org.floens.chan.core.site.sites.chan4.Chan4ReaderRequest; import org.floens.chan.core.presenter.ImageViewerPresenter; import org.floens.chan.core.presenter.ReplyPresenter; import org.floens.chan.core.presenter.ThreadPresenter; import org.floens.chan.core.receiver.WatchUpdateReceiver; import org.floens.chan.core.saver.ImageSaveTask; +import org.floens.chan.core.site.sites.chan4.Chan4; import org.floens.chan.ui.activity.StartActivity; import org.floens.chan.ui.adapter.DrawerAdapter; import org.floens.chan.ui.adapter.PostsFilter; @@ -46,6 +48,9 @@ import dagger.Component; NetModule.class }) @Singleton +/** + * Note: please avoid adding inject() statements for Sites. + */ public interface ChanGraph { ChanParser getChanParser(); @@ -61,6 +66,8 @@ public interface ChanGraph { FileCache getFileCache(); + HttpCallManager getHttpCallManager(); + void inject(Chan chan); void inject(MainSettingsController mainSettingsController); @@ -116,4 +123,6 @@ public interface ChanGraph { void inject(ViewThreadController viewThreadController); void inject(WatchManager.PinWatcher pinWatcher); + + void inject(Chan4 chan4); } diff --git a/Clover/app/src/main/java/org/floens/chan/core/http/ReplyManager.java b/Clover/app/src/main/java/org/floens/chan/core/manager/ReplyManager.java similarity index 61% rename from Clover/app/src/main/java/org/floens/chan/core/http/ReplyManager.java rename to Clover/app/src/main/java/org/floens/chan/core/manager/ReplyManager.java index c8fc9668..949fa7a8 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/http/ReplyManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/ReplyManager.java @@ -15,48 +15,32 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.core.http; +package org.floens.chan.core.manager; import android.content.Context; -import org.floens.chan.core.di.UserAgentProvider; import org.floens.chan.core.model.Loadable; -import org.floens.chan.core.model.Reply; +import org.floens.chan.core.site.http.Reply; import java.io.File; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Singleton; -import okhttp3.OkHttpClient; -import okhttp3.Request; - /** - * To send an reply to 4chan. + * Manages replies. */ @Singleton public class ReplyManager { - private static final int TIMEOUT = 30000; - private final Context context; - private String userAgent; - private OkHttpClient client; private Map drafts = new HashMap<>(); @Inject - public ReplyManager(Context context, UserAgentProvider userAgentProvider) { + public ReplyManager(Context context) { this.context = context; - userAgent = userAgentProvider.getUserAgent(); - - client = new OkHttpClient.Builder() - .connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS) - .readTimeout(TIMEOUT, TimeUnit.MILLISECONDS) - .writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS) - .build(); } public Reply getReply(Loadable loadable) { @@ -85,23 +69,4 @@ public class ReplyManager { public File getPickFile() { return new File(context.getCacheDir(), "picked_file"); } - - public void makeHttpCall(HttpCall httpCall, HttpCallback callback) { - httpCall.setCallback(callback); - - Request.Builder requestBuilder = new Request.Builder(); - - httpCall.setup(requestBuilder); - - requestBuilder.header("User-Agent", userAgent); - Request request = requestBuilder.build(); - - client.newCall(request).enqueue(httpCall); - } - - public interface HttpCallback { - void onHttpSuccess(T httpPost); - - void onHttpFail(T httpPost); - } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/BoardReference.java b/Clover/app/src/main/java/org/floens/chan/core/model/BoardReference.java new file mode 100644 index 00000000..f396331e --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/model/BoardReference.java @@ -0,0 +1,28 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.model; + + +public interface BoardReference { + /** + * Get the Board object that this model references. + * + * @return a {@link Board} + */ + Board getBoard(); +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/Loadable.java b/Clover/app/src/main/java/org/floens/chan/core/model/Loadable.java index 1e87b78a..c7a749fa 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/Loadable.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/Loadable.java @@ -35,7 +35,7 @@ import org.floens.chan.core.site.Site; * references the same loadable and that the loadable is properly saved in the database. */ @DatabaseTable -public class Loadable implements SiteReference { +public class Loadable implements SiteReference, BoardReference { @DatabaseField(generatedId = true) public int id; @@ -123,6 +123,11 @@ public class Loadable implements SiteReference { return site; } + @Override + public Board getBoard() { + return board; + } + public void setTitle(String title) { if (!TextUtils.equals(this.title, title)) { this.title = title; diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java index d9adf1ee..7d5b70eb 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java @@ -22,17 +22,20 @@ import android.text.TextUtils; import org.floens.chan.R; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.database.DatabaseManager; -import org.floens.chan.core.http.ReplyHttpCall; -import org.floens.chan.core.http.ReplyManager; +import org.floens.chan.core.manager.ReplyManager; import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.manager.WatchManager; import org.floens.chan.core.model.Board; import org.floens.chan.core.model.ChanThread; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Post; -import org.floens.chan.core.model.Reply; import org.floens.chan.core.model.SavedReply; import org.floens.chan.core.settings.ChanSettings; +import org.floens.chan.core.site.Site; +import org.floens.chan.core.site.http.HttpCall; +import org.floens.chan.core.site.http.HttpCallManager; +import org.floens.chan.core.site.http.ReplyResponse; +import org.floens.chan.core.site.http.Reply; import org.floens.chan.ui.helper.ImagePickDelegate; import org.floens.chan.ui.layout.CaptchaCallback; import org.floens.chan.ui.layout.CaptchaLayoutInterface; @@ -49,7 +52,7 @@ import static org.floens.chan.utils.AndroidUtils.getReadableFileSize; import static org.floens.chan.utils.AndroidUtils.getRes; import static org.floens.chan.utils.AndroidUtils.getString; -public class ReplyPresenter implements ReplyManager.HttpCallback, CaptchaCallback, ImagePickDelegate.ImagePickCallback { +public class ReplyPresenter implements CaptchaCallback, ImagePickDelegate.ImagePickCallback, Site.PostListener { public enum Page { INPUT, CAPTCHA, @@ -70,6 +73,9 @@ public class ReplyPresenter implements ReplyManager.HttpCallback, @Inject WatchManager watchManager; + @Inject + HttpCallManager httpCallManager; + @Inject DatabaseManager databaseManager; @@ -189,21 +195,20 @@ public class ReplyPresenter implements ReplyManager.HttpCallback, public void onSubmitClicked() { callback.loadViewsIntoDraft(draft); - draft.board = loadable.boardCode; - draft.resto = loadable.isThreadMode() ? loadable.no : -1; + draft.loadable = loadable; if (ChanSettings.passLoggedIn()) { - draft.usePass = true; + draft.noVerification = true; draft.passId = ChanSettings.passId.get(); } else { - draft.usePass = false; + draft.noVerification = false; draft.passId = null; } draft.spoilerImage = draft.spoilerImage && board.spoilers; draft.captchaResponse = null; - if (draft.usePass) { + if (draft.noVerification) { makeSubmitCall(); } else { switchPage(Page.CAPTCHA, true); @@ -211,8 +216,8 @@ public class ReplyPresenter implements ReplyManager.HttpCallback, } @Override - public void onHttpSuccess(ReplyHttpCall replyCall) { - if (replyCall.posted) { + public void onPostComplete(HttpCall httpCall, ReplyResponse replyResponse) { + if (replyResponse.posted) { if (ChanSettings.postPinThread.get() && loadable.isThreadMode()) { ChanThread thread = callback.getThread(); if (thread != null) { @@ -220,7 +225,7 @@ public class ReplyPresenter implements ReplyManager.HttpCallback, } } - SavedReply savedReply = new SavedReply(loadable.boardCode, replyCall.postNo, replyCall.password); + SavedReply savedReply = new SavedReply(loadable.boardCode, replyResponse.postNo, replyResponse.password); databaseManager.runTask(databaseManager.getDatabaseSavedReplyManager().saveReply(savedReply)); switchPage(Page.INPUT, false); @@ -234,23 +239,20 @@ public class ReplyPresenter implements ReplyManager.HttpCallback, callback.onPosted(); if (bound && !loadable.isThreadMode()) { - callback.showThread(databaseManager.getDatabaseLoadableManager().get(Loadable.forThread(loadable.site, loadable.board, replyCall.postNo))); + callback.showThread(databaseManager.getDatabaseLoadableManager().get(Loadable.forThread(loadable.site, loadable.board, replyResponse.postNo))); } } else { - if (replyCall.errorMessage == null) { - replyCall.errorMessage = getString(R.string.reply_error); + if (replyResponse.errorMessage == null) { + replyResponse.errorMessage = getString(R.string.reply_error); } switchPage(Page.INPUT, true); - callback.openMessage(true, false, replyCall.errorMessage, true); - if (replyCall.probablyBanned) { -// callback.openMessageWebview(); - } + callback.openMessage(true, false, replyResponse.errorMessage, true); } } @Override - public void onHttpFail(ReplyHttpCall httpPost) { + public void onPostError(HttpCall httpCall) { switchPage(Page.INPUT, true); callback.openMessage(true, false, getString(R.string.reply_error), true); } @@ -338,7 +340,7 @@ public class ReplyPresenter implements ReplyManager.HttpCallback, } private void makeSubmitCall() { - replyManager.makeHttpCall(new ReplyHttpCall(draft), this); + loadable.getSite().post(draft, this); switchPage(Page.LOADING, true); } diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java index 0965fd5a..053b0bd8 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java @@ -25,8 +25,7 @@ import org.floens.chan.chan.ChanLoader; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.exception.ChanLoaderException; -import org.floens.chan.core.http.DeleteHttpCall; -import org.floens.chan.core.http.ReplyManager; +import org.floens.chan.core.manager.ReplyManager; import org.floens.chan.core.manager.WatchManager; import org.floens.chan.core.model.Board; import org.floens.chan.core.model.ChanThread; @@ -40,6 +39,10 @@ import org.floens.chan.core.model.SavedReply; import org.floens.chan.core.pool.ChanLoaderFactory; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.site.Site; +import org.floens.chan.core.site.http.DeleteRequest; +import org.floens.chan.core.site.http.DeleteResponse; +import org.floens.chan.core.site.http.HttpCall; +import org.floens.chan.core.site.http.HttpCallManager; import org.floens.chan.ui.adapter.PostAdapter; import org.floens.chan.ui.adapter.PostsFilter; import org.floens.chan.ui.cell.PostCellInterface; @@ -85,6 +88,9 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt @Inject ReplyManager replyManager; + @Inject + HttpCallManager httpCallManager; + @Inject ChanLoaderFactory chanLoaderFactory; @@ -426,7 +432,10 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt menu.add(new FloatingMenuItem(POST_OPTION_OPEN_BROWSER, R.string.action_open_browser)); menu.add(new FloatingMenuItem(POST_OPTION_SHARE, R.string.post_share)); menu.add(new FloatingMenuItem(POST_OPTION_COPY_TEXT, R.string.post_copy_text)); - menu.add(new FloatingMenuItem(POST_OPTION_REPORT, R.string.post_report)); + + if (loadable.getSite().feature(Site.Feature.POST_REPORT)) { + menu.add(new FloatingMenuItem(POST_OPTION_REPORT, R.string.post_report)); + } if (!loadable.isThreadMode()) { menu.add(new FloatingMenuItem(POST_OPTION_HIDE, R.string.post_hide)); @@ -603,14 +612,14 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt databaseManager.getDatabaseSavedReplyManager().findSavedReply(post.boardId, post.no) ); if (reply != null) { - replyManager.makeHttpCall(new DeleteHttpCall(reply, onlyImageDelete), new ReplyManager.HttpCallback() { + loadable.getSite().delete(new DeleteRequest(loadable.getSite(), post, reply, onlyImageDelete), new Site.DeleteListener() { @Override - public void onHttpSuccess(DeleteHttpCall httpPost) { + public void onDeleteComplete(HttpCall httpPost, DeleteResponse deleteResponse) { String message; - if (httpPost.deleted) { + if (deleteResponse.deleted) { message = getString(R.string.delete_success); - } else if (!TextUtils.isEmpty(httpPost.errorMessage)) { - message = httpPost.errorMessage; + } else if (!TextUtils.isEmpty(deleteResponse.errorMessage)) { + message = deleteResponse.errorMessage; } else { message = getString(R.string.delete_error); } @@ -618,7 +627,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt } @Override - public void onHttpFail(DeleteHttpCall httpPost) { + public void onDeleteError(HttpCall httpCall) { threadPresenterCallback.hideDeleting(getString(R.string.delete_error)); } }); diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/Site.java b/Clover/app/src/main/java/org/floens/chan/core/site/Site.java index 109a1b90..ee124932 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/Site.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/Site.java @@ -1,23 +1,65 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.core.site; import org.floens.chan.chan.ChanLoaderRequest; import org.floens.chan.chan.ChanLoaderRequestParams; import org.floens.chan.core.model.Board; +import org.floens.chan.core.model.Loadable; +import org.floens.chan.core.model.Post; +import org.floens.chan.core.site.http.DeleteRequest; +import org.floens.chan.core.site.http.DeleteResponse; +import org.floens.chan.core.site.http.HttpCall; +import org.floens.chan.core.site.http.LoginRequest; +import org.floens.chan.core.site.http.LoginResponse; +import org.floens.chan.core.site.http.Reply; +import org.floens.chan.core.site.http.ReplyResponse; public interface Site { enum Feature { /** * This site supports posting. (Or rather, we've implemented support for it.) + * + * @see #post(Reply, PostListener) + * @see SiteEndpoints#reply(Loadable) */ POSTING, /** * This site supports deleting posts. + * + * @see #delete(DeleteRequest, DeleteListener) + * @see SiteEndpoints#delete(Post) */ POST_DELETE, + /** + * This site supports reporting posts. + * + * @see SiteEndpoints#report(Post) + */ + POST_REPORT, + /** * This site supports some sort of login (like 4pass). + * + * @see #login(LoginRequest, LoginListener) + * @see SiteEndpoints#login() */ LOGIN } @@ -75,11 +117,35 @@ public interface Site { void boards(BoardsListener boardsListener); + interface BoardsListener { + void onBoardsReceived(Boards boards); + } + Board board(String name); ChanLoaderRequest loaderRequest(ChanLoaderRequestParams request); - interface BoardsListener { - void onBoardsReceived(Boards boards); + void post(Reply reply, PostListener postListener); + + interface PostListener { + void onPostComplete(HttpCall httpCall, ReplyResponse replyResponse); + + void onPostError(HttpCall httpCall); + } + + void delete(DeleteRequest deleteRequest, DeleteListener deleteListener); + + interface DeleteListener { + void onDeleteComplete(HttpCall httpCall, DeleteResponse deleteResponse); + + void onDeleteError(HttpCall httpCall); + } + + void login(LoginRequest loginRequest, LoginListener loginListener); + + interface LoginListener { + void onLoginComplete(HttpCall httpCall, LoginResponse loginResponse); + + void onLoginError(HttpCall httpCall); } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/SiteEndpoints.java b/Clover/app/src/main/java/org/floens/chan/core/site/SiteEndpoints.java index 088cbc67..e184fe5f 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/SiteEndpoints.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/SiteEndpoints.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.core.site; import org.floens.chan.core.model.Board; @@ -22,5 +39,11 @@ public interface SiteEndpoints { String boards(); - String reply(Board board, Loadable thread); + String reply(Loadable thread); + + String delete(Post post); + + String report(Post post); + + String login(); } diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteRequest.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteRequest.java new file mode 100644 index 00000000..44187cce --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteRequest.java @@ -0,0 +1,37 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.site.http; + + +import org.floens.chan.core.model.Post; +import org.floens.chan.core.model.SavedReply; +import org.floens.chan.core.site.Site; + +public class DeleteRequest { + public final Site site; + public final Post post; + public final SavedReply savedReply; + public final boolean imageOnly; + + public DeleteRequest(Site site, Post post, SavedReply savedReply, boolean imageOnly) { + this.site = site; + this.post = post; + this.savedReply = savedReply; + this.imageOnly = imageOnly; + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteResponse.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteResponse.java new file mode 100644 index 00000000..1b7d6b74 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteResponse.java @@ -0,0 +1,24 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.site.http; + + +public class DeleteResponse { + public boolean deleted; + public String errorMessage; +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/http/HttpCall.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCall.java similarity index 62% rename from Clover/app/src/main/java/org/floens/chan/core/http/HttpCall.java rename to Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCall.java index 1656f6e7..94b47a90 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/http/HttpCall.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCall.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.core.http; +package org.floens.chan.core.site.http; import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.IOUtils; @@ -28,29 +28,24 @@ import okhttp3.Callback; import okhttp3.Request; import okhttp3.Response; +/** + * Http calls are an abstraction over a normal OkHttp call. + *

These HttpCalls are used for emulating <form> elements used for posting, reporting, deleting, etc. + *

Implement {@link #setup(Request.Builder)} and {@link #process(Response, String)}. + * {@code setup()} is called on the main thread, set up up the request builder here. {@code execute()} is + * called on a worker thread after the response was executed, do something with the response here. + */ public abstract class HttpCall implements Callback { private static final String TAG = "HttpCall"; private boolean successful = false; - private ReplyManager.HttpCallback callback; - - public void setSuccessful(boolean successful) { - this.successful = successful; - } + private HttpCallback callback; + private Exception exception; public abstract void setup(Request.Builder requestBuilder); public abstract void process(Response response, String result) throws IOException; - @SuppressWarnings("unchecked") - public void postUI(boolean successful) { - if (successful) { - callback.onHttpSuccess(this); - } else { - callback.onHttpFail(this); - } - } - @Override public void onResponse(Call call, Response response) { try { @@ -61,31 +56,60 @@ public abstract class HttpCall implements Callback { } else { onFailure(call, null); } - } catch (IOException e) { + } catch (Exception e) { + exception = e; Logger.e(TAG, "IOException processing response", e); } finally { IOUtils.closeQuietly(response.body()); } + if (successful) { + callSuccess(); + } else { + callFail(exception); + } + } + + @Override + public void onFailure(Call call, IOException e) { + callFail(e); + } + + public Exception getException() { + return exception; + } + + public boolean isSuccessful() { + return successful; + } + + private void callSuccess() { AndroidUtils.runOnUiThread(new Runnable() { + @SuppressWarnings("unchecked") @Override public void run() { - postUI(successful); + callback.onHttpSuccess(HttpCall.this); } }); } - @Override - public void onFailure(Call call, IOException e) { + private void callFail(final Exception e) { AndroidUtils.runOnUiThread(new Runnable() { + @SuppressWarnings("unchecked") @Override public void run() { - postUI(false); + callback.onHttpFail(HttpCall.this, e); } }); } - void setCallback(ReplyManager.HttpCallback callback) { + public void setCallback(HttpCallback callback) { this.callback = callback; } + + public interface HttpCallback { + void onHttpSuccess(T httpCall); + + void onHttpFail(T httpCall, Exception e); + } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCallManager.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCallManager.java new file mode 100644 index 00000000..998654a0 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCallManager.java @@ -0,0 +1,63 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.site.http; + + +import org.floens.chan.core.di.UserAgentProvider; + +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import okhttp3.OkHttpClient; +import okhttp3.Request; + +/** + * Manages the {@link HttpCall} executions. + */ +@Singleton +public class HttpCallManager { + private static final int TIMEOUT = 30000; + + private UserAgentProvider userAgentProvider; + private OkHttpClient client; + + @Inject + public HttpCallManager(UserAgentProvider userAgentProvider) { + this.userAgentProvider = userAgentProvider; + client = new OkHttpClient.Builder() + .connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS) + .readTimeout(TIMEOUT, TimeUnit.MILLISECONDS) + .writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS) + .build(); + } + + public void makeHttpCall(HttpCall httpCall, HttpCall.HttpCallback callback) { + httpCall.setCallback(callback); + + Request.Builder requestBuilder = new Request.Builder(); + + httpCall.setup(requestBuilder); + + requestBuilder.header("User-Agent", userAgentProvider.getUserAgent()); + Request request = requestBuilder.build(); + + client.newCall(request).enqueue(httpCall); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginRequest.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginRequest.java new file mode 100644 index 00000000..d35d3653 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginRequest.java @@ -0,0 +1,33 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.site.http; + + +import org.floens.chan.core.site.Site; + +public class LoginRequest { + public final Site site; + public final String user; + public final String pass; + + public LoginRequest(Site site, String user, String pass) { + this.site = site; + this.user = user; + this.pass = pass; + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginResponse.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginResponse.java new file mode 100644 index 00000000..68d16ab9 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginResponse.java @@ -0,0 +1,27 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.site.http; + + +public class LoginResponse { + public boolean success; + public String message; + + // TODO(multi-site) make this a cookie abstraction + public String token; +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/Reply.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/Reply.java similarity index 79% rename from Clover/app/src/main/java/org/floens/chan/core/model/Reply.java rename to Clover/app/src/main/java/org/floens/chan/core/site/http/Reply.java index 400b7f52..a87c37cb 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/Reply.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/Reply.java @@ -15,7 +15,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.core.model; +package org.floens.chan.core.site.http; + +import org.floens.chan.core.model.Loadable; import java.io.File; @@ -24,17 +26,19 @@ import java.io.File; */ public class Reply { /** - * Optional. Null when ReCaptcha v2 was used or a 4pass + * Optional. {@code null} when ReCaptcha v2 was used or a 4pass */ public String captchaChallenge; /** - * Optional. Null when a 4pass was used. + * Optional. {@code null} when a 4pass was used. */ public String captchaResponse; - public boolean usePass = false; - public String board; - public int resto; + + // TODO(multi-site) flip boolean + public boolean noVerification = false; + + public Loadable loadable; public String passId; public File file; diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/ReplyResponse.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/ReplyResponse.java new file mode 100644 index 00000000..77559db9 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/ReplyResponse.java @@ -0,0 +1,42 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.site.http; + +import org.floens.chan.core.site.Site; + +/** + * Generic response for {@link Site#post(Reply, Site.PostListener)} that the reply layout uses. + */ +public class ReplyResponse { + /** + * {@code true} if the post when through, {@code false} otherwise. + */ + public boolean posted; + + /** + * Error message used to show to the user if {@link #posted} is {@code false}. + *

Optional + */ + public String errorMessage; + + // TODO(multi-site) + public int threadNo; + public int postNo; + public String password; + public boolean probablyBanned; +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java index f4a9596e..f4348f4e 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java @@ -1,5 +1,23 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.core.site.sites.chan4; +import com.android.volley.RequestQueue; import com.android.volley.Response; import com.android.volley.VolleyError; @@ -11,7 +29,11 @@ import org.floens.chan.core.model.Post; import org.floens.chan.core.site.Boards; import org.floens.chan.core.site.Site; import org.floens.chan.core.site.SiteEndpoints; -import org.floens.chan.core.site.loaders.Chan4ReaderRequest; +import org.floens.chan.core.site.http.DeleteRequest; +import org.floens.chan.core.site.http.HttpCall; +import org.floens.chan.core.site.http.HttpCallManager; +import org.floens.chan.core.site.http.LoginRequest; +import org.floens.chan.core.site.http.Reply; import org.floens.chan.utils.Logger; import java.util.ArrayList; @@ -21,6 +43,8 @@ import java.util.Locale; import java.util.Map; import java.util.Random; +import javax.inject.Inject; + import static org.floens.chan.Chan.getGraph; public class Chan4 implements Site { @@ -28,6 +52,12 @@ public class Chan4 implements Site { private static final Random random = new Random(); + @Inject + HttpCallManager httpCallManager; + + @Inject + RequestQueue requestQueue; + private final SiteEndpoints endpoints = new SiteEndpoints() { @Override public String catalog(Board board) { @@ -69,12 +99,28 @@ public class Chan4 implements Site { } @Override - public String reply(Board board, Loadable thread) { - return "https://sys.4chan.org/" + board.code + "/post"; + public String reply(Loadable loadable) { + return "https://sys.4chan.org/" + loadable.getBoard().code + "/post"; + } + + @Override + public String delete(Post post) { + return "https://sys.4chan.org/" + post.board.code + "/imgboard.php"; + } + + @Override + public String report(Post post) { + return "https://sys.4chan.org/" + post.board.code + "/imgboard.php?mode=report&no=" + post.no; + } + + @Override + public String login() { + return "https://sys.4chan.org/auth"; } }; public Chan4() { + getGraph().inject(this); } /** @@ -100,6 +146,9 @@ public class Chan4 implements Site { case POST_DELETE: // yes, with the password saved when posting. return true; + case POST_REPORT: + // yes, with a custom url + return true; default: return false; } @@ -144,7 +193,7 @@ public class Chan4 implements Site { @Override public void boards(final BoardsListener listener) { - getGraph().getRequestQueue().add(new Chan4BoardsRequest(this, new Response.Listener>() { + requestQueue.add(new Chan4BoardsRequest(this, new Response.Listener>() { @Override public void onResponse(List response) { listener.onBoardsReceived(new Boards(response)); @@ -170,4 +219,49 @@ public class Chan4 implements Site { public ChanLoaderRequest loaderRequest(ChanLoaderRequestParams request) { return new ChanLoaderRequest(new Chan4ReaderRequest(request)); } + + @Override + public void post(Reply reply, final PostListener postListener) { + httpCallManager.makeHttpCall(new Chan4ReplyHttpCall(reply), new HttpCall.HttpCallback() { + @Override + public void onHttpSuccess(Chan4ReplyHttpCall httpPost) { + postListener.onPostComplete(httpPost, httpPost.replyResponse); + } + + @Override + public void onHttpFail(Chan4ReplyHttpCall httpPost, Exception e) { + postListener.onPostError(httpPost); + } + }); + } + + @Override + public void delete(DeleteRequest deleteRequest, final DeleteListener deleteListener) { + httpCallManager.makeHttpCall(new Chan4DeleteHttpCall(deleteRequest), new HttpCall.HttpCallback() { + @Override + public void onHttpSuccess(Chan4DeleteHttpCall httpPost) { + deleteListener.onDeleteComplete(httpPost, httpPost.deleteResponse); + } + + @Override + public void onHttpFail(Chan4DeleteHttpCall httpPost, Exception e) { + deleteListener.onDeleteError(httpPost); + } + }); + } + + @Override + public void login(LoginRequest loginRequest, final LoginListener loginListener) { + httpCallManager.makeHttpCall(new Chan4PassHttpCall(loginRequest), new HttpCall.HttpCallback() { + @Override + public void onHttpSuccess(Chan4PassHttpCall httpCall) { + loginListener.onLoginComplete(httpCall, httpCall.loginResponse); + } + + @Override + public void onHttpFail(Chan4PassHttpCall httpCall, Exception e) { + loginListener.onLoginError(httpCall); + } + }); + } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/http/DeleteHttpCall.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4DeleteHttpCall.java similarity index 63% rename from Clover/app/src/main/java/org/floens/chan/core/http/DeleteHttpCall.java rename to Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4DeleteHttpCall.java index 213eedd1..ae15969e 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/http/DeleteHttpCall.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4DeleteHttpCall.java @@ -15,10 +15,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.core.http; +package org.floens.chan.core.site.sites.chan4; -import org.floens.chan.chan.ChanUrls; -import org.floens.chan.core.model.SavedReply; +import org.floens.chan.core.site.http.DeleteRequest; +import org.floens.chan.core.site.http.DeleteResponse; +import org.floens.chan.core.site.http.HttpCall; import org.jsoup.Jsoup; import java.io.IOException; @@ -29,31 +30,27 @@ import okhttp3.FormBody; import okhttp3.Request; import okhttp3.Response; -public class DeleteHttpCall extends HttpCall { +public class Chan4DeleteHttpCall extends HttpCall { private static final Pattern ERROR_MESSAGE = Pattern.compile("\"errmsg\"[^>]*>(.*?)<\\/span"); - public boolean deleted; - public String errorMessage; + private final DeleteRequest deleteRequest; + public final DeleteResponse deleteResponse = new DeleteResponse(); - private final SavedReply reply; - private final boolean onlyImageDelete; - - public DeleteHttpCall(final SavedReply reply, boolean onlyImageDelete) { - this.reply = reply; - this.onlyImageDelete = onlyImageDelete; + public Chan4DeleteHttpCall(DeleteRequest deleteRequest) { + this.deleteRequest = deleteRequest; } @Override public void setup(Request.Builder requestBuilder) { FormBody.Builder formBuilder = new FormBody.Builder(); - formBuilder.add(Integer.toString(reply.no), "delete"); - if (onlyImageDelete) { + formBuilder.add(Integer.toString(deleteRequest.post.no), "delete"); + if (deleteRequest.imageOnly) { formBuilder.add("onlyimgdel", "on"); } formBuilder.add("mode", "usrdel"); - formBuilder.add("pwd", reply.password); + formBuilder.add("pwd", deleteRequest.savedReply.password); - requestBuilder.url(ChanUrls.getDeleteUrl(reply.board)); + requestBuilder.url(deleteRequest.site.endpoints().delete(deleteRequest.post)); requestBuilder.post(formBuilder.build()); } @@ -61,9 +58,9 @@ public class DeleteHttpCall extends HttpCall { public void process(Response response, String result) throws IOException { Matcher errorMessageMatcher = ERROR_MESSAGE.matcher(result); if (errorMessageMatcher.find()) { - errorMessage = Jsoup.parse(errorMessageMatcher.group(1)).body().ownText(); + deleteResponse.errorMessage = Jsoup.parse(errorMessageMatcher.group(1)).body().ownText(); } else { - deleted = true; + deleteResponse.deleted = true; } } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/http/PassHttpCall.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4PassHttpCall.java similarity index 73% rename from Clover/app/src/main/java/org/floens/chan/core/http/PassHttpCall.java rename to Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4PassHttpCall.java index 4d68f1a5..a2cffa19 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/http/PassHttpCall.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4PassHttpCall.java @@ -15,9 +15,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.core.http; +package org.floens.chan.core.site.sites.chan4; -import org.floens.chan.chan.ChanUrls; +import org.floens.chan.core.site.http.HttpCall; +import org.floens.chan.core.site.http.LoginRequest; +import org.floens.chan.core.site.http.LoginResponse; import java.io.IOException; import java.net.HttpCookie; @@ -27,17 +29,12 @@ import okhttp3.FormBody; import okhttp3.Request; import okhttp3.Response; -public class PassHttpCall extends HttpCall { - public boolean success; - public String message; - public String passId; +public class Chan4PassHttpCall extends HttpCall { + private final LoginRequest loginRequest; + public final LoginResponse loginResponse = new LoginResponse(); - private String token; - private String pin; - - public PassHttpCall(String token, String pin) { - this.token = token; - this.pin = pin; + public Chan4PassHttpCall(LoginRequest loginRequest) { + this.loginRequest = loginRequest; } @Override @@ -46,10 +43,10 @@ public class PassHttpCall extends HttpCall { formBuilder.add("act", "do_login"); - formBuilder.add("id", token); - formBuilder.add("pin", pin); + formBuilder.add("id", loginRequest.user); + formBuilder.add("pin", loginRequest.pass); - requestBuilder.url(ChanUrls.getPassUrl()); + requestBuilder.url(loginRequest.site.endpoints().login()); requestBuilder.post(formBuilder.build()); } @@ -59,6 +56,7 @@ public class PassHttpCall extends HttpCall { if (result.contains("Success! Your device is now authorized")) { authSuccess = true; } else { + String message; if (result.contains("Your Token must be exactly 10 characters")) { message = "Incorrect token"; } else if (result.contains("You have left one or more fields blank")) { @@ -68,6 +66,7 @@ public class PassHttpCall extends HttpCall { } else { message = "Unknown error"; } + loginResponse.message = message; } if (authSuccess) { @@ -86,11 +85,11 @@ public class PassHttpCall extends HttpCall { } if (passId != null) { - this.passId = passId; - message = "Success! Your device is now authorized."; - success = true; + loginResponse.token = passId; + loginResponse.message = "Success! Your device is now authorized."; + loginResponse.success = true; } else { - message = "Could not get pass id"; + loginResponse.message = "Could not get pass id"; } } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/loaders/Chan4ReaderRequest.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReaderRequest.java similarity index 99% rename from Clover/app/src/main/java/org/floens/chan/core/site/loaders/Chan4ReaderRequest.java rename to Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReaderRequest.java index ac3a6db9..c773dc2c 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/loaders/Chan4ReaderRequest.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReaderRequest.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.core.site.loaders; +package org.floens.chan.core.site.sites.chan4; import android.util.JsonReader; diff --git a/Clover/app/src/main/java/org/floens/chan/core/http/ReplyHttpCall.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReplyHttpCall.java similarity index 69% rename from Clover/app/src/main/java/org/floens/chan/core/http/ReplyHttpCall.java rename to Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReplyHttpCall.java index 5eca9c22..c9c85540 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/http/ReplyHttpCall.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReplyHttpCall.java @@ -15,12 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.core.http; +package org.floens.chan.core.site.sites.chan4; import android.text.TextUtils; -import org.floens.chan.chan.ChanUrls; -import org.floens.chan.core.model.Reply; +import org.floens.chan.core.site.Site; +import org.floens.chan.core.site.http.HttpCall; +import org.floens.chan.core.site.http.ReplyResponse; +import org.floens.chan.core.site.http.Reply; import org.jsoup.Jsoup; import java.io.IOException; @@ -34,40 +36,34 @@ import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; -public class ReplyHttpCall extends HttpCall { - private static final String TAG = "ReplyHttpCall"; +public class Chan4ReplyHttpCall extends HttpCall { + private static final String TAG = "Chan4ReplyHttpCall"; private static final Random RANDOM = new Random(); private static final Pattern THREAD_NO_PATTERN = Pattern.compile(""); private static final Pattern ERROR_MESSAGE = Pattern.compile("\"errmsg\"[^>]*>(.*?)<\\/span"); + private static final String PROBABLY_BANNED_TEXT = "banned"; - public boolean posted; - public String errorMessage; - public String text; - public String password; - public int threadNo = -1; - public int postNo = -1; - public boolean probablyBanned; + public final Reply reply; + public final ReplyResponse replyResponse = new ReplyResponse(); - private final Reply reply; - - public ReplyHttpCall(Reply reply) { + public Chan4ReplyHttpCall(Reply reply) { this.reply = reply; } @Override public void setup(Request.Builder requestBuilder) { - boolean thread = reply.resto >= 0; + boolean thread = reply.loadable.isThreadMode(); - password = Long.toHexString(RANDOM.nextLong()); + replyResponse.password = Long.toHexString(RANDOM.nextLong()); MultipartBody.Builder formBuilder = new MultipartBody.Builder(); formBuilder.setType(MultipartBody.FORM); formBuilder.addFormDataPart("mode", "regist"); - formBuilder.addFormDataPart("pwd", password); + formBuilder.addFormDataPart("pwd", replyResponse.password); if (thread) { - formBuilder.addFormDataPart("resto", String.valueOf(reply.resto)); + formBuilder.addFormDataPart("resto", String.valueOf(reply.loadable.no)); } formBuilder.addFormDataPart("name", reply.name); @@ -79,7 +75,7 @@ public class ReplyHttpCall extends HttpCall { formBuilder.addFormDataPart("com", reply.comment); - if (reply.captchaResponse != null) { + if (!reply.noVerification) { if (reply.captchaChallenge != null) { formBuilder.addFormDataPart("recaptcha_challenge_field", reply.captchaChallenge); formBuilder.addFormDataPart("recaptcha_response_field", reply.captchaResponse); @@ -98,33 +94,32 @@ public class ReplyHttpCall extends HttpCall { formBuilder.addFormDataPart("spoiler", "on"); } - requestBuilder.url(ChanUrls.getReplyUrl(reply.board)); + Site site = reply.loadable.getSite(); + requestBuilder.url(site.endpoints().reply(reply.loadable)); requestBuilder.post(formBuilder.build()); - if (reply.usePass) { + if (reply.noVerification) { requestBuilder.addHeader("Cookie", "pass_id=" + reply.passId); } } @Override public void process(Response response, String result) throws IOException { - text = result; - Matcher errorMessageMatcher = ERROR_MESSAGE.matcher(result); if (errorMessageMatcher.find()) { - errorMessage = Jsoup.parse(errorMessageMatcher.group(1)).body().text(); - probablyBanned = errorMessage.contains("banned"); + replyResponse.errorMessage = Jsoup.parse(errorMessageMatcher.group(1)).body().text(); + replyResponse.probablyBanned = replyResponse.errorMessage.contains(PROBABLY_BANNED_TEXT); } else { Matcher threadNoMatcher = THREAD_NO_PATTERN.matcher(result); if (threadNoMatcher.find()) { try { - threadNo = Integer.parseInt(threadNoMatcher.group(1)); - postNo = Integer.parseInt(threadNoMatcher.group(2)); + replyResponse.threadNo = Integer.parseInt(threadNoMatcher.group(1)); + replyResponse.postNo = Integer.parseInt(threadNoMatcher.group(2)); } catch (NumberFormatException ignored) { } - if (threadNo >= 0 && postNo >= 0) { - posted = true; + if (replyResponse.threadNo >= 0 && replyResponse.postNo >= 0) { + replyResponse.posted = true; } } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/loaders/PostParseCallable.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/PostParseCallable.java similarity index 98% rename from Clover/app/src/main/java/org/floens/chan/core/site/loaders/PostParseCallable.java rename to Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/PostParseCallable.java index d969b2f7..16807a3b 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/loaders/PostParseCallable.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/PostParseCallable.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.core.site.loaders; +package org.floens.chan.core.site.sites.chan4; import org.floens.chan.chan.ChanParser; import org.floens.chan.core.database.DatabaseSavedReplyManager; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java index 7ffbf7af..38db03c8 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java @@ -26,12 +26,16 @@ import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; -import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.controller.Controller; -import org.floens.chan.core.http.PassHttpCall; -import org.floens.chan.core.http.ReplyManager; +import org.floens.chan.core.manager.ReplyManager; import org.floens.chan.core.settings.ChanSettings; +import org.floens.chan.core.site.Site; +import org.floens.chan.core.site.Sites; +import org.floens.chan.core.site.http.HttpCall; +import org.floens.chan.core.site.http.HttpCallManager; +import org.floens.chan.core.site.http.LoginRequest; +import org.floens.chan.core.site.http.LoginResponse; import org.floens.chan.ui.view.CrossfadeView; import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.AnimationUtils; @@ -41,10 +45,13 @@ import javax.inject.Inject; import static org.floens.chan.Chan.getGraph; import static org.floens.chan.utils.AndroidUtils.getString; -public class PassSettingsController extends Controller implements View.OnClickListener, ReplyManager.HttpCallback { +public class PassSettingsController extends Controller implements View.OnClickListener, Site.LoginListener { @Inject ReplyManager replyManager; + @Inject + HttpCallManager httpCallManager; + private LinearLayout container; private CrossfadeView crossfadeView; private TextView errors; @@ -120,36 +127,37 @@ public class PassSettingsController extends Controller implements View.OnClickLi } @Override - public void onHttpSuccess(PassHttpCall httpPost) { - if (httpPost.success) { - authSuccess(httpPost); + public void onLoginComplete(HttpCall httpCall, LoginResponse loginResponse) { + if (loginResponse.success) { + authSuccess(loginResponse); } else { - authFail(httpPost); + authFail(loginResponse); } authAfter(); } @Override - public void onHttpFail(PassHttpCall httpPost) { - authFail(httpPost); + public void onLoginError(HttpCall httpCall) { + authFail(null); authAfter(); } - private void authSuccess(PassHttpCall httpPost) { + private void authSuccess(LoginResponse response) { crossfadeView.toggle(false, true); button.setText(R.string.setting_pass_logout); - ChanSettings.passId.set(httpPost.passId); - authenticated.setText(httpPost.message); + ChanSettings.passId.set(response.token); + authenticated.setText(response.message); ((PassSettingControllerListener) previousSiblingController).onPassEnabledChanged(true); } - private void authFail(PassHttpCall httpPost) { - if (httpPost.message == null) { - httpPost.message = getString(R.string.setting_pass_error); + private void authFail(LoginResponse response) { + String message = getString(R.string.setting_pass_error); + if (response != null && response.message != null) { + message = response.message; } - showError(httpPost.message); + showError(message); button.setText(R.string.setting_pass_login); } @@ -170,7 +178,9 @@ public class PassSettingsController extends Controller implements View.OnClickLi ChanSettings.passToken.set(inputToken.getText().toString()); ChanSettings.passPin.set(inputPin.getText().toString()); - replyManager.makeHttpCall(new PassHttpCall(ChanSettings.passToken.get(), ChanSettings.passPin.get()), this); + // TODO(multi-site) some selector of some sorts + Site site = Sites.defaultSite(); + site.login(new LoginRequest(site, ChanSettings.passToken.get(), ChanSettings.passPin.get()), this); } private void showError(String error) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ReportController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ReportController.java index c3ab73e7..44db40e5 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ReportController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ReportController.java @@ -20,6 +20,7 @@ package org.floens.chan.ui.controller; import android.annotation.SuppressLint; import android.content.Context; import android.webkit.CookieManager; +import android.webkit.WebSettings; import android.webkit.WebView; import org.floens.chan.R; @@ -27,8 +28,12 @@ import org.floens.chan.chan.ChanUrls; import org.floens.chan.controller.Controller; import org.floens.chan.core.model.Post; import org.floens.chan.core.settings.ChanSettings; +import org.floens.chan.core.site.Site; +import org.floens.chan.core.site.Sites; import org.floens.chan.ui.helper.PostHelper; +import okhttp3.HttpUrl; + public class ReportController extends Controller { private Post post; @@ -44,18 +49,26 @@ public class ReportController extends Controller { super.onCreate(); navigationItem.title = context.getString(R.string.report_screen, PostHelper.getTitle(post, null)); - CookieManager cookieManager = CookieManager.getInstance(); - cookieManager.removeAllCookie(); - if (ChanSettings.passLoggedIn()) { - for (String cookie : ChanUrls.getReportCookies(ChanSettings.passId.get())) { - cookieManager.setCookie(ChanUrls.getReportDomain(), cookie); + Site site = post.board.getSite(); + String url = site.endpoints().report(post); + + if (site == Sites.CHAN4) { + CookieManager cookieManager = CookieManager.getInstance(); + cookieManager.removeAllCookie(); + if (ChanSettings.passLoggedIn()) { + HttpUrl parsed = HttpUrl.parse(url); + String domain = parsed.scheme() + "://" + parsed.host() + "/"; + for (String cookie : ChanUrls.getReportCookies(ChanSettings.passId.get())) { + cookieManager.setCookie(domain, cookie); + } } } WebView webView = new WebView(context); - webView.getSettings().setJavaScriptEnabled(true); - webView.getSettings().setDomStorageEnabled(true); - webView.loadUrl(ChanUrls.getReportUrl(post.boardId, post.no)); + WebSettings settings = webView.getSettings(); + settings.setJavaScriptEnabled(true); + settings.setDomStorageEnabled(true); + webView.loadUrl(url); view = webView; } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/helper/ImagePickDelegate.java b/Clover/app/src/main/java/org/floens/chan/ui/helper/ImagePickDelegate.java index b266e78c..1cac1c03 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/helper/ImagePickDelegate.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/helper/ImagePickDelegate.java @@ -21,12 +21,10 @@ import android.app.Activity; import android.content.Intent; import android.database.Cursor; import android.net.Uri; -import android.os.Build; import android.os.ParcelFileDescriptor; import android.provider.OpenableColumns; -import org.floens.chan.Chan; -import org.floens.chan.core.http.ReplyManager; +import org.floens.chan.core.manager.ReplyManager; import org.floens.chan.utils.IOUtils; import org.floens.chan.utils.Logger; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java index f096263c..b9d84173 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java @@ -37,7 +37,7 @@ import android.widget.Toast; import org.floens.chan.R; import org.floens.chan.core.model.ChanThread; import org.floens.chan.core.model.Loadable; -import org.floens.chan.core.model.Reply; +import org.floens.chan.core.site.http.Reply; import org.floens.chan.core.presenter.ReplyPresenter; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.ui.activity.StartActivity;