From 14f97d7ce3d3e78dbd2da7b0e201c10ca43c2e50 Mon Sep 17 00:00:00 2001 From: Thodoris1999 Date: Thu, 26 Jul 2018 12:51:17 +0300 Subject: [PATCH] make TopicViewModel an interface for loads (not finished) --- app/build.gradle | 2 +- .../mthmmy/activities/topic/DeleteTask.java | 62 ++ .../mthmmy/activities/topic/EditTask.java | 77 +++ .../activities/topic/GetQuoteTextTask.java | 4 - .../topic/PrepareForEditResult.java | 51 ++ .../activities/topic/PrepareForEditTask.java | 79 +++ .../activities/topic/PrepareForReply.java | 96 +++ .../topic/PrepareForReplyResult.java | 34 + .../mthmmy/activities/topic/ReplyTask.java | 74 ++ .../activities/topic/TopicActivity.java | 650 +++++------------- .../mthmmy/activities/topic/TopicAdapter.java | 125 +--- .../mthmmy/activities/topic/TopicTask.java | 91 ++- .../mthmmy/viewmodel/TopicViewModel.java | 221 +++++- 13 files changed, 939 insertions(+), 627 deletions(-) create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/topic/DeleteTask.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/topic/EditTask.java delete mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/topic/GetQuoteTextTask.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForEditResult.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForEditTask.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForReply.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForReplyResult.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/topic/ReplyTask.java diff --git a/app/build.gradle b/app/build.gradle index 4e9bb2f4..dd59ec42 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,7 +42,7 @@ dependencies { implementation 'com.android.support:cardview-v7:27.1.1' implementation 'com.android.support:recyclerview-v7:27.1.1' implementation 'com.google.firebase:firebase-core:16.0.1' - implementation 'com.google.firebase:firebase-messaging:17.0.0' + implementation 'com.google.firebase:firebase-messaging:17.1.0' implementation 'com.crashlytics.sdk.android:crashlytics:2.9.4' implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.squareup.picasso:picasso:2.5.2' diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/DeleteTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/DeleteTask.java new file mode 100644 index 00000000..7c74d17a --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/DeleteTask.java @@ -0,0 +1,62 @@ +package gr.thmmy.mthmmy.activities.topic; + +import android.os.AsyncTask; + +import java.io.IOException; + +import gr.thmmy.mthmmy.base.BaseApplication; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import timber.log.Timber; + +import static gr.thmmy.mthmmy.activities.topic.Posting.replyStatus; + +public class DeleteTask extends AsyncTask { + private DeleteTaskCallbacks listener; + + public DeleteTask(DeleteTaskCallbacks listener) { + this.listener = listener; + } + + @Override + protected void onPreExecute() { + listener.onDeleteTaskStarted(); + } + + @Override + protected Boolean doInBackground(String... args) { + Request delete = new Request.Builder() + .url(args[0]) + .header("User-Agent", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36") + .build(); + + try { + OkHttpClient client = BaseApplication.getInstance().getClient(); + client.newCall(delete).execute(); + Response response = client.newCall(delete).execute(); + //Response response = client.newCall(delete).execute(); + switch (replyStatus(response)) { + case SUCCESSFUL: + return true; + default: + Timber.e("Something went wrong. Request string: %s", delete.toString()); + return true; + } + } catch (IOException e) { + Timber.e(e, "Delete failed."); + return false; + } + } + + @Override + protected void onPostExecute(Boolean result) { + listener.onDeleteTaskFinished(result); + } + + public interface DeleteTaskCallbacks { + void onDeleteTaskStarted(); + void onDeleteTaskFinished(boolean result); + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/EditTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/EditTask.java new file mode 100644 index 00000000..4d4802b3 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/EditTask.java @@ -0,0 +1,77 @@ +package gr.thmmy.mthmmy.activities.topic; + +import android.os.AsyncTask; + +import java.io.IOException; + +import gr.thmmy.mthmmy.base.BaseApplication; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import timber.log.Timber; + +import static gr.thmmy.mthmmy.activities.topic.Posting.replyStatus; + +public class EditTask extends AsyncTask { + private EditTaskCallbacks listener; + private int position; + + public EditTask(EditTaskCallbacks listener, int position) { + this.listener = listener; + this.position = position; + } + + @Override + protected void onPreExecute() { + listener.onEditTaskStarted(); + } + + @Override + protected Boolean doInBackground(String... strings) { + RequestBody postBody = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("message", strings[1]) + .addFormDataPart("num_replies", strings[2]) + .addFormDataPart("seqnum", strings[3]) + .addFormDataPart("sc", strings[4]) + .addFormDataPart("subject", strings[5]) + .addFormDataPart("topic", strings[6]) + .build(); + Request post = new Request.Builder() + .url(strings[0]) + .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36") + .post(postBody) + .build(); + + try { + OkHttpClient client = BaseApplication.getInstance().getClient(); + client.newCall(post).execute(); + Response response = client.newCall(post).execute(); + switch (replyStatus(response)) { + case SUCCESSFUL: + return true; + case NEW_REPLY_WHILE_POSTING: + //TODO this... + return true; + default: + Timber.e("Malformed post. Request string: %s", post.toString()); + return true; + } + } catch (IOException e) { + Timber.e(e, "Edit failed."); + return false; + } + } + + @Override + protected void onPostExecute(Boolean result) { + listener.onEditTaskFinished(result, position); + } + + public interface EditTaskCallbacks { + void onEditTaskStarted(); + void onEditTaskFinished(boolean result, int position); + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/GetQuoteTextTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/GetQuoteTextTask.java deleted file mode 100644 index 0b29087f..00000000 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/GetQuoteTextTask.java +++ /dev/null @@ -1,4 +0,0 @@ -package gr.thmmy.mthmmy.activities.topic; - -public class GetQuoteTextTask { -} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForEditResult.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForEditResult.java new file mode 100644 index 00000000..0c3f9dec --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForEditResult.java @@ -0,0 +1,51 @@ +package gr.thmmy.mthmmy.activities.topic; + +public class PrepareForEditResult { + private final String postText, commitEditUrl, numReplies, seqnum, sc, topic; + private int position; + private boolean successful; + + public PrepareForEditResult(String postText, String commitEditUrl, String numReplies, String seqnum, + String sc, String topic, int position, boolean successful) { + this.postText = postText; + this.commitEditUrl = commitEditUrl; + this.numReplies = numReplies; + this.seqnum = seqnum; + this.sc = sc; + this.topic = topic; + this.position = position; + this.successful = successful; + } + + public String getPostText() { + return postText; + } + + public String getCommitEditUrl() { + return commitEditUrl; + } + + public String getNumReplies() { + return numReplies; + } + + public String getSeqnum() { + return seqnum; + } + + public String getSc() { + return sc; + } + + public String getTopic() { + return topic; + } + + public int getPosition() { + return position; + } + + public boolean isSuccessful() { + return successful; + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForEditTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForEditTask.java new file mode 100644 index 00000000..f1933cce --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForEditTask.java @@ -0,0 +1,79 @@ +package gr.thmmy.mthmmy.activities.topic; + +import android.os.AsyncTask; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Selector; + +import java.io.IOException; + +import gr.thmmy.mthmmy.base.BaseApplication; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import timber.log.Timber; + +public class PrepareForEditTask extends AsyncTask { + private int position; + private String replyPageUrl; + private PrepareForEditCallbacks listener; + private OnPrepareEditFinished finishListener; + + public PrepareForEditTask(PrepareForEditCallbacks listener, OnPrepareEditFinished finishListener, int position, String replyPageUrl) { + this.listener = listener; + this.finishListener = finishListener; + this.position = position; + this.replyPageUrl = replyPageUrl; + } + + @Override + protected void onPreExecute() { + listener.onPrepareEditStarted(); + } + + @Override + protected PrepareForEditResult doInBackground(String... strings) { + Document document; + String url = strings[0]; + Request request = new Request.Builder() + .url(url + ";wap2") + .build(); + + try { + String postText, commitEditURL, numReplies, seqnum, sc, topic; + OkHttpClient client = BaseApplication.getInstance().getClient(); + Response response = client.newCall(request).execute(); + document = Jsoup.parse(response.body().string()); + + Element message = document.select("textarea").first(); + postText = message.text(); + + commitEditURL = document.select("form").first().attr("action"); + numReplies = replyPageUrl.substring(replyPageUrl.indexOf("num_replies=") + 12); + seqnum = document.select("input[name=seqnum]").first().attr("value"); + sc = document.select("input[name=sc]").first().attr("value"); + topic = document.select("input[name=topic]").first().attr("value"); + + return new PrepareForEditResult(postText, commitEditURL, numReplies, seqnum, sc, topic, position, true); + } catch (IOException | Selector.SelectorParseException e) { + Timber.e(e, "Prepare failed."); + return new PrepareForEditResult(null, null, null, null, null, null, position, false); + } + } + + @Override + protected void onPostExecute(PrepareForEditResult result) { + finishListener.onPrepareEditFinished(result, position); + } + + public interface PrepareForEditCallbacks { + void onPrepareEditStarted(); + void onPrepareEditCancelled(); + } + + public interface OnPrepareEditFinished { + void onPrepareEditFinished(PrepareForEditResult result, int position); + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForReply.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForReply.java new file mode 100644 index 00000000..752692f5 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForReply.java @@ -0,0 +1,96 @@ +package gr.thmmy.mthmmy.activities.topic; + +import android.os.AsyncTask; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Selector; + +import java.io.IOException; +import java.util.ArrayList; + +import gr.thmmy.mthmmy.base.BaseApplication; +import gr.thmmy.mthmmy.model.Post; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import timber.log.Timber; + +public class PrepareForReply extends AsyncTask { + private PrepareForReplyCallbacks listener; + private OnPrepareForReplyFinished finishListener; + private String replyPageUrl; + private ArrayList postsList; + + public PrepareForReply(PrepareForReplyCallbacks listener, OnPrepareForReplyFinished finishListener, + String replyPageUrl, ArrayList postsList) { + this.listener = listener; + this.finishListener = finishListener; + this.replyPageUrl = replyPageUrl; + this.postsList = postsList; + } + + @Override + protected void onPreExecute() { + listener.onPrepareForReplyStarted(); + } + + @Override + protected PrepareForReplyResult doInBackground(Integer... quoteList) { + String numReplies = null; + String seqnum = null; + String sc = null; + String topic = null; + StringBuilder buildedQuotes = new StringBuilder(); + + Document document; + Request request = new Request.Builder() + .url(replyPageUrl + ";wap2") + .build(); + + OkHttpClient client = BaseApplication.getInstance().getClient(); + try { + Response response = client.newCall(request).execute(); + document = Jsoup.parse(response.body().string()); + + numReplies = replyPageUrl.substring(replyPageUrl.indexOf("num_replies=") + 12); + seqnum = document.select("input[name=seqnum]").first().attr("value"); + sc = document.select("input[name=sc]").first().attr("value"); + topic = document.select("input[name=topic]").first().attr("value"); + } catch (IOException | Selector.SelectorParseException e) { + Timber.e(e, "Prepare failed."); + } + + for (Integer quotePosition : quoteList) { + request = new Request.Builder() + .url("https://www.thmmy.gr/smf/index.php?action=quotefast;quote=" + + postsList.get(quotePosition).getPostIndex() + + ";" + "sesc=" + sc + ";xml") + .build(); + + try { + Response response = client.newCall(request).execute(); + String body = response.body().string(); + buildedQuotes.append(body.substring(body.indexOf("") + 7, body.indexOf(""))); + buildedQuotes.append("\n\n"); + } catch (IOException | Selector.SelectorParseException e) { + Timber.e(e, "Quote building failed."); + } + } + return new PrepareForReplyResult(numReplies, seqnum, sc, topic, buildedQuotes.toString()); + } + + @Override + protected void onPostExecute(PrepareForReplyResult result) { + finishListener.onPrepareForReplyFinished(result); + } + + public interface PrepareForReplyCallbacks { + void onPrepareForReplyStarted(); + void onPrepareForReplyCancelled(); + } + + public interface OnPrepareForReplyFinished { + void onPrepareForReplyFinished(PrepareForReplyResult result); + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForReplyResult.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForReplyResult.java new file mode 100644 index 00000000..24364d49 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/PrepareForReplyResult.java @@ -0,0 +1,34 @@ +package gr.thmmy.mthmmy.activities.topic; + +public class PrepareForReplyResult { + private final String numReplies, seqnum, sc, topic, buildedQuotes; + + + public PrepareForReplyResult(String numReplies, String seqnum, String sc, String topic, String buildedQuotes) { + this.numReplies = numReplies; + this.seqnum = seqnum; + this.sc = sc; + this.topic = topic; + this.buildedQuotes = buildedQuotes; + } + + public String getNumReplies() { + return numReplies; + } + + public String getSeqnum() { + return seqnum; + } + + public String getSc() { + return sc; + } + + public String getTopic() { + return topic; + } + + public String getBuildedQuotes() { + return buildedQuotes; + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/ReplyTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/ReplyTask.java new file mode 100644 index 00000000..5ec23f2f --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/ReplyTask.java @@ -0,0 +1,74 @@ +package gr.thmmy.mthmmy.activities.topic; + +import android.os.AsyncTask; + +import java.io.IOException; + +import gr.thmmy.mthmmy.base.BaseApplication; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import timber.log.Timber; + +import static gr.thmmy.mthmmy.activities.topic.Posting.replyStatus; + +public class ReplyTask extends AsyncTask { + private OnReplyTaskFinished listener; + private boolean includeAppSignature; + + public ReplyTask(OnReplyTaskFinished listener, boolean includeAppSignature) { + this.listener = listener; + this.includeAppSignature = includeAppSignature; + } + + @Override + protected Boolean doInBackground(String... args) { + final String sentFrommTHMMY = includeAppSignature + ? "\n[right][size=7pt][i]sent from [url=https://play.google.com/store/apps/details?id=gr.thmmy.mthmmy]mTHMMY [/url][/i][/size][/right]" + : ""; + RequestBody postBody = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("message", args[1] + sentFrommTHMMY) + .addFormDataPart("num_replies", args[2]) + .addFormDataPart("seqnum", args[3]) + .addFormDataPart("sc", args[4]) + .addFormDataPart("subject", args[0]) + .addFormDataPart("topic", args[5]) + .build(); + Request post = new Request.Builder() + .url("https://www.thmmy.gr/smf/index.php?action=post2") + .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36") + .post(postBody) + .build(); + + try { + OkHttpClient client = BaseApplication.getInstance().getClient(); + client.newCall(post).execute(); + Response response = client.newCall(post).execute(); + switch (replyStatus(response)) { + case SUCCESSFUL: + return true; + case NEW_REPLY_WHILE_POSTING: + //TODO this... + return true; + default: + Timber.e("Malformed post. Request string: %s", post.toString()); + return true; + } + } catch (IOException e) { + Timber.e(e, "Post failed."); + return false; + } + } + + @Override + protected void onPostExecute(Boolean result) { + listener.onReplyTaskFinished(result); + } + + public interface OnReplyTaskFinished { + void onReplyTaskFinished(boolean result); + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java index a1ff2822..f4cf139b 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java @@ -2,28 +2,19 @@ package gr.thmmy.mthmmy.activities.topic; import android.annotation.SuppressLint; import android.app.NotificationManager; -import android.arch.lifecycle.ViewModelProvider; import android.arch.lifecycle.ViewModelProviders; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.graphics.Rect; import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AlertDialog; -import android.support.v7.preference.PreferenceManager; import android.support.v7.widget.RecyclerView; -import android.text.Html; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.method.LinkMovementMethod; -import android.text.style.ClickableSpan; -import android.text.style.URLSpan; -import android.util.SparseArray; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -36,42 +27,20 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Selector; - -import java.io.IOException; import java.util.ArrayList; import java.util.Objects; import gr.thmmy.mthmmy.R; -import gr.thmmy.mthmmy.activities.board.BoardActivity; -import gr.thmmy.mthmmy.activities.profile.ProfileActivity; -import gr.thmmy.mthmmy.activities.settings.SettingsActivity; import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.model.Bookmark; import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.utils.CustomLinearLayoutManager; import gr.thmmy.mthmmy.utils.HTMLUtils; -import gr.thmmy.mthmmy.utils.parsing.ParseException; -import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import gr.thmmy.mthmmy.viewmodel.TopicViewModel; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; -import okhttp3.MultipartBody; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; import timber.log.Timber; -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_TITLE; -import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_URL; -import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_THUMBNAIL_URL; -import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_URL; -import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME; -import static gr.thmmy.mthmmy.activities.topic.Posting.replyStatus; import static gr.thmmy.mthmmy.services.NotificationService.NEW_POST_TAG; /** @@ -81,7 +50,9 @@ import static gr.thmmy.mthmmy.services.NotificationService.NEW_POST_TAG; * key {@link #BUNDLE_TOPIC_TITLE} for faster title rendering. */ @SuppressWarnings("unchecked") -public class TopicActivity extends BaseActivity { +public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskObserver, + DeleteTask.DeleteTaskCallbacks, ReplyTask.OnReplyTaskFinished, PrepareForEditTask.PrepareForEditCallbacks, + EditTask.EditTaskCallbacks, PrepareForReply.PrepareForReplyCallbacks { //Activity's variables /** * The key to use when putting topic's url String to {@link TopicActivity}'s Bundle. @@ -91,76 +62,18 @@ public class TopicActivity extends BaseActivity { * The key to use when putting topic's title String to {@link TopicActivity}'s Bundle. */ public static final String BUNDLE_TOPIC_TITLE = "TOPIC_TITLE"; - private static TopicTask topicTask; private MaterialProgressBar progressBar; private TextView toolbarTitle; - /** - * Holds this topic's base url. For example a topic with url similar to - * "https://www.thmmy.gr/smf/index.php?topic=1.15;topicseen" or - * "https://www.thmmy.gr/smf/index.php?topic=1.msg1#msg1" - * has the base url "https://www.thmmy.gr/smf/index.php?topic=1" - */ - private static String base_url = ""; - /** - * Holds this topic's title. At first this gets the value of the topic title that came with - * bundle and is rendered in the toolbar while parsing this topic. Later, after topic's parsing - * is done, it gets the value of {@link #parsedTitle} if bundle title and parsed title differ. - */ - private String topicTitle; - /** - * Holds this topic's title as parsed from the html source. If this (parsed) title is different - * than the one that came with activity's bundle then the parsed title is preferred over the - * bundle one and gets rendered in the toolbar. - */ - private String parsedTitle; - private String topicPageUrl; private RecyclerView recyclerView; - /** - * Holds the url of this page - */ - private String loadedPageUrl = ""; - /** - * Holds the topicId of this page - */ - private int loadedPageTopicId = -1; - /** - * Becomes true after user has posted in this topic and the page is being reloaded and false - * when topic's reloading is done - */ - private boolean reloadingPage = false; //Posts related private TopicAdapter topicAdapter; /** * Holds a list of this topic's posts */ private ArrayList postsList; - /** - * Holds the index of the post that has focus - */ - - /** - * Holds the position in the {@link #postsList} of the post with focus - */ - private static int postFocusPosition = 0; //Reply related private FloatingActionButton replyFAB; - /** - * Holds this topic's reply url - */ - private String replyPageUrl = null; //Topic's pages related - /** - * Holds current page's index (starting from 1, not 0) - */ - private int thisPage = 1; - /** - * Holds this topic's number of pages - */ - private int numberOfPages = 1; - /** - * Holds a list of this topic's pages urls - */ - private final SparseArray pagesUrls = new SparseArray<>(); //Page select related /** * Used for handling bottom navigation bar's buttons long click user interactions @@ -197,21 +110,22 @@ public class TopicActivity extends BaseActivity { private ImageButton lastPage; private TopicViewModel viewModel; - //Topic's info related - private SpannableStringBuilder topicTreeAndMods = new SpannableStringBuilder("Loading..."), - topicViewers = new SpannableStringBuilder("Loading..."); - - boolean includeAppSignaturePreference = true; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_topic); + viewModel = ViewModelProviders.of(this).get(TopicViewModel.class); + viewModel.setTopicTaskObserver(this); + viewModel.setDeleteTaskCallbacks(this); + viewModel.setReplyFinishListener(this); + viewModel.setPrepareForEditCallbacks(this); + viewModel.setEditTaskCallbacks(this); + viewModel.setPrepareForReplyCallbacks(this); + Bundle extras = getIntent().getExtras(); - topicTitle = extras.getString(BUNDLE_TOPIC_TITLE); - String maybeTopicTitle = topicTitle; - topicPageUrl = extras.getString(BUNDLE_TOPIC_URL); + String topicTitle = extras.getString(BUNDLE_TOPIC_TITLE); + String topicPageUrl = extras.getString(BUNDLE_TOPIC_URL); ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory( Uri.parse(topicPageUrl)); if (!target.is(ThmmyPage.PageCategory.TOPIC)) { @@ -222,10 +136,6 @@ public class TopicActivity extends BaseActivity { topicPageUrl = ThmmyPage.sanitizeTopicUrl(topicPageUrl); - if (sessionManager.isLoggedIn()) { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - includeAppSignaturePreference = sharedPrefs.getBoolean(SettingsActivity.APP_SIGNATURE_ENABLE_KEY, true); - } thisPageBookmark = new Bookmark(topicTitle, ThmmyPage.getTopicId(topicPageUrl), true); @@ -251,20 +161,11 @@ public class TopicActivity extends BaseActivity { recyclerView = findViewById(R.id.topic_recycler_view); recyclerView.setHasFixedSize(true); - recyclerView.setOnTouchListener( - new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - v.performClick(); - return topicTask != null && topicTask.getStatus() == AsyncTask.Status.RUNNING; - } - } - ); //LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext()); CustomLinearLayoutManager layoutManager = new CustomLinearLayoutManager( - getApplicationContext(), loadedPageUrl); + getApplicationContext(), topicPageUrl); recyclerView.setLayoutManager(layoutManager); - topicAdapter = new TopicAdapter(this, postsList, viewModel.getTopicTaskResultMutableLiveData().getValue().getBaseUrl(), topicTask); + topicAdapter = new TopicAdapter(this, postsList); recyclerView.setAdapter(topicAdapter); replyFAB = findViewById(R.id.topic_fab); @@ -272,13 +173,9 @@ public class TopicActivity extends BaseActivity { bottomNavBar = findViewById(R.id.bottom_navigation_bar); if (!sessionManager.isLoggedIn()) replyFAB.hide(); else { - replyFAB.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (sessionManager.isLoggedIn()) { - PrepareForReply prepareForReply = new PrepareForReply(); - prepareForReply.execute(topicAdapter.getToQuoteList()); - } + replyFAB.setOnClickListener(view -> { + if (sessionManager.isLoggedIn()) { + viewModel.prepareForReply(postsList, topicAdapter.getToQuoteList()); } }); } @@ -294,14 +191,8 @@ public class TopicActivity extends BaseActivity { initDecrementButton(previousPage, SMALL_STEP); initIncrementButton(nextPage, SMALL_STEP); initIncrementButton(lastPage, LARGE_STEP); - paginationEnabled(false); - - //Gets posts - //topicTask = new TopicTask(); - //topicTask.execute(topicPageUrl); //Attempt data parsing - viewModel = ViewModelProviders.of(this).get(TopicViewModel.class); - viewModel.getTopicTaskResultMutableLiveData().observe(this, topicTaskResult -> { + viewModel.getTopicTaskResult().observe(this, topicTaskResult -> { if (topicTaskResult == null) { hideControls(); } else { @@ -320,19 +211,19 @@ public class TopicActivity extends BaseActivity { } postsList.addAll(topicTaskResult.getNewPostsList()); topicAdapter.notifyItemRangeInserted(0, postsList.size()); - topicAdapter.prepareForDelete(new TopicActivity.DeleteTask()); - topicAdapter.prepareForPrepareForEdit(new TopicActivity.PrepareForEdit()); - pageIndicator.setText(String.valueOf(thisPage) + "/" + String.valueOf(numberOfPages)); + pageIndicator.setText(String.valueOf(topicTaskResult.getCurrentPageIndex()) + "/" + + String.valueOf(topicTaskResult.getPageCount())); pageRequestValue = topicTaskResult.getCurrentPageIndex(); if (topicTaskResult.getCurrentPageIndex() == topicTaskResult.getPageCount()) { NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager != null) - notificationManager.cancel(NEW_POST_TAG, loadedPageTopicId); + notificationManager.cancel(NEW_POST_TAG, topicTaskResult.getLoadedPageTopicId()); } showControls(); + break; case NETWORK_ERROR: Toast.makeText(getBaseContext(), "Network Error", Toast.LENGTH_SHORT).show(); break; @@ -355,23 +246,41 @@ public class TopicActivity extends BaseActivity { } }); - viewModel.initialLoad(topicPageUrl); + viewModel.getPrepareForReplyResult().observe(this, prepareForReplyResult -> { + if (prepareForReplyResult != null) { + //prepare for a reply + viewModel.setWritingReply(true); + postsList.add(Post.newQuickReply()); + topicAdapter.notifyItemInserted(postsList.size()); + recyclerView.scrollToPosition(postsList.size() - 1); + showControls(); + } + }); + viewModel.getPrepareForEditResult().observe(this, result -> { + if (result != null && result.isSuccessful()) { + viewModel.setEditingPost(true); + postsList.get(result.getPosition()).setPostType(Post.TYPE_EDIT); + topicAdapter.notifyItemChanged(result.getPosition()); + recyclerView.scrollToPosition(result.getPosition()); + showControls(); + } + }); + viewModel.initialLoad(topicPageUrl); } public void hideControls() { progressBar.setVisibility(ProgressBar.VISIBLE); - paginationEnabled(false); if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(false); } public void showControls() { - progressBar.setVisibility(ProgressBar.INVISIBLE); - if (replyPageUrl == null) { + progressBar.setVisibility(ProgressBar.GONE); + if (viewModel.getTopicTaskResult().getValue().getReplyPageUrl() == null) { replyFAB.hide(); - topicAdapter.resetTopic(viewModel.getTopicTaskResultMutableLiveData().getValue().getBaseUrl(), new TopicTask(), false); - } else topicAdapter.resetTopic(viewModel.getTopicTaskResultMutableLiveData().getValue().getBaseUrl(), new TopicTask(), true); - paginationEnabled(true); + topicAdapter.resetTopic(false); + } else + topicAdapter.resetTopic(true); if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true); } @@ -397,12 +306,12 @@ public class TopicActivity extends BaseActivity { LinearLayout infoDialog = (LinearLayout) inflater.inflate(R.layout.dialog_topic_info , null); TextView treeAndMods = infoDialog.findViewById(R.id.topic_tree_and_mods); - //treeAndMods.setText(new SpannableStringBuilder("Loading...")); + treeAndMods.setText(new SpannableStringBuilder("Loading...")); treeAndMods.setMovementMethod(LinkMovementMethod.getInstance()); TextView usersViewing = infoDialog.findViewById(R.id.users_viewing); - //usersViewing.setText(new SpannableStringBuilder("Loading...")); + usersViewing.setText(new SpannableStringBuilder("Loading...")); usersViewing.setMovementMethod(LinkMovementMethod.getInstance()); - viewModel.getTopicTaskResultMutableLiveData().observe(this, topicTaskResult -> { + viewModel.getTopicTaskResult().observe(this, topicTaskResult -> { if (topicTaskResult == null) { usersViewing.setText(new SpannableStringBuilder("Loading...")); treeAndMods.setText(new SpannableStringBuilder("Loading...")); @@ -421,7 +330,7 @@ public class TopicActivity extends BaseActivity { case R.id.menu_share: Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND); sendIntent.setType("text/plain"); - sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, topicPageUrl); + sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, viewModel.getTopicUrl()); startActivity(Intent.createChooser(sendIntent, "Share via")); return true; //invalidateOptionsMenu(); default: @@ -452,19 +361,13 @@ public class TopicActivity extends BaseActivity { super.onResume(); refreshTopicBookmark(); drawer.setSelection(-1); - - if (sessionManager.isLoggedIn()) { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - includeAppSignaturePreference = sharedPrefs.getBoolean(SettingsActivity.APP_SIGNATURE_ENABLE_KEY, true); - } } @Override protected void onDestroy() { super.onDestroy(); recyclerView.setAdapter(null); - if (topicTask != null && topicTask.getStatus() != AsyncTask.Status.RUNNING) - topicTask.cancel(true); + viewModel.stopLoading(); } //--------------------------------------BOTTOM NAV BAR METHODS---------------------------------- @@ -527,26 +430,22 @@ public class TopicActivity extends BaseActivity { @SuppressLint("ClickableViewAccessibility") private void initIncrementButton(ImageButton increment, final int step) { // Increment once for a click - increment.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - if (!autoIncrement && step == LARGE_STEP) { - changePage(numberOfPages - 1); - } else if (!autoIncrement) { - incrementPageRequestValue(step); - changePage(pageRequestValue - 1); - } + increment.setOnClickListener(v -> { + if (!autoIncrement && step == LARGE_STEP) { + viewModel.changePage(viewModel.getTopicTaskResult().getValue().getPageCount() - 1); + } else if (!autoIncrement) { + incrementPageRequestValue(step); + viewModel.changePage(pageRequestValue - 1); } }); // Auto increment for a long click increment.setOnLongClickListener( - new View.OnLongClickListener() { - public boolean onLongClick(View arg0) { - paginationDisable(arg0); - autoIncrement = true; - repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY); - return false; - } + arg0 -> { + paginationDisable(arg0); + autoIncrement = true; + repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY); + return false; } ); @@ -560,11 +459,11 @@ public class TopicActivity extends BaseActivity { } else if (rect != null && event.getAction() == MotionEvent.ACTION_UP && autoIncrement) { autoIncrement = false; paginationEnabled(true); - changePage(pageRequestValue - 1); + viewModel.changePage(pageRequestValue - 1); } else if (rect != null && event.getAction() == MotionEvent.ACTION_MOVE) { if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { autoIncrement = false; - decrementPageRequestValue(pageRequestValue - thisPage); + decrementPageRequestValue(pageRequestValue - viewModel.getTopicTaskResult().getValue().getCurrentPageIndex()); paginationEnabled(true); } } @@ -576,26 +475,22 @@ public class TopicActivity extends BaseActivity { @SuppressLint("ClickableViewAccessibility") private void initDecrementButton(ImageButton decrement, final int step) { // Decrement once for a click - decrement.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - if (!autoDecrement && step == LARGE_STEP) { - changePage(0); - } else if (!autoDecrement) { - decrementPageRequestValue(step); - changePage(pageRequestValue - 1); - } + decrement.setOnClickListener(v -> { + if (!autoDecrement && step == LARGE_STEP) { + viewModel.changePage(0); + } else if (!autoDecrement) { + decrementPageRequestValue(step); + viewModel.changePage(pageRequestValue - 1); } }); // Auto decrement for a long click decrement.setOnLongClickListener( - new View.OnLongClickListener() { - public boolean onLongClick(View arg0) { - paginationDisable(arg0); - autoDecrement = true; - repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY); - return false; - } + arg0 -> { + paginationDisable(arg0); + autoDecrement = true; + repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY); + return false; } ); @@ -609,12 +504,12 @@ public class TopicActivity extends BaseActivity { } else if (event.getAction() == MotionEvent.ACTION_UP && autoDecrement) { autoDecrement = false; paginationEnabled(true); - changePage(pageRequestValue - 1); + viewModel.changePage(pageRequestValue - 1); } else if (event.getAction() == MotionEvent.ACTION_MOVE) { if (rect != null && !rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { autoIncrement = false; - incrementPageRequestValue(thisPage - pageRequestValue); + incrementPageRequestValue(viewModel.getTopicTaskResult().getValue().getCurrentPageIndex() - pageRequestValue); paginationEnabled(true); } } @@ -624,11 +519,11 @@ public class TopicActivity extends BaseActivity { } private void incrementPageRequestValue(int step) { - if (pageRequestValue < numberOfPages - step) { + if (pageRequestValue < viewModel.getTopicTaskResult().getValue().getPageCount() - step) { pageRequestValue = pageRequestValue + step; } else - pageRequestValue = numberOfPages; - pageIndicator.setText(pageRequestValue + "/" + String.valueOf(numberOfPages)); + pageRequestValue = viewModel.getTopicTaskResult().getValue().getPageCount(); + pageIndicator.setText(pageRequestValue + "/" + String.valueOf(viewModel.getTopicTaskResult().getValue().getPageCount())); } private void decrementPageRequestValue(int step) { @@ -636,346 +531,107 @@ public class TopicActivity extends BaseActivity { pageRequestValue = pageRequestValue - step; else pageRequestValue = 1; - pageIndicator.setText(pageRequestValue + "/" + String.valueOf(numberOfPages)); - } - - private void changePage(int pageRequested) { - if (pageRequested != thisPage - 1) { - if (topicTask != null && topicTask.getStatus() != AsyncTask.Status.RUNNING) - topicTask.cancel(true); - - topicTask = new TopicTask(); - topicTask.execute(pagesUrls.get(pageRequested)); //Attempt data parsing - - } + pageIndicator.setText(pageRequestValue + "/" + String.valueOf(viewModel.getTopicTaskResult().getValue().getPageCount())); } //------------------------------------BOTTOM NAV BAR METHODS END------------------------------------ - class PrepareForReply extends AsyncTask, Void, Boolean> { - String numReplies, seqnum, sc, topic, buildedQuotes = ""; - - @Override - protected void onPreExecute() { - changePage(numberOfPages - 1); - progressBar.setVisibility(ProgressBar.VISIBLE); - paginationEnabled(false); - replyFAB.setEnabled(false); - replyFAB.hide(); - bottomNavBar.setVisibility(View.GONE); - } - - @Override - protected Boolean doInBackground(ArrayList... quoteList) { - Document document; - Request request = new Request.Builder() - .url(replyPageUrl + ";wap2") - .build(); - - try { - Response response = client.newCall(request).execute(); - document = Jsoup.parse(response.body().string()); - - numReplies = replyPageUrl.substring(replyPageUrl.indexOf("num_replies=") + 12); - seqnum = document.select("input[name=seqnum]").first().attr("value"); - sc = document.select("input[name=sc]").first().attr("value"); - topic = document.select("input[name=topic]").first().attr("value"); - } catch (IOException | Selector.SelectorParseException e) { - Timber.e(e, "Prepare failed."); - return false; - } - - for (Integer quotePosition : quoteList[0]) { - request = new Request.Builder() - .url("https://www.thmmy.gr/smf/index.php?action=quotefast;quote=" + - postsList.get(quotePosition).getPostIndex() + - ";" + "sesc=" + sc + ";xml") - .build(); - - try { - Response response = client.newCall(request).execute(); - String body = response.body().string(); - buildedQuotes += body.substring(body.indexOf("") + 7, body.indexOf("")); - buildedQuotes += "\n\n"; - } catch (IOException | Selector.SelectorParseException e) { - Timber.e(e, "Quote building failed."); - return false; - } - } - return true; - } - - @Override - protected void onPostExecute(Boolean result) { - postsList.add(Post.newQuickReply()); - topicAdapter.notifyItemInserted(postsList.size()); - topicAdapter.prepareForReply(new ReplyTask(), topicTitle, numReplies, seqnum, sc, - topic, buildedQuotes); - recyclerView.scrollToPosition(postsList.size() - 1); - progressBar.setVisibility(ProgressBar.GONE); - } + @Override + public void onTopicTaskStarted() { + hideControls(); } - class ReplyTask extends AsyncTask { - - @Override - protected void onPreExecute() { - progressBar.setVisibility(ProgressBar.VISIBLE); - paginationEnabled(false); - replyFAB.setEnabled(false); - } - - @Override - protected Boolean doInBackground(String... args) { - final String sentFrommTHMMY = includeAppSignaturePreference - ? "\n[right][size=7pt][i]sent from [url=https://play.google.com/store/apps/details?id=gr.thmmy.mthmmy]mTHMMY [/url][/i][/size][/right]" - : ""; - RequestBody postBody = new MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart("message", args[1] + sentFrommTHMMY) - .addFormDataPart("num_replies", args[2]) - .addFormDataPart("seqnum", args[3]) - .addFormDataPart("sc", args[4]) - .addFormDataPart("subject", args[0]) - .addFormDataPart("topic", args[5]) - .build(); - Request post = new Request.Builder() - .url("https://www.thmmy.gr/smf/index.php?action=post2") - .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36") - .post(postBody) - .build(); - - try { - client.newCall(post).execute(); - Response response = client.newCall(post).execute(); - switch (replyStatus(response)) { - case SUCCESSFUL: - return true; - case NEW_REPLY_WHILE_POSTING: - //TODO this... - return true; - default: - Timber.e("Malformed post. Request string: %s", post.toString()); - return true; - } - } catch (IOException e) { - Timber.e(e, "Post failed."); - return false; - } - } - - @Override - protected void onPostExecute(Boolean result) { - View view = getCurrentFocus(); - if (view != null) { - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); - } - - postsList.remove(postsList.size() - 1); - topicAdapter.notifyItemRemoved(postsList.size()); - - progressBar.setVisibility(ProgressBar.GONE); - replyFAB.setVisibility(View.VISIBLE); - bottomNavBar.setVisibility(View.VISIBLE); - - if (!result) - Toast.makeText(TopicActivity.this, "Post failed!", Toast.LENGTH_SHORT).show(); - paginationEnabled(true); - replyFAB.setEnabled(true); - - if (result) { - topicTask = new TopicTask(); - if ((postsList.get(postsList.size() - 1).getPostNumber() + 1) % 15 == 0) - topicTask.execute(viewModel.getTopicTaskResultMutableLiveData().getValue().getBaseUrl() + "." + 2147483647); - else { - reloadingPage = true; - topicTask.execute(loadedPageUrl); - } - } - } + @Override + public void onTopicTaskCancelled() { + showControls(); } - class DeleteTask extends AsyncTask { - - @Override - protected void onPreExecute() { - progressBar.setVisibility(ProgressBar.VISIBLE); - paginationEnabled(false); - replyFAB.setEnabled(false); - } - - @Override - protected Boolean doInBackground(String... args) { - Request delete = new Request.Builder() - .url(args[0]) - .header("User-Agent", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36") - .build(); - - try { - client.newCall(delete).execute(); - Response response = client.newCall(delete).execute(); - //Response response = client.newCall(delete).execute(); - switch (replyStatus(response)) { - case SUCCESSFUL: - return true; - default: - Timber.e("Something went wrong. Request string: %s", delete.toString()); - return true; - } - } catch (IOException e) { - Timber.e(e, "Delete failed."); - return false; - } + @Override + public void onReplyTaskFinished(boolean success) { + View view = getCurrentFocus(); + if (view != null) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } - @Override - protected void onPostExecute(Boolean result) { - progressBar.setVisibility(ProgressBar.GONE); - replyFAB.setVisibility(View.VISIBLE); - bottomNavBar.setVisibility(View.VISIBLE); + postsList.remove(postsList.size() - 1); + topicAdapter.notifyItemRemoved(postsList.size()); - if (!result) - Toast.makeText(TopicActivity.this, "Post deleted!", Toast.LENGTH_SHORT).show(); - paginationEnabled(true); - replyFAB.setEnabled(true); + showControls(); + viewModel.setWritingReply(false); - if (result) { - topicTask = new TopicTask(); - reloadingPage = true; - topicTask.execute(loadedPageUrl); + if (success) { + if ((postsList.get(postsList.size() - 1).getPostNumber() + 1) % 15 == 0) { + viewModel.loadUrl(viewModel.getBaseUrl() + "." + 2147483647); + } else { + viewModel.reloadPage(); } + } else { + Toast.makeText(TopicActivity.this, "Post failed!", Toast.LENGTH_SHORT).show(); } - } - - class PrepareForEdit extends AsyncTask { - int position; - String commitEditURL, numReplies, seqnum, sc, topic, postText = ""; - @Override - protected void onPreExecute() { - progressBar.setVisibility(ProgressBar.VISIBLE); - paginationEnabled(false); - replyFAB.setEnabled(false); - replyFAB.hide(); - bottomNavBar.setVisibility(View.GONE); - topicAdapter.disablePostEditing(); - } + } - @Override - protected Boolean doInBackground(Integer... positions) { - Document document; - position = positions[0]; - String url = postsList.get(position).getPostEditURL(); - Request request = new Request.Builder() - .url(url + ";wap2") - .build(); + @Override + public void onPrepareForReplyStarted() { + hideControls(); + } - try { - Response response = client.newCall(request).execute(); - document = Jsoup.parse(response.body().string()); + @Override + public void onPrepareForReplyCancelled() { + showControls(); + } - Element message = document.select("textarea").first(); - postText = message.text(); + @Override + public void onDeleteTaskStarted() { + hideControls(); + } - commitEditURL = document.select("form").first().attr("action"); - numReplies = replyPageUrl.substring(replyPageUrl.indexOf("num_replies=") + 12); - seqnum = document.select("input[name=seqnum]").first().attr("value"); - sc = document.select("input[name=sc]").first().attr("value"); - topic = document.select("input[name=topic]").first().attr("value"); + @Override + public void onDeleteTaskFinished(boolean result) { + showControls(); - return true; - } catch (IOException | Selector.SelectorParseException e) { - Timber.e(e, "Prepare failed."); - return false; - } + if (result) { + viewModel.reloadPage(); + } else { + Toast.makeText(TopicActivity.this, "Post deleted!", Toast.LENGTH_SHORT).show(); } + } - - @Override - protected void onPostExecute(Boolean result) { - postsList.get(position).setPostType(Post.TYPE_EDIT); - topicAdapter.notifyItemChanged(position); - topicAdapter.prepareForEdit(new EditTask(), commitEditURL, numReplies, seqnum, sc, topic, postText); - recyclerView.scrollToPosition(position); - progressBar.setVisibility(ProgressBar.GONE); - } + @Override + public void onPrepareEditStarted() { + hideControls(); } - public class EditTask extends AsyncTask { - EditTaskDTO dto; + @Override + public void onPrepareEditCancelled() { + showControls(); + } - @Override - protected void onPreExecute() { - progressBar.setVisibility(ProgressBar.VISIBLE); - paginationEnabled(false); - replyFAB.setEnabled(false); - } + @Override + public void onEditTaskStarted() { + hideControls(); + } - @Override - protected Boolean doInBackground(EditTaskDTO... editTaskDTOS) { - dto = editTaskDTOS[0]; - RequestBody postBody = new MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart("message", dto.getMessage()) - .addFormDataPart("num_replies", dto.getNumReplies()) - .addFormDataPart("seqnum", dto.getSeqnum()) - .addFormDataPart("sc", dto.getSc()) - .addFormDataPart("subject", dto.getSubject()) - .addFormDataPart("topic", dto.getTopic()) - .build(); - Request post = new Request.Builder() - .url(dto.getUrl()) - .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36") - .post(postBody) - .build(); - - try { - client.newCall(post).execute(); - Response response = client.newCall(post).execute(); - switch (replyStatus(response)) { - case SUCCESSFUL: - return true; - case NEW_REPLY_WHILE_POSTING: - //TODO this... - return true; - default: - Timber.e("Malformed post. Request string: %s", post.toString()); - return true; - } - } catch (IOException e) { - Timber.e(e, "Edit failed."); - return false; - } + @Override + public void onEditTaskFinished(boolean result, int position) { + View view = getCurrentFocus(); + if (view != null) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } - @Override - protected void onPostExecute(Boolean result) { - View view = getCurrentFocus(); - if (view != null) { - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); - } + postsList.get(position).setPostType(Post.TYPE_POST); + topicAdapter.notifyItemChanged(position); + viewModel.setEditingPost(false); - postsList.get(dto.getPosition()).setPostType(Post.TYPE_POST); - topicAdapter.notifyItemChanged(dto.getPosition()); + showControls(); - progressBar.setVisibility(ProgressBar.GONE); - replyFAB.setVisibility(View.VISIBLE); - bottomNavBar.setVisibility(View.VISIBLE); - - if (!result) - Toast.makeText(TopicActivity.this, "Edit failed!", Toast.LENGTH_SHORT).show(); - paginationEnabled(true); - replyFAB.setEnabled(true); - topicAdapter.enablePostEditing(); - - if (result) { - topicTask = new TopicTask(); - reloadingPage = true; - topicTask.execute(loadedPageUrl); - } + if (result) { + viewModel.setEditingPost(false); + viewModel.reloadPage(); + } else { + Toast.makeText(TopicActivity.this, "Edit failed!", Toast.LENGTH_SHORT).show(); } } } \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java index df252fc2..89f1ed25 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java @@ -2,6 +2,7 @@ package gr.thmmy.mthmmy.activities.topic; import android.annotation.SuppressLint; import android.annotation.TargetApi; +import android.arch.lifecycle.ViewModelProviders; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -47,6 +48,7 @@ import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.ThmmyFile; import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.utils.CircleTransform; +import gr.thmmy.mthmmy.viewmodel.TopicViewModel; import timber.log.Timber; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -69,7 +71,6 @@ class TopicAdapter extends RecyclerView.Adapter { */ private static int THUMBNAIL_SIZE; private final Context context; - private String topicTitle; private String baseUrl; private final ArrayList toQuoteList = new ArrayList<>(); private final List postsList; @@ -88,15 +89,11 @@ class TopicAdapter extends RecyclerView.Adapter { * Index of state indicator in the boolean array. If true quote button for this post is checked. */ private static final int isQuoteButtonChecked = 1; - private TopicActivity.TopicTask topicTask; - private TopicActivity.ReplyTask replyTask; - private TopicActivity.DeleteTask deleteTask; - private TopicActivity.PrepareForEdit prepareForEditTask; - private TopicActivity.EditTask editTask; + private TopicViewModel viewModel; private final String[] replyDataHolder = new String[2]; private final int replySubject = 0, replyText = 1; - private String commitEditURL, numReplies, seqnum, sc, topic, buildedQuotes, postText; + private String commitEditURL, buildedQuotes, postText; private boolean canReply = false; private boolean postEditingDisabled = false; @@ -104,54 +101,23 @@ class TopicAdapter extends RecyclerView.Adapter { * @param context the context of the {@link RecyclerView} * @param postsList List of {@link Post} objects to use */ - TopicAdapter(Context context, List postsList, String baseUrl, - TopicActivity.TopicTask topicTask) { + TopicAdapter(TopicActivity context, List postsList) { this.context = context; this.postsList = postsList; - this.baseUrl = baseUrl; + + viewModel = ViewModelProviders.of(context).get(TopicViewModel.class); THUMBNAIL_SIZE = (int) context.getResources().getDimension(R.dimen.thumbnail_size); for (int i = 0; i < postsList.size(); ++i) { //Initializes properties, array's values will be false by default viewProperties.add(new boolean[3]); } - this.topicTask = topicTask; } ArrayList getToQuoteList() { return toQuoteList; } - void prepareForReply(TopicActivity.ReplyTask replyTask, String topicTitle, String numReplies, - String seqnum, String sc, String topic, String buildedQuotes) { - this.replyTask = replyTask; - this.topicTitle = topicTitle; - this.numReplies = numReplies; - this.seqnum = seqnum; - this.sc = sc; - this.topic = topic; - this.buildedQuotes = buildedQuotes; - } - - void prepareForDelete(TopicActivity.DeleteTask deleteTask) { - this.deleteTask = deleteTask; - } - - void prepareForPrepareForEdit(TopicActivity.PrepareForEdit prepareForEditTask) { - this.prepareForEditTask = prepareForEditTask; - } - - void prepareForEdit(TopicActivity.EditTask editTask, String commitEditURL, String numReplies, String seqnum, String sc, - String topic, String postText) { - this.commitEditURL = commitEditURL; - this.editTask = editTask; - this.numReplies = numReplies; - this.seqnum = seqnum; - this.sc = sc; - this.topic = topic; - this.postText = postText; - } - @Override public int getItemViewType(int position) { return postsList.get(position).getPostType(); @@ -170,22 +136,14 @@ class TopicAdapter extends RecyclerView.Adapter { final EditText quickReplyText = view.findViewById(R.id.quick_reply_text); quickReplyText.setFocusableInTouchMode(true); - quickReplyText.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - quickReplyText.post(new Runnable() { - @Override - public void run() { - InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(quickReplyText, InputMethodManager.SHOW_IMPLICIT); - } - }); - } - }); + quickReplyText.setOnFocusChangeListener((v, hasFocus) -> quickReplyText.post(() -> { + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(quickReplyText, InputMethodManager.SHOW_IMPLICIT); + })); quickReplyText.requestFocus(); //Default post subject - replyDataHolder[replySubject] = "Re: " + topicTitle; + replyDataHolder[replySubject] = "Re: " + viewModel.getTopicTitle(); //Build quotes if (!Objects.equals(buildedQuotes, "")) replyDataHolder[replyText] = buildedQuotes; @@ -483,7 +441,7 @@ class TopicAdapter extends RecyclerView.Adapter { @Override public void onClick(DialogInterface dialog, int whichButton) { - deleteTask.execute(currentPost.getPostDeleteURL()); + viewModel.deletePost(currentPost.getPostDeleteURL()); } }) .setNegativeButton(android.R.string.no, null).show(); @@ -494,15 +452,11 @@ class TopicAdapter extends RecyclerView.Adapter { final TextView editPostButton = popUpContent.findViewById(R.id.edit_post); - if (postEditingDisabled || currentPost.getPostEditURL() == null || currentPost.getPostEditURL().equals("")) { + if (viewModel.isEditingPost() || currentPost.getPostEditURL() == null || currentPost.getPostEditURL().equals("")) { editPostButton.setVisibility(View.GONE); } else { - editPostButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - prepareForEditTask.execute(position); - } - }); + editPostButton.setOnClickListener(v -> viewModel.prepareForEdit(position, + postsList.get(position).getPostEditURL())); } //Displays the popup @@ -565,11 +519,12 @@ class TopicAdapter extends RecyclerView.Adapter { if (holder.quickReplySubject.getText().toString().isEmpty()) return; if (holder.quickReply.getText().toString().isEmpty()) return; holder.submitButton.setEnabled(false); - replyTask.execute(holder.quickReplySubject.getText().toString(), - holder.quickReply.getText().toString(), numReplies, seqnum, sc, topic); + + viewModel.postReply(context, holder.quickReplySubject.getText().toString(), + holder.quickReply.getText().toString()); holder.quickReplySubject.getText().clear(); - holder.quickReplySubject.setText("Re: " + topicTitle); + holder.quickReplySubject.setText("Re: " + viewModel.getTopicTitle()); holder.quickReply.getText().clear(); holder.submitButton.setEnabled(true); } @@ -599,19 +554,16 @@ class TopicAdapter extends RecyclerView.Adapter { holder.editSubject.setText(postsList.get(position).getSubject()); holder.editMessage.setText(postText); - holder.submitButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (holder.editSubject.getText().toString().isEmpty()) return; - if (holder.editMessage.getText().toString().isEmpty()) return; - holder.submitButton.setEnabled(false); - editTask.execute(new EditTaskDTO(position, commitEditURL, holder.editSubject.getText().toString(), - holder.editMessage.getText().toString(), numReplies, seqnum, sc, topic)); - - holder.editSubject.getText().clear(); - holder.editSubject.setText(postsList.get(position).getSubject()); - holder.submitButton.setEnabled(true); - } + holder.submitButton.setOnClickListener(view -> { + if (holder.editSubject.getText().toString().isEmpty()) return; + if (holder.editMessage.getText().toString().isEmpty()) return; + holder.submitButton.setEnabled(false); + + viewModel.editPost(position, holder.editSubject.getText().toString(), holder.editMessage.getText().toString()); + + holder.editSubject.getText().clear(); + holder.editSubject.setText(postsList.get(position).getSubject()); + holder.submitButton.setEnabled(true); }); if (backPressHidden) { @@ -621,9 +573,7 @@ class TopicAdapter extends RecyclerView.Adapter { } } - void resetTopic(String baseUrl, TopicActivity.TopicTask topicTask, boolean canReply) { - this.baseUrl = baseUrl; - this.topicTask = topicTask; + void resetTopic(boolean canReply) { this.canReply = canReply; viewProperties.clear(); for (int i = 0; i < postsList.size(); ++i) { @@ -753,10 +703,11 @@ class TopicAdapter extends RecyclerView.Adapter { final String uriString = uri.toString(); ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(uri); + viewModel.stopLoading(); if (target.is(ThmmyPage.PageCategory.TOPIC)) { //This url points to a topic //Checks if this is the current topic - if (Objects.equals(uriString.substring(0, uriString.lastIndexOf(".")), baseUrl)) { + if (Objects.equals(uriString.substring(0, uriString.lastIndexOf(".")), viewModel.getBaseUrl())) { //Gets uri's targeted message's index number String msgIndexReq = uriString.substring(uriString.indexOf("msg") + 3); if (msgIndexReq.contains("#")) @@ -772,7 +723,7 @@ class TopicAdapter extends RecyclerView.Adapter { } } - topicTask.execute(uri.toString()); + viewModel.loadUrl(uri.toString()); } Intent intent = new Intent(context, TopicActivity.class); @@ -866,12 +817,4 @@ class TopicAdapter extends RecyclerView.Adapter { return context.getResources().getString(R.string.fa_file); } - - public void disablePostEditing() { - postEditingDisabled = true; - } - - public void enablePostEditing() { - postEditingDisabled = false; - } } \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicTask.java index 6c654475..74f2c23a 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicTask.java @@ -16,7 +16,6 @@ import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.utils.parsing.ParseException; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; -import gr.thmmy.mthmmy.viewmodel.TopicViewModel; import okhttp3.Request; import okhttp3.Response; import timber.log.Timber; @@ -28,25 +27,71 @@ import timber.log.Timber; * parameter.

*/ public class TopicTask extends AsyncTask { - //input data - private TopicViewModel viewModel; + //----------------------------input data----------------------------- + private TopicTaskObserver topicTaskObserver; + private OnTopicTaskCompleted finishListener; + /** + * Becomes true when a page is being reloaded + */ private boolean reloadingPage; private ArrayList lastPostsList; - //output data + //-----------------------------output data---------------------------- private ResultCode resultCode; - private String topicTitle, replyPageUrl, topicTreeAndMods, topicViewers; + /** + * Holds this topic's title. At first this gets the value of the topic title that came with + * bundle and is rendered in the toolbar while parsing this topic. Later, if a different topic + * title is parsed from the html source, it gets updated. + */ + private String topicTitle; + /** + * This topic's reply url + */ + private String replyPageUrl; + //Topic's info related + private String topicTreeAndMods; + private String topicViewers; + private ArrayList newPostsList; + /** + * The topicId of the loaded page + */ private int loadedPageTopicId = -1; + /** + * The index of the post that has focus + */ private int focusedPostIndex = 0; + /** + * A list of the requested topic's pages urls + */ private SparseArray pagesUrls = new SparseArray<>(); - //(possibly) update data - private int currentPageIndex, pageCount; - private String baseUrl, lastPageLoadAttemptedUrl; + //-------------------------------------(possibly) update data------------------------------ + /** + * Holds current page's index (starting from 1, not 0) + */ + private int currentPageIndex; + /** + * Holds the requested topic's number of pages + */ + private int pageCount; + /** + * Holds this topic's base url. For example a topic with url similar to + * "https://www.thmmy.gr/smf/index.php?topic=1.15;topicseen" or + * "https://www.thmmy.gr/smf/index.php?topic=1.msg1#msg1" + * has the base url "https://www.thmmy.gr/smf/index.php?topic=1" + */ + private String baseUrl; + /** + * The url of the last page that was attempted to be loaded + */ + private String lastPageLoadAttemptedUrl; //consecutive load constructor - public TopicTask(boolean reloadingPage, String baseUrl, int currentPageIndex, int pageCount, - String lastPageLoadAttemptedUrl, ArrayList lastPostsList) { - this.viewModel = viewModel; + public TopicTask(TopicTaskObserver topicTaskObserver, OnTopicTaskCompleted finishListener, + boolean reloadingPage, String baseUrl, int currentPageIndex, int pageCount, + String lastPageLoadAttemptedUrl, + ArrayList lastPostsList) { + this.topicTaskObserver = topicTaskObserver; + this.finishListener = finishListener; this.reloadingPage = reloadingPage; this.baseUrl = baseUrl; this.currentPageIndex = currentPageIndex; @@ -55,9 +100,10 @@ public class TopicTask extends AsyncTask { this.lastPostsList = lastPostsList; } - //first load constructor - public TopicTask() { - this.viewModel = viewModel; + // first load or reload constructor + public TopicTask(TopicTaskObserver topicTaskObserver, OnTopicTaskCompleted finishListener) { + this.topicTaskObserver = topicTaskObserver; + this.finishListener = finishListener; this.reloadingPage = false; this.baseUrl = ""; this.currentPageIndex = 1; @@ -66,6 +112,11 @@ public class TopicTask extends AsyncTask { this.lastPostsList = null; } + @Override + protected void onPreExecute() { + topicTaskObserver.onTopicTaskStarted(); + } + @Override protected TopicTaskResult doInBackground(String... strings) { Document document = null; @@ -100,8 +151,8 @@ public class TopicTask extends AsyncTask { } else if ((Objects.equals(newPageUrl, baseUrl) && currentPageIndex == 1) || Integer.parseInt(newPageUrl.substring(baseUrl.length() + 1)) / 15 + 1 ==currentPageIndex) resultCode = ResultCode.SAME_PAGE; + } else if (!Objects.equals(lastPageLoadAttemptedUrl, "")) topicTitle = null; - if (reloadingPage) reloadingPage = !reloadingPage; lastPageLoadAttemptedUrl = newPageUrl; if (strings[0].substring(0, strings[0].lastIndexOf(".")).contains("topic=")) @@ -203,10 +254,20 @@ public class TopicTask extends AsyncTask { "είναι προσβάσιμο από εσάς.)").size() > 0; } + @Override + protected void onPostExecute(TopicTaskResult topicTaskResult) { + finishListener.onTopicTaskCompleted(topicTaskResult); + } + public enum ResultCode { SUCCESS, NETWORK_ERROR, PARSING_ERROR, OTHER_ERROR, SAME_PAGE, UNAUTHORIZED } + public interface TopicTaskObserver { + void onTopicTaskStarted(); + void onTopicTaskCancelled(); + } + public interface OnTopicTaskCompleted { void onTopicTaskCompleted(TopicTaskResult result); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java index 8286b3fd..3f48c240 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java +++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java @@ -1,40 +1,223 @@ package gr.thmmy.mthmmy.viewmodel; -import android.app.NotificationManager; import android.arch.lifecycle.MutableLiveData; import android.content.Context; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.Toast; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.preference.PreferenceManager; -import java.util.Objects; +import java.util.ArrayList; -import gr.thmmy.mthmmy.activities.topic.TopicActivity; +import gr.thmmy.mthmmy.activities.settings.SettingsActivity; +import gr.thmmy.mthmmy.activities.topic.DeleteTask; +import gr.thmmy.mthmmy.activities.topic.EditTask; +import gr.thmmy.mthmmy.activities.topic.PrepareForReply; +import gr.thmmy.mthmmy.activities.topic.PrepareForReplyResult; +import gr.thmmy.mthmmy.activities.topic.PrepareForEditResult; +import gr.thmmy.mthmmy.activities.topic.PrepareForEditTask; +import gr.thmmy.mthmmy.activities.topic.ReplyTask; import gr.thmmy.mthmmy.activities.topic.TopicTask; import gr.thmmy.mthmmy.activities.topic.TopicTaskResult; -import gr.thmmy.mthmmy.model.Bookmark; -import timber.log.Timber; +import gr.thmmy.mthmmy.base.BaseActivity; +import gr.thmmy.mthmmy.model.Post; +import gr.thmmy.mthmmy.session.SessionManager; -import static gr.thmmy.mthmmy.services.NotificationService.NEW_POST_TAG; +public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTaskCompleted, + PrepareForReply.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished { -public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTaskCompleted { - private MutableLiveData topicTaskResultMutableLiveData; + //TODO: make variables for editing and replying state - public MutableLiveData getTopicTaskResultMutableLiveData() { - if (topicTaskResultMutableLiveData == null) { - topicTaskResultMutableLiveData = new MutableLiveData<>(); - //load topic data + private boolean editingPost = false; + private boolean writingReply = false; + + private TopicTask currentTopicTask; + private PrepareForEditTask currentPrepareForEditTask; + private PrepareForReply currentPrepareForReplyTask; + + private TopicTask.TopicTaskObserver topicTaskObserver; + private DeleteTask.DeleteTaskCallbacks deleteTaskCallbacks; + private ReplyTask.OnReplyTaskFinished replyFinishListener; + private PrepareForEditTask.PrepareForEditCallbacks prepareForEditCallbacks; + private EditTask.EditTaskCallbacks editTaskCallbacks; + private PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks; + + private MutableLiveData topicTaskResult = new MutableLiveData<>(); + private MutableLiveData prepareForReplyResult = new MutableLiveData<>(); + private MutableLiveData prepareForEditResult = new MutableLiveData<>(); + + public void setTopicTaskObserver(TopicTask.TopicTaskObserver topicTaskObserver) { + this.topicTaskObserver = topicTaskObserver; + } + + public void setDeleteTaskCallbacks(DeleteTask.DeleteTaskCallbacks deleteTaskCallbacks) { + this.deleteTaskCallbacks = deleteTaskCallbacks; + } + + public void setReplyFinishListener(ReplyTask.OnReplyTaskFinished replyFinishListener) { + this.replyFinishListener = replyFinishListener; + } + + public void setPrepareForEditCallbacks(PrepareForEditTask.PrepareForEditCallbacks prepareForEditCallbacks) { + this.prepareForEditCallbacks = prepareForEditCallbacks; + } + + public void setEditTaskCallbacks(EditTask.EditTaskCallbacks editTaskCallbacks) { + this.editTaskCallbacks = editTaskCallbacks; + } + + public void setPrepareForReplyCallbacks(PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks) { + this.prepareForReplyCallbacks = prepareForReplyCallbacks; + } + + public MutableLiveData getTopicTaskResult() { + return topicTaskResult; + } + + public MutableLiveData getPrepareForReplyResult() { + return prepareForReplyResult; + } + + public MutableLiveData getPrepareForEditResult() { + return prepareForEditResult; + } + + public void setEditingPost(boolean editingPost) { + this.editingPost = editingPost; + } + + public boolean isEditingPost() { + return editingPost; + } + + public void setWritingReply(boolean writingReply) { + this.writingReply = writingReply; + } + + public String getBaseUrl() { + if (topicTaskResult.getValue() != null) { + return topicTaskResult.getValue().getBaseUrl(); + } else { + return ""; + } + } + + public String getTopicUrl() { + if (topicTaskResult.getValue() != null) { + return topicTaskResult.getValue().getLastPageLoadAttemptedUrl(); + } else { + throw new NullPointerException("Topic task has not finished yet!"); + } + } + + public String getTopicTitle() { + if (topicTaskResult.getValue() != null) { + return topicTaskResult.getValue().getTopicTitle(); + } else { + throw new NullPointerException("Topic task has not finished yet!"); } - return topicTaskResultMutableLiveData; } public void initialLoad(String pageUrl) { - new TopicTask().execute(pageUrl); - //load posts + currentTopicTask = new TopicTask(topicTaskObserver, this); + currentTopicTask.execute(pageUrl); + } + + public void loadUrl(String pageUrl) { + stopLoading(); + currentTopicTask = new TopicTask(topicTaskObserver, this); + currentTopicTask.execute(pageUrl); + } + + public void reloadPage() { + stopLoading(); + currentTopicTask = new TopicTask(topicTaskObserver, this); + currentTopicTask.execute(topicTaskResult.getValue().getLastPageLoadAttemptedUrl()); + } + + public void changePage(int pageRequested) { + if (pageRequested != topicTaskResult.getValue().getCurrentPageIndex() - 1) { + stopLoading(); + currentTopicTask = new TopicTask(topicTaskObserver, this); + currentTopicTask.execute(topicTaskResult.getValue().getPagesUrls().get(pageRequested)); + } + } + + public void prepareForReply(ArrayList postsList, ArrayList toQuoteList) { + if (topicTaskResult.getValue() == null) + throw new NullPointerException("Topic task has not finished yet!"); + stopLoading(); + changePage(topicTaskResult.getValue().getPageCount() - 1); + currentPrepareForReplyTask = new PrepareForReply(prepareForReplyCallbacks, this, + topicTaskResult.getValue().getReplyPageUrl(), postsList); + currentPrepareForReplyTask.execute(toQuoteList.toArray(new Integer[0])); + } + + public void postReply(Context context, String subject, String reply) { + if (prepareForReplyResult.getValue() == null) { + throw new NullPointerException("Reply preparation was not found!"); + } + PrepareForReplyResult replyForm = prepareForReplyResult.getValue(); + boolean includeAppSignature = true; + SessionManager sessionManager = BaseActivity.getSessionManager(); + if (sessionManager.isLoggedIn()) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + includeAppSignature = prefs.getBoolean(SettingsActivity.APP_SIGNATURE_ENABLE_KEY, true); + } + new ReplyTask(replyFinishListener, includeAppSignature).execute(subject, reply, + replyForm.getNumReplies(), replyForm.getSeqnum(), replyForm.getSc(), replyForm.getTopic()); + } + + public void deletePost(String postDeleteUrl) { + new DeleteTask(deleteTaskCallbacks).execute(postDeleteUrl); + } + + public void prepareForEdit(int position, String postEditURL) { + if (topicTaskResult.getValue() == null) + throw new NullPointerException("Topic task has not finished yet!"); + currentPrepareForEditTask = new PrepareForEditTask(prepareForEditCallbacks, this, position, + topicTaskResult.getValue().getReplyPageUrl()); + currentPrepareForEditTask.execute(postEditURL); + } + + public void editPost(int position, String subject, String message) { + if (prepareForEditResult.getValue() == null) + throw new NullPointerException("Edit preparation was not found!"); + PrepareForEditResult editResult = prepareForEditResult.getValue(); + new EditTask(editTaskCallbacks, position).execute(editResult.getCommitEditUrl(), message, + editResult.getNumReplies(), editResult.getSeqnum(), editResult.getSc(), subject, editResult.getTopic()); + } + + /** + * cancel tasks that change the ui + */ + public void stopLoading() { + if (currentTopicTask != null && currentTopicTask.getStatus() == AsyncTask.Status.RUNNING) { + currentTopicTask.cancel(true); + topicTaskObserver.onTopicTaskCancelled(); + } + if (currentPrepareForEditTask != null && currentPrepareForEditTask.getStatus() == AsyncTask.Status.RUNNING) { + currentPrepareForEditTask.cancel(true); + prepareForEditCallbacks.onPrepareEditCancelled(); + } + if (currentPrepareForReplyTask != null && currentPrepareForReplyTask.getStatus() == AsyncTask.Status.RUNNING) { + currentPrepareForReplyTask.cancel(true); + prepareForReplyCallbacks.onPrepareForReplyCancelled(); + } + // no need to cancel reply, edit and delete task for navigation } @Override public void onTopicTaskCompleted(TopicTaskResult result) { - topicTaskResultMutableLiveData.setValue(result); + topicTaskResult.setValue(result); + } + + @Override + public void onPrepareForReplyFinished(PrepareForReplyResult result) { + prepareForReplyResult.setValue(result); + } + + @Override + public void onPrepareEditFinished(PrepareForEditResult result, int position) { + prepareForEditResult.setValue(result); } }