mirror of https://github.com/ThmmyNoLife/mTHMMY
Browse Source
Change TopicActivity to MVVM arch using ViewModel, Add EditPost, Clean-up TopicActivity, Add post focus, Other changes and fixespull/44/head
oogee
6 years ago
committed by
Apostolof
24 changed files with 1829 additions and 876 deletions
File diff suppressed because it is too large
@ -0,0 +1,61 @@ |
|||
package gr.thmmy.mthmmy.activities.topic.tasks; |
|||
|
|||
import android.os.AsyncTask; |
|||
|
|||
import java.io.IOException; |
|||
|
|||
import gr.thmmy.mthmmy.activities.topic.Posting; |
|||
import gr.thmmy.mthmmy.base.BaseApplication; |
|||
import okhttp3.OkHttpClient; |
|||
import okhttp3.Request; |
|||
import okhttp3.Response; |
|||
import timber.log.Timber; |
|||
|
|||
public class DeleteTask extends AsyncTask<String, Void, Boolean> { |
|||
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 (Posting.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); |
|||
} |
|||
} |
@ -0,0 +1,77 @@ |
|||
package gr.thmmy.mthmmy.activities.topic.tasks; |
|||
|
|||
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<String, Void, Boolean> { |
|||
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); |
|||
} |
|||
} |
@ -0,0 +1,51 @@ |
|||
package gr.thmmy.mthmmy.activities.topic.tasks; |
|||
|
|||
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; |
|||
} |
|||
} |
@ -0,0 +1,80 @@ |
|||
package gr.thmmy.mthmmy.activities.topic.tasks; |
|||
|
|||
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.activities.topic.tasks.PrepareForEditResult; |
|||
import gr.thmmy.mthmmy.base.BaseApplication; |
|||
import okhttp3.OkHttpClient; |
|||
import okhttp3.Request; |
|||
import okhttp3.Response; |
|||
import timber.log.Timber; |
|||
|
|||
public class PrepareForEditTask extends AsyncTask<String, Void, PrepareForEditResult> { |
|||
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); |
|||
} |
|||
} |
@ -0,0 +1,91 @@ |
|||
package gr.thmmy.mthmmy.activities.topic.tasks; |
|||
|
|||
import android.os.AsyncTask; |
|||
|
|||
import org.jsoup.Jsoup; |
|||
import org.jsoup.nodes.Document; |
|||
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 PrepareForReply extends AsyncTask<Integer, Void, PrepareForReplyResult> { |
|||
private PrepareForReplyCallbacks listener; |
|||
private OnPrepareForReplyFinished finishListener; |
|||
private String replyPageUrl; |
|||
|
|||
public PrepareForReply(PrepareForReplyCallbacks listener, OnPrepareForReplyFinished finishListener, |
|||
String replyPageUrl) { |
|||
this.listener = listener; |
|||
this.finishListener = finishListener; |
|||
this.replyPageUrl = replyPageUrl; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPreExecute() { |
|||
listener.onPrepareForReplyStarted(); |
|||
} |
|||
|
|||
@Override |
|||
protected PrepareForReplyResult doInBackground(Integer... postIndices) { |
|||
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 postIndex : postIndices) { |
|||
request = new Request.Builder() |
|||
.url("https://www.thmmy.gr/smf/index.php?action=quotefast;quote=" + |
|||
postIndex + ";" + "sesc=" + sc + ";xml") |
|||
.build(); |
|||
|
|||
try { |
|||
Response response = client.newCall(request).execute(); |
|||
String body = response.body().string(); |
|||
buildedQuotes.append(body.substring(body.indexOf("<quote>") + 7, body.indexOf("</quote>"))); |
|||
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); |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
package gr.thmmy.mthmmy.activities.topic.tasks; |
|||
|
|||
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; |
|||
} |
|||
} |
@ -0,0 +1,80 @@ |
|||
package gr.thmmy.mthmmy.activities.topic.tasks; |
|||
|
|||
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<String, Void, Boolean> { |
|||
private ReplyTaskCallbacks listener; |
|||
private boolean includeAppSignature; |
|||
|
|||
public ReplyTask(ReplyTaskCallbacks listener, boolean includeAppSignature) { |
|||
this.listener = listener; |
|||
this.includeAppSignature = includeAppSignature; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPreExecute() { |
|||
listener.onReplyTaskStarted(); |
|||
} |
|||
|
|||
@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 ReplyTaskCallbacks { |
|||
void onReplyTaskStarted(); |
|||
void onReplyTaskFinished(boolean result); |
|||
} |
|||
} |
@ -0,0 +1,172 @@ |
|||
package gr.thmmy.mthmmy.activities.topic.tasks; |
|||
|
|||
import android.os.AsyncTask; |
|||
import android.util.SparseArray; |
|||
|
|||
import org.jsoup.Jsoup; |
|||
import org.jsoup.nodes.Document; |
|||
import org.jsoup.nodes.Element; |
|||
|
|||
import java.io.IOException; |
|||
import java.util.ArrayList; |
|||
import java.util.Objects; |
|||
|
|||
import gr.thmmy.mthmmy.activities.topic.TopicParser; |
|||
import gr.thmmy.mthmmy.base.BaseApplication; |
|||
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 okhttp3.Request; |
|||
import okhttp3.Response; |
|||
import timber.log.Timber; |
|||
|
|||
/** |
|||
* An {@link AsyncTask} that handles asynchronous fetching of this topic page and parsing of its |
|||
* data. |
|||
* <p>TopicTask's {@link AsyncTask#execute execute} method needs a topic's url as String |
|||
* parameter.</p> |
|||
*/ |
|||
public class TopicTask extends AsyncTask<String, Void, TopicTaskResult> { |
|||
private TopicTaskObserver topicTaskObserver; |
|||
private OnTopicTaskCompleted finishListener; |
|||
|
|||
public TopicTask(TopicTaskObserver topicTaskObserver, OnTopicTaskCompleted finishListener) { |
|||
this.topicTaskObserver = topicTaskObserver; |
|||
this.finishListener = finishListener; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPreExecute() { |
|||
topicTaskObserver.onTopicTaskStarted(); |
|||
} |
|||
|
|||
@Override |
|||
protected TopicTaskResult doInBackground(String... strings) { |
|||
String topicTitle = null; |
|||
String topicTreeAndMods = ""; |
|||
String topicViewers = ""; |
|||
ArrayList<Post> newPostsList = null; |
|||
int loadedPageTopicId = -1; |
|||
int focusedPostIndex = 0; |
|||
SparseArray<String> pagesUrls = new SparseArray<>(); |
|||
int currentPageIndex = 1; |
|||
int pageCount = 1; |
|||
String baseUrl = ""; |
|||
String lastPageLoadAttemptedUrl = ""; |
|||
|
|||
Document topic = null; |
|||
String newPageUrl = strings[0]; |
|||
|
|||
//Finds the index of message focus if present
|
|||
int postFocus = 0; |
|||
{ |
|||
if (newPageUrl.contains("msg")) { |
|||
String tmp = newPageUrl.substring(newPageUrl.indexOf("msg") + 3); |
|||
if (tmp.contains(";")) |
|||
postFocus = Integer.parseInt(tmp.substring(0, tmp.indexOf(";"))); |
|||
else if (tmp.contains("#")) |
|||
postFocus = Integer.parseInt(tmp.substring(0, tmp.indexOf("#"))); |
|||
} |
|||
} |
|||
|
|||
lastPageLoadAttemptedUrl = newPageUrl; |
|||
if (strings[0].substring(0, strings[0].lastIndexOf(".")).contains("topic=")) |
|||
baseUrl = strings[0].substring(0, strings[0].lastIndexOf(".")); //New topic's base url
|
|||
String replyPageUrl = null; |
|||
Request request = new Request.Builder() |
|||
.url(newPageUrl) |
|||
.build(); |
|||
ResultCode resultCode; |
|||
try { |
|||
Response response = BaseApplication.getInstance().getClient().newCall(request).execute(); |
|||
topic = Jsoup.parse(response.body().string()); |
|||
|
|||
ParseHelpers.Language language = ParseHelpers.Language.getLanguage(topic); |
|||
|
|||
//Finds topic's tree, mods and users viewing
|
|||
topicTreeAndMods = topic.select("div.nav").first().html(); |
|||
topicViewers = TopicParser.parseUsersViewingThisTopic(topic, language); |
|||
|
|||
//Finds reply page url
|
|||
Element replyButton = topic.select("a:has(img[alt=Reply])").first(); |
|||
if (replyButton == null) |
|||
replyButton = topic.select("a:has(img[alt=Απάντηση])").first(); |
|||
if (replyButton != null) replyPageUrl = replyButton.attr("href"); |
|||
|
|||
//Finds topic title if missing
|
|||
topicTitle = topic.select("td[id=top_subject]").first().text(); |
|||
if (topicTitle.contains("Topic:")) { |
|||
topicTitle = topicTitle.substring(topicTitle.indexOf("Topic:") + 7 |
|||
, topicTitle.indexOf("(Read") - 2); |
|||
} else { |
|||
topicTitle = topicTitle.substring(topicTitle.indexOf("Θέμα:") + 6 |
|||
, topicTitle.indexOf("(Αναγνώστηκε") - 2); |
|||
Timber.d("Parsed title: %s", topicTitle); |
|||
} |
|||
|
|||
//Finds current page's index
|
|||
currentPageIndex = TopicParser.parseCurrentPageIndex(topic, language); |
|||
|
|||
//Finds number of pages
|
|||
pageCount = TopicParser.parseTopicNumberOfPages(topic, currentPageIndex, language); |
|||
|
|||
for (int i = 0; i < pageCount; i++) { |
|||
//Generate each page's url from topic's base url +".15*numberOfPage"
|
|||
pagesUrls.put(i, baseUrl + "." + String.valueOf(i * 15)); |
|||
} |
|||
|
|||
newPostsList = TopicParser.parseTopic(topic, language); |
|||
|
|||
loadedPageTopicId = Integer.parseInt(ThmmyPage.getTopicId(lastPageLoadAttemptedUrl)); |
|||
|
|||
//Finds the position of the focused message if present
|
|||
for (int i = 0; i < newPostsList.size(); ++i) { |
|||
if (newPostsList.get(i).getPostIndex() == postFocus) { |
|||
focusedPostIndex = i; |
|||
break; |
|||
} |
|||
} |
|||
resultCode = ResultCode.SUCCESS; |
|||
} catch (IOException e) { |
|||
Timber.i(e, "IO Exception"); |
|||
resultCode = ResultCode.NETWORK_ERROR; |
|||
} catch (Exception e) { |
|||
if (isUnauthorized(topic)) { |
|||
resultCode = ResultCode.UNAUTHORIZED; |
|||
} else { |
|||
Timber.e(e, "Parsing Error"); |
|||
resultCode = ResultCode.PARSING_ERROR; |
|||
} |
|||
} |
|||
return new TopicTaskResult(resultCode, baseUrl, topicTitle, replyPageUrl, newPostsList, |
|||
loadedPageTopicId, currentPageIndex, pageCount, focusedPostIndex, topicTreeAndMods, |
|||
topicViewers, lastPageLoadAttemptedUrl, pagesUrls); |
|||
} |
|||
|
|||
private boolean isUnauthorized(Document document) { |
|||
return document != null && document.select("body:contains(The topic or board you" + |
|||
" are looking for appears to be either missing or off limits to you.)," + |
|||
"body:contains(Το θέμα ή πίνακας που ψάχνετε ή δεν υπάρχει ή δεν " + |
|||
"είναι προσβάσιμο από εσάς.)").size() > 0; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPostExecute(TopicTaskResult topicTaskResult) { |
|||
finishListener.onTopicTaskCompleted(topicTaskResult); |
|||
} |
|||
|
|||
public enum ResultCode { |
|||
SUCCESS, NETWORK_ERROR, PARSING_ERROR, UNAUTHORIZED |
|||
} |
|||
|
|||
public interface TopicTaskObserver { |
|||
void onTopicTaskStarted(); |
|||
|
|||
void onTopicTaskCancelled(); |
|||
} |
|||
|
|||
public interface OnTopicTaskCompleted { |
|||
void onTopicTaskCompleted(TopicTaskResult result); |
|||
} |
|||
} |
@ -0,0 +1,125 @@ |
|||
package gr.thmmy.mthmmy.activities.topic.tasks; |
|||
|
|||
import android.util.SparseArray; |
|||
|
|||
import java.util.ArrayList; |
|||
|
|||
import gr.thmmy.mthmmy.activities.topic.tasks.TopicTask; |
|||
import gr.thmmy.mthmmy.model.Post; |
|||
|
|||
public class TopicTaskResult { |
|||
private final TopicTask.ResultCode resultCode; |
|||
/** |
|||
* 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 final String baseUrl; |
|||
/** |
|||
* 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 final String topicTitle; |
|||
/** |
|||
* This topic's reply url |
|||
*/ |
|||
private final String replyPageUrl; |
|||
private final ArrayList<Post> newPostsList; |
|||
/** |
|||
* The topicId of the loaded page |
|||
*/ |
|||
private final int loadedPageTopicId; |
|||
/** |
|||
* Holds current page's index (starting from 1, not 0) |
|||
*/ |
|||
private final int currentPageIndex; |
|||
/** |
|||
* Holds the requested topic's number of pages |
|||
*/ |
|||
private final int pageCount; |
|||
/** |
|||
* The index of the post that has focus |
|||
*/ |
|||
private final int focusedPostIndex; |
|||
//Topic's info related
|
|||
private final String topicTreeAndMods; |
|||
private final String topicViewers; |
|||
/** |
|||
* The url of the last page that was attempted to be loaded |
|||
*/ |
|||
private final String lastPageLoadAttemptedUrl; |
|||
private final SparseArray<String> pagesUrls; |
|||
|
|||
public TopicTaskResult(TopicTask.ResultCode resultCode, String baseUrl, String topicTitle, |
|||
String replyPageUrl, ArrayList<Post> newPostsList, int loadedPageTopicId, |
|||
int currentPageIndex, int pageCount, int focusedPostIndex, String topicTreeAndMods, |
|||
String topicViewers, String lastPageLoadAttemptedUrl, SparseArray<String> pagesUrls) { |
|||
this.resultCode = resultCode; |
|||
this.baseUrl = baseUrl; |
|||
this.topicTitle = topicTitle; |
|||
this.replyPageUrl = replyPageUrl; |
|||
this.newPostsList = newPostsList; |
|||
this.loadedPageTopicId = loadedPageTopicId; |
|||
this.currentPageIndex = currentPageIndex; |
|||
this.pageCount = pageCount; |
|||
this.focusedPostIndex = focusedPostIndex; |
|||
this.topicTreeAndMods = topicTreeAndMods; |
|||
this.topicViewers = topicViewers; |
|||
this.lastPageLoadAttemptedUrl = lastPageLoadAttemptedUrl; |
|||
this.pagesUrls = pagesUrls; |
|||
} |
|||
|
|||
public TopicTask.ResultCode getResultCode() { |
|||
return resultCode; |
|||
} |
|||
|
|||
public String getBaseUrl() { |
|||
return baseUrl; |
|||
} |
|||
|
|||
public String getTopicTitle() { |
|||
return topicTitle; |
|||
} |
|||
|
|||
public String getReplyPageUrl() { |
|||
return replyPageUrl; |
|||
} |
|||
|
|||
public ArrayList<Post> getNewPostsList() { |
|||
return newPostsList; |
|||
} |
|||
|
|||
public int getLoadedPageTopicId() { |
|||
return loadedPageTopicId; |
|||
} |
|||
|
|||
public int getCurrentPageIndex() { |
|||
return currentPageIndex; |
|||
} |
|||
|
|||
public int getPageCount() { |
|||
return pageCount; |
|||
} |
|||
|
|||
public int getFocusedPostIndex() { |
|||
return focusedPostIndex; |
|||
} |
|||
|
|||
public String getTopicTreeAndMods() { |
|||
return topicTreeAndMods; |
|||
} |
|||
|
|||
public String getTopicViewers() { |
|||
return topicViewers; |
|||
} |
|||
|
|||
public String getLastPageLoadAttemptedUrl() { |
|||
return lastPageLoadAttemptedUrl; |
|||
} |
|||
|
|||
public SparseArray<String> getPagesUrls() { |
|||
return pagesUrls; |
|||
} |
|||
} |
@ -0,0 +1,76 @@ |
|||
package gr.thmmy.mthmmy.utils; |
|||
|
|||
import android.app.Activity; |
|||
import android.content.Intent; |
|||
import android.net.Uri; |
|||
import android.os.Build; |
|||
import android.os.Bundle; |
|||
import android.text.Html; |
|||
import android.text.SpannableStringBuilder; |
|||
import android.text.style.ClickableSpan; |
|||
import android.text.style.URLSpan; |
|||
import android.view.View; |
|||
|
|||
import gr.thmmy.mthmmy.activities.board.BoardActivity; |
|||
import gr.thmmy.mthmmy.activities.profile.ProfileActivity; |
|||
import gr.thmmy.mthmmy.model.ThmmyPage; |
|||
|
|||
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; |
|||
|
|||
public class HTMLUtils { |
|||
private HTMLUtils() {} |
|||
|
|||
public static SpannableStringBuilder getSpannableFromHtml(Activity activity, String html) { |
|||
CharSequence sequence; |
|||
if (Build.VERSION.SDK_INT >= 24) { |
|||
sequence = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY); |
|||
} else { |
|||
//noinspection deprecation
|
|||
sequence = Html.fromHtml(html); |
|||
} |
|||
SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence); |
|||
URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class); |
|||
for (URLSpan span : urls) { |
|||
makeLinkClickable(activity, strBuilder, span); |
|||
} |
|||
return strBuilder; |
|||
} |
|||
|
|||
private static void makeLinkClickable(Activity activity, SpannableStringBuilder strBuilder, final URLSpan span) { |
|||
int start = strBuilder.getSpanStart(span); |
|||
int end = strBuilder.getSpanEnd(span); |
|||
int flags = strBuilder.getSpanFlags(span); |
|||
ClickableSpan clickable = new ClickableSpan() { |
|||
@Override |
|||
public void onClick(View view) { |
|||
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(span.getURL())); |
|||
if (target.is(ThmmyPage.PageCategory.BOARD)) { |
|||
Intent intent = new Intent(activity.getApplicationContext(), BoardActivity.class); |
|||
Bundle extras = new Bundle(); |
|||
extras.putString(BUNDLE_BOARD_URL, span.getURL()); |
|||
extras.putString(BUNDLE_BOARD_TITLE, ""); |
|||
intent.putExtras(extras); |
|||
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); |
|||
activity.getApplicationContext().startActivity(intent); |
|||
} else if (target.is(ThmmyPage.PageCategory.PROFILE)) { |
|||
Intent intent = new Intent(activity.getApplicationContext(), ProfileActivity.class); |
|||
Bundle extras = new Bundle(); |
|||
extras.putString(BUNDLE_PROFILE_URL, span.getURL()); |
|||
extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, ""); |
|||
extras.putString(BUNDLE_PROFILE_USERNAME, ""); |
|||
intent.putExtras(extras); |
|||
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); |
|||
activity.getApplicationContext().startActivity(intent); |
|||
} else if (target.is(ThmmyPage.PageCategory.INDEX)) |
|||
activity.finish(); |
|||
} |
|||
}; |
|||
strBuilder.setSpan(clickable, start, end, flags); |
|||
strBuilder.removeSpan(span); |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
package gr.thmmy.mthmmy.viewmodel; |
|||
|
|||
import android.arch.lifecycle.LiveData; |
|||
import android.arch.lifecycle.MutableLiveData; |
|||
import android.arch.lifecycle.ViewModel; |
|||
|
|||
import gr.thmmy.mthmmy.model.Bookmark; |
|||
|
|||
public class BaseViewModel extends ViewModel { |
|||
protected MutableLiveData<Bookmark> currentPageBookmark; |
|||
|
|||
public LiveData<Bookmark> getCurrentPageBookmark() { |
|||
if (currentPageBookmark == null) { |
|||
currentPageBookmark = new MutableLiveData<>(); |
|||
} |
|||
return currentPageBookmark; |
|||
} |
|||
} |
@ -0,0 +1,316 @@ |
|||
package gr.thmmy.mthmmy.viewmodel; |
|||
|
|||
import android.arch.lifecycle.MutableLiveData; |
|||
import android.content.Context; |
|||
import android.content.SharedPreferences; |
|||
import android.os.AsyncTask; |
|||
import android.preference.PreferenceManager; |
|||
|
|||
import java.util.ArrayList; |
|||
|
|||
import gr.thmmy.mthmmy.activities.settings.SettingsActivity; |
|||
import gr.thmmy.mthmmy.activities.topic.tasks.DeleteTask; |
|||
import gr.thmmy.mthmmy.activities.topic.tasks.EditTask; |
|||
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditResult; |
|||
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditTask; |
|||
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReply; |
|||
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyResult; |
|||
import gr.thmmy.mthmmy.activities.topic.tasks.ReplyTask; |
|||
import gr.thmmy.mthmmy.activities.topic.tasks.TopicTask; |
|||
import gr.thmmy.mthmmy.activities.topic.tasks.TopicTaskResult; |
|||
import gr.thmmy.mthmmy.base.BaseActivity; |
|||
import gr.thmmy.mthmmy.model.Post; |
|||
import gr.thmmy.mthmmy.session.SessionManager; |
|||
|
|||
public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTaskCompleted, |
|||
PrepareForReply.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished { |
|||
/** |
|||
* topic state |
|||
*/ |
|||
private boolean editingPost = false; |
|||
private boolean writingReply = false; |
|||
/** |
|||
* A list of {@link Post#getPostIndex()} for building quotes for replying |
|||
*/ |
|||
private ArrayList<Integer> toQuoteList = new ArrayList<>(); |
|||
/** |
|||
* caches the expand/collapse state of the user extra info in the current page for the recyclerview |
|||
*/ |
|||
private ArrayList<Boolean> isUserExtraInfoVisibile = new ArrayList<>(); |
|||
/** |
|||
* holds the adapter position of the post being edited |
|||
*/ |
|||
private int postBeingEditedPosition; |
|||
|
|||
private TopicTask currentTopicTask; |
|||
private PrepareForEditTask currentPrepareForEditTask; |
|||
private PrepareForReply currentPrepareForReplyTask; |
|||
|
|||
//callbacks for topic activity
|
|||
private TopicTask.TopicTaskObserver topicTaskObserver; |
|||
private DeleteTask.DeleteTaskCallbacks deleteTaskCallbacks; |
|||
private ReplyTask.ReplyTaskCallbacks replyFinishListener; |
|||
private PrepareForEditTask.PrepareForEditCallbacks prepareForEditCallbacks; |
|||
private EditTask.EditTaskCallbacks editTaskCallbacks; |
|||
private PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks; |
|||
|
|||
private MutableLiveData<TopicTaskResult> topicTaskResult = new MutableLiveData<>(); |
|||
private MutableLiveData<PrepareForReplyResult> prepareForReplyResult = new MutableLiveData<>(); |
|||
private MutableLiveData<PrepareForEditResult> prepareForEditResult = new MutableLiveData<>(); |
|||
|
|||
private String firstTopicUrl; |
|||
|
|||
public void initialLoad(String pageUrl) { |
|||
firstTopicUrl = pageUrl; |
|||
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() { |
|||
if (topicTaskResult.getValue() == null) |
|||
throw new NullPointerException("No topic task has finished yet!"); |
|||
loadUrl(topicTaskResult.getValue().getLastPageLoadAttemptedUrl()); |
|||
} |
|||
|
|||
public void changePage(int pageRequested) { |
|||
if (topicTaskResult.getValue() == null) |
|||
throw new NullPointerException("No page has been loaded yet!"); |
|||
if (pageRequested != topicTaskResult.getValue().getCurrentPageIndex() - 1) |
|||
loadUrl(topicTaskResult.getValue().getPagesUrls().get(pageRequested)); |
|||
} |
|||
|
|||
public void prepareForReply() { |
|||
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()); |
|||
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.POSTING_APP_SIGNATURE_ENABLE_KEY, true); |
|||
} |
|||
toQuoteList.clear(); |
|||
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!"); |
|||
stopLoading(); |
|||
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 |
|||
* topic, prepare for edit, prepare for reply tasks need to cancel all other ui changing tasks |
|||
* before starting |
|||
*/ |
|||
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, user should not have to wait for the ui
|
|||
// after he is done posting, editing or deleting
|
|||
} |
|||
|
|||
// callbacks for viewmodel
|
|||
@Override |
|||
public void onTopicTaskCompleted(TopicTaskResult result) { |
|||
topicTaskResult.setValue(result); |
|||
if (result.getResultCode() == TopicTask.ResultCode.SUCCESS) { |
|||
isUserExtraInfoVisibile.clear(); |
|||
for (int i = 0; i < result.getNewPostsList().size(); i++) { |
|||
isUserExtraInfoVisibile.add(false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onPrepareForReplyFinished(PrepareForReplyResult result) { |
|||
writingReply = true; |
|||
prepareForReplyResult.setValue(result); |
|||
} |
|||
|
|||
@Override |
|||
public void onPrepareEditFinished(PrepareForEditResult result, int position) { |
|||
editingPost = true; |
|||
postBeingEditedPosition = position; |
|||
prepareForEditResult.setValue(result); |
|||
} |
|||
|
|||
// <-------------Just getters, setters and helper methods below here---------------->
|
|||
|
|||
public boolean isUserExtraInfoVisible(int position) { |
|||
return isUserExtraInfoVisibile.get(position); |
|||
} |
|||
|
|||
public void hideUserInfo(int position) { |
|||
isUserExtraInfoVisibile.set(position, false); |
|||
} |
|||
|
|||
public void toggleUserInfo(int position) { |
|||
isUserExtraInfoVisibile.set(position, !isUserExtraInfoVisibile.get(position)); |
|||
} |
|||
|
|||
public ArrayList<Integer> getToQuoteList() { |
|||
return toQuoteList; |
|||
} |
|||
|
|||
public void postIndexToggle(Integer postIndex) { |
|||
if (toQuoteList.contains(postIndex)) |
|||
toQuoteList.remove(postIndex); |
|||
else |
|||
toQuoteList.add(postIndex); |
|||
} |
|||
|
|||
public void setTopicTaskObserver(TopicTask.TopicTaskObserver topicTaskObserver) { |
|||
this.topicTaskObserver = topicTaskObserver; |
|||
} |
|||
|
|||
public void setDeleteTaskCallbacks(DeleteTask.DeleteTaskCallbacks deleteTaskCallbacks) { |
|||
this.deleteTaskCallbacks = deleteTaskCallbacks; |
|||
} |
|||
|
|||
public void setReplyFinishListener(ReplyTask.ReplyTaskCallbacks 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<TopicTaskResult> getTopicTaskResult() { |
|||
return topicTaskResult; |
|||
} |
|||
|
|||
public MutableLiveData<PrepareForReplyResult> getPrepareForReplyResult() { |
|||
return prepareForReplyResult; |
|||
} |
|||
|
|||
public MutableLiveData<PrepareForEditResult> getPrepareForEditResult() { |
|||
return prepareForEditResult; |
|||
} |
|||
|
|||
public void setEditingPost(boolean editingPost) { |
|||
this.editingPost = editingPost; |
|||
} |
|||
|
|||
public boolean isEditingPost() { |
|||
return editingPost; |
|||
} |
|||
|
|||
public int getPostBeingEditedPosition() { |
|||
return postBeingEditedPosition; |
|||
} |
|||
|
|||
public boolean canReply() { |
|||
return topicTaskResult.getValue() != null && topicTaskResult.getValue().getReplyPageUrl() != null; |
|||
} |
|||
|
|||
public boolean isWritingReply() { |
|||
return writingReply; |
|||
} |
|||
|
|||
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 { |
|||
// topic task has not finished yet (log? disable menu button until load is finished?)
|
|||
return firstTopicUrl; |
|||
} |
|||
} |
|||
|
|||
public String getTopicTitle() { |
|||
if (topicTaskResult.getValue() == null) |
|||
throw new NullPointerException("Topic task has not finished yet!"); |
|||
return topicTaskResult.getValue().getTopicTitle(); |
|||
} |
|||
|
|||
public int getCurrentPageIndex() { |
|||
if (topicTaskResult.getValue() == null) |
|||
throw new NullPointerException("No page has been loaded yet!"); |
|||
return topicTaskResult.getValue().getCurrentPageIndex(); |
|||
} |
|||
|
|||
public int getPageCount() { |
|||
if (topicTaskResult.getValue() == null) |
|||
throw new NullPointerException("No page has been loaded yet!"); |
|||
|
|||
return topicTaskResult.getValue().getPageCount(); |
|||
} |
|||
|
|||
public String getPostBeingEditedText() { |
|||
if (prepareForEditResult.getValue() == null) |
|||
throw new NullPointerException("Edit preparation was not found!"); |
|||
return prepareForEditResult.getValue().getPostText(); |
|||
} |
|||
|
|||
public String getBuildedQuotes() { |
|||
if (prepareForReplyResult.getValue() != null) { |
|||
return prepareForReplyResult.getValue().getBuildedQuotes(); |
|||
} else { |
|||
return ""; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,5 @@ |
|||
<vector android:height="24dp" android:tint="#FFFFFF" |
|||
android:viewportHeight="24.0" android:viewportWidth="24.0" |
|||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
|||
<path android:fillColor="#FF000000" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/> |
|||
</vector> |
@ -0,0 +1,118 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginBottom="5dp" |
|||
android:paddingEnd="4dp" |
|||
android:paddingStart="4dp"> |
|||
|
|||
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto" |
|||
android:id="@+id/card_view" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:layout_gravity="center" |
|||
android:foreground="?android:attr/selectableItemBackground" |
|||
card_view:cardBackgroundColor="@color/card_background" |
|||
card_view:cardCornerRadius="5dp" |
|||
card_view:cardElevation="2dp" |
|||
card_view:cardPreventCornerOverlap="false" |
|||
card_view:cardUseCompatPadding="true"> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:orientation="vertical"> |
|||
|
|||
<RelativeLayout |
|||
android:id="@+id/header" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="0dp" |
|||
android:layout_weight="1" |
|||
android:clickable="true" |
|||
android:focusable="true" |
|||
android:paddingLeft="16dp" |
|||
android:paddingRight="16dp" |
|||
android:paddingTop="16dp"> |
|||
|
|||
<FrameLayout |
|||
android:id="@+id/thumbnail_holder" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_alignParentTop="true" |
|||
android:layout_centerVertical="true" |
|||
android:layout_marginEnd="16dp"> |
|||
|
|||
<ImageView |
|||
android:id="@+id/thumbnail" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="center" |
|||
android:adjustViewBounds="true" |
|||
android:contentDescription="@string/post_thumbnail" |
|||
android:maxHeight="@dimen/thumbnail_size" |
|||
android:maxWidth="@dimen/thumbnail_size" |
|||
app:srcCompat="@drawable/ic_default_user_thumbnail_white_24dp" /> |
|||
</FrameLayout> |
|||
|
|||
<TextView |
|||
android:id="@+id/username" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_alignParentTop="true" |
|||
android:layout_toEndOf="@+id/thumbnail_holder" |
|||
android:ellipsize="end" |
|||
android:maxLines="1" |
|||
android:text="@string/post_author" |
|||
android:textColor="@color/primary_text" |
|||
android:textStyle="bold" /> |
|||
|
|||
<EditText |
|||
android:id="@+id/edit_message_subject" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_below="@+id/username" |
|||
android:layout_toEndOf="@+id/thumbnail_holder" |
|||
android:hint="@string/subject" |
|||
android:inputType="textMultiLine" |
|||
android:maxLength="80" |
|||
android:textSize="10sp" |
|||
tools:ignore="SmallSp" /> |
|||
</RelativeLayout> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:orientation="horizontal" |
|||
android:paddingLeft="16dp" |
|||
android:paddingRight="16dp"> |
|||
|
|||
<android.support.design.widget.TextInputLayout |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:layout_weight="1" |
|||
android:orientation="vertical"> |
|||
|
|||
<EditText |
|||
android:id="@+id/edit_message_text" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:hint="@string/post_message" |
|||
android:inputType="textMultiLine" /> |
|||
</android.support.design.widget.TextInputLayout> |
|||
|
|||
<android.support.v7.widget.AppCompatImageButton |
|||
android:id="@+id/edit_message_submit" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="bottom" |
|||
android:layout_marginBottom="5dp" |
|||
android:layout_marginEnd="5dp" |
|||
android:background="@color/card_background" |
|||
android:contentDescription="@string/submit" |
|||
app:srcCompat="@drawable/ic_send_accent_24dp" /> |
|||
</LinearLayout> |
|||
</LinearLayout> |
|||
</android.support.v7.widget.CardView> |
|||
</FrameLayout> |
Loading…
Reference in new issue