Browse Source

viewmodel progress

pull/34/head
Thodoris1999 7 years ago
parent
commit
d4c097f654
  1. 6
      app/build.gradle
  2. 4
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/GetQuoteTextTask.java
  3. 387
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  4. 213
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicTask.java
  5. 87
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicTaskResult.java
  6. 6
      app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
  7. 76
      app/src/main/java/gr/thmmy/mthmmy/utils/HTMLUtils.java
  8. 18
      app/src/main/java/gr/thmmy/mthmmy/viewmodel/BaseViewModel.java
  9. 40
      app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java

6
app/build.gradle

@ -25,6 +25,11 @@ android {
archivesBaseName = archivesBaseName + "-$date"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
@ -56,6 +61,7 @@ dependencies {
implementation 'com.jakewharton.timber:timber:4.7.0'
implementation 'net.gotev:uploadservice:3.4.2'
implementation 'net.gotev:uploadservice-okhttp:3.4.2'
implementation 'android.arch.lifecycle:extensions:1.1.1'
}
apply plugin: 'com.google.gms.google-services'

4
app/src/main/java/gr/thmmy/mthmmy/activities/topic/GetQuoteTextTask.java

@ -0,0 +1,4 @@
package gr.thmmy.mthmmy.activities.topic;
public class GetQuoteTextTask {
}

387
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java

@ -2,6 +2,8 @@ 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;
@ -52,8 +54,10 @@ 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;
@ -130,14 +134,10 @@ public class TopicActivity extends BaseActivity {
* Holds a list of this topic's posts
*/
private ArrayList<Post> postsList;
/**
* Gets assigned to {@link #postFocus} when there is no post focus information in the url
*/
private static final int NO_POST_FOCUS = -1;
/**
* Holds the index of the post that has focus
*/
private int postFocus = NO_POST_FOCUS;
/**
* Holds the position in the {@link #postsList} of the post with focus
*/
@ -195,6 +195,7 @@ public class TopicActivity extends BaseActivity {
private TextView pageIndicator;
private ImageButton nextPage;
private ImageButton lastPage;
private TopicViewModel viewModel;
//Topic's info related
private SpannableStringBuilder topicTreeAndMods = new SpannableStringBuilder("Loading..."),
@ -209,6 +210,7 @@ public class TopicActivity extends BaseActivity {
Bundle extras = getIntent().getExtras();
topicTitle = extras.getString(BUNDLE_TOPIC_TITLE);
String maybeTopicTitle = topicTitle;
topicPageUrl = extras.getString(BUNDLE_TOPIC_URL);
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(
Uri.parse(topicPageUrl));
@ -262,7 +264,7 @@ public class TopicActivity extends BaseActivity {
CustomLinearLayoutManager layoutManager = new CustomLinearLayoutManager(
getApplicationContext(), loadedPageUrl);
recyclerView.setLayoutManager(layoutManager);
topicAdapter = new TopicAdapter(this, postsList, base_url, topicTask);
topicAdapter = new TopicAdapter(this, postsList, viewModel.getTopicTaskResultMutableLiveData().getValue().getBaseUrl(), topicTask);
recyclerView.setAdapter(topicAdapter);
replyFAB = findViewById(R.id.topic_fab);
@ -295,8 +297,82 @@ public class TopicActivity extends BaseActivity {
paginationEnabled(false);
//Gets posts
topicTask = new TopicTask();
topicTask.execute(topicPageUrl); //Attempt data parsing
//topicTask = new TopicTask();
//topicTask.execute(topicPageUrl); //Attempt data parsing
viewModel = ViewModelProviders.of(this).get(TopicViewModel.class);
viewModel.getTopicTaskResultMutableLiveData().observe(this, topicTaskResult -> {
if (topicTaskResult == null) {
hideControls();
} else {
switch (topicTaskResult.getResultCode()) {
case SUCCESS:
if (topicTitle == null || Objects.equals(topicTitle, "")
|| !Objects.equals(topicTitle, topicTaskResult.getTopicTitle())) {
toolbarTitle.setText(topicTaskResult.getTopicTitle());
}
if (!postsList.isEmpty()) {
recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug
postsList.clear();
if (topicTitle != null) toolbarTitle.setText(topicTitle);
topicAdapter.notifyItemRangeRemoved(0, postsList.size() - 1);
}
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));
pageRequestValue = topicTaskResult.getCurrentPageIndex();
if (topicTaskResult.getCurrentPageIndex() == topicTaskResult.getPageCount()) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null)
notificationManager.cancel(NEW_POST_TAG, loadedPageTopicId);
}
showControls();
case NETWORK_ERROR:
Toast.makeText(getBaseContext(), "Network Error", Toast.LENGTH_SHORT).show();
break;
case SAME_PAGE:
showControls();
Toast.makeText(getBaseContext(), "That's the same page", Toast.LENGTH_SHORT).show();
//TODO change focus
break;
case UNAUTHORIZED:
showControls();
Toast.makeText(getBaseContext(), "This topic is either missing or off limits to you", Toast.LENGTH_SHORT).show();
break;
default:
//Parse failed - should never happen
Timber.d("Parse failed!"); //TODO report ParseException!!!
Toast.makeText(getBaseContext(), "Fatal Error", Toast.LENGTH_SHORT).show();
finish();
break;
}
}
});
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) {
replyFAB.hide();
topicAdapter.resetTopic(viewModel.getTopicTaskResultMutableLiveData().getValue().getBaseUrl(), new TopicTask(), false);
} else topicAdapter.resetTopic(viewModel.getTopicTaskResultMutableLiveData().getValue().getBaseUrl(), new TopicTask(), true);
paginationEnabled(true);
if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true);
}
@Override
@ -321,11 +397,22 @@ 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(topicTreeAndMods);
//treeAndMods.setText(new SpannableStringBuilder("Loading..."));
treeAndMods.setMovementMethod(LinkMovementMethod.getInstance());
TextView usersViewing = infoDialog.findViewById(R.id.users_viewing);
usersViewing.setText(topicViewers);
//usersViewing.setText(new SpannableStringBuilder("Loading..."));
usersViewing.setMovementMethod(LinkMovementMethod.getInstance());
viewModel.getTopicTaskResultMutableLiveData().observe(this, topicTaskResult -> {
if (topicTaskResult == null) {
usersViewing.setText(new SpannableStringBuilder("Loading..."));
treeAndMods.setText(new SpannableStringBuilder("Loading..."));
} else {
String treeAndModsString = topicTaskResult.getTopicTreeAndMods();
treeAndMods.setText(HTMLUtils.getSpannableFromHtml(this, treeAndModsString));
String topicViewersString = topicTaskResult.getTopicViewers();
usersViewing.setText(HTMLUtils.getSpannableFromHtml(this, topicViewersString));
}
});
builder.setView(infoDialog);
AlertDialog dialog = builder.create();
@ -336,7 +423,7 @@ public class TopicActivity extends BaseActivity {
sendIntent.setType("text/plain");
sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, topicPageUrl);
startActivity(Intent.createChooser(sendIntent, "Share via"));
return true;
return true; //invalidateOptionsMenu();
default:
return super.onOptionsItemSelected(item);
}
@ -564,282 +651,6 @@ public class TopicActivity extends BaseActivity {
}
//------------------------------------BOTTOM NAV BAR METHODS END------------------------------------
private enum ResultCode {
SUCCESS, NETWORK_ERROR, PARSING_ERROR, OTHER_ERROR, SAME_PAGE, UNAUTHORIZED
}
/**
* 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>
*/
class TopicTask extends AsyncTask<String, Void, ResultCode> {
ArrayList<Post> localPostsList;
@Override
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
paginationEnabled(false);
if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(false);
}
protected ResultCode doInBackground(String... strings) {
Document document = null;
String newPageUrl = strings[0];
//Finds the index of message focus if present
{
postFocus = NO_POST_FOCUS;
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("#")));
}
}
//Checks if the page to be loaded is the one already shown
if (!reloadingPage && !Objects.equals(loadedPageUrl, "") && newPageUrl.contains(base_url)) {
if (newPageUrl.contains("topicseen#new") || newPageUrl.contains("#new"))
if (thisPage == numberOfPages)
return ResultCode.SAME_PAGE;
if (newPageUrl.contains("msg")) {
String tmpUrlSbstr = newPageUrl.substring(newPageUrl.indexOf("msg") + 3);
if (tmpUrlSbstr.contains("msg"))
tmpUrlSbstr = tmpUrlSbstr.substring(0, tmpUrlSbstr.indexOf("msg") - 1);
int testAgainst = Integer.parseInt(tmpUrlSbstr);
for (Post post : postsList) {
if (post.getPostIndex() == testAgainst) {
return ResultCode.SAME_PAGE;
}
}
} else if ((Objects.equals(newPageUrl, base_url) && thisPage == 1) ||
Integer.parseInt(newPageUrl.substring(base_url.length() + 1)) / 15 + 1 == thisPage)
return ResultCode.SAME_PAGE;
} else if (!Objects.equals(loadedPageUrl, "")) topicTitle = null;
if (reloadingPage) reloadingPage = !reloadingPage;
loadedPageUrl = newPageUrl;
if (strings[0].substring(0, strings[0].lastIndexOf(".")).contains("topic="))
base_url = strings[0].substring(0, strings[0].lastIndexOf(".")); //New topic's base url
replyPageUrl = null;
Request request = new Request.Builder()
.url(newPageUrl)
.build();
try {
Response response = client.newCall(request).execute();
document = Jsoup.parse(response.body().string());
localPostsList = parse(document);
loadedPageTopicId = Integer.parseInt(ThmmyPage.getTopicId(loadedPageUrl));
//Finds the position of the focused message if present
for (int i = 0; i < localPostsList.size(); ++i) {
if (localPostsList.get(i).getPostIndex() == postFocus) {
postFocusPosition = i;
break;
}
}
return ResultCode.SUCCESS;
} catch (IOException e) {
Timber.i(e, "IO Exception");
return ResultCode.NETWORK_ERROR;
} catch (ParseException e) {
if (isUnauthorized(document))
return ResultCode.UNAUTHORIZED;
Timber.e(e, "Parsing Error");
return ResultCode.PARSING_ERROR;
} catch (Exception e) {
Timber.e(e, "Exception");
return ResultCode.OTHER_ERROR;
}
}
protected void onPostExecute(ResultCode parseResult) {
switch (parseResult) {
case SUCCESS:
if (topicTitle == null || Objects.equals(topicTitle, "")
|| !Objects.equals(topicTitle, parsedTitle)) {
toolbarTitle.setText(parsedTitle);
topicTitle = parsedTitle;
thisPageBookmark = new Bookmark(parsedTitle, Integer.toString(loadedPageTopicId), true);
invalidateOptionsMenu();
}
if (!postsList.isEmpty()) {
recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug
postsList.clear();
topicAdapter.notifyItemRangeRemoved(0, postsList.size() - 1);
}
postsList.addAll(localPostsList);
topicAdapter.notifyItemRangeInserted(0, postsList.size());
topicAdapter.prepareForDelete(new DeleteTask());
topicAdapter.prepareForPrepareForEdit(new PrepareForEdit());
progressBar.setVisibility(ProgressBar.INVISIBLE);
if (replyPageUrl == null) {
replyFAB.hide();
topicAdapter.resetTopic(base_url, new TopicTask(), false);
} else topicAdapter.resetTopic(base_url, new TopicTask(), true);
if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true);
//Set current page
pageIndicator.setText(String.valueOf(thisPage) + "/" + String.valueOf(numberOfPages));
pageRequestValue = thisPage;
if (thisPage == numberOfPages) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null)
notificationManager.cancel(NEW_POST_TAG, loadedPageTopicId);
}
paginationEnabled(true);
break;
case NETWORK_ERROR:
Toast.makeText(getBaseContext(), "Network Error", Toast.LENGTH_SHORT).show();
break;
case SAME_PAGE:
stopLoading();
Toast.makeText(getBaseContext(), "That's the same page", Toast.LENGTH_SHORT).show();
//TODO change focus
break;
case UNAUTHORIZED:
stopLoading();
Toast.makeText(getBaseContext(), "This topic is either missing or off limits to you", Toast.LENGTH_SHORT).show();
break;
default:
//Parse failed - should never happen
Timber.d("Parse failed!"); //TODO report ParseException!!!
Toast.makeText(getBaseContext(), "Fatal Error", Toast.LENGTH_SHORT).show();
finish();
break;
}
}
private void stopLoading() {
progressBar.setVisibility(ProgressBar.INVISIBLE);
if (replyPageUrl == null) {
replyFAB.hide();
topicAdapter.resetTopic(base_url, new TopicTask(), false);
} else topicAdapter.resetTopic(base_url, new TopicTask(), true);
if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true);
paginationEnabled(true);
}
/**
* All the parsing a topic needs.
*
* @param topic {@link Document} object containing this topic's source code
* @see org.jsoup.Jsoup Jsoup
*/
private ArrayList<Post> parse(Document topic) throws ParseException {
try {
ParseHelpers.Language language = ParseHelpers.Language.getLanguage(topic);
//Finds topic's tree, mods and users viewing
{
topicTreeAndMods = getSpannableFromHtml(topic.select("div.nav").first().html());
topicViewers = getSpannableFromHtml(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
{
parsedTitle = topic.select("td[id=top_subject]").first().text();
if (parsedTitle.contains("Topic:")) {
parsedTitle = parsedTitle.substring(parsedTitle.indexOf("Topic:") + 7
, parsedTitle.indexOf("(Read") - 2);
} else {
parsedTitle = parsedTitle.substring(parsedTitle.indexOf("Θέμα:") + 6
, parsedTitle.indexOf("(Αναγνώστηκε") - 2);
Timber.d("Parsed title: %s", parsedTitle);
}
}
{ //Finds current page's index
thisPage = TopicParser.parseCurrentPageIndex(topic, language);
}
{ //Finds number of pages
numberOfPages = TopicParser.parseTopicNumberOfPages(topic, thisPage, language);
for (int i = 0; i < numberOfPages; i++) {
//Generate each page's url from topic's base url +".15*numberOfPage"
pagesUrls.put(i, base_url + "." + String.valueOf(i * 15));
}
}
return TopicParser.parseTopic(topic, language);
} catch (Exception e) {
throw new ParseException("Parsing failed (TopicTask)");
}
}
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;
}
private void makeLinkClickable(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(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);
getApplicationContext().startActivity(intent);
} else if (target.is(ThmmyPage.PageCategory.PROFILE)) {
Intent intent = new Intent(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);
getApplicationContext().startActivity(intent);
} else if (target.is(ThmmyPage.PageCategory.INDEX))
finish();
}
};
strBuilder.setSpan(clickable, start, end, flags);
strBuilder.removeSpan(span);
}
private SpannableStringBuilder getSpannableFromHtml(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(strBuilder, span);
}
return strBuilder;
}
}
class PrepareForReply extends AsyncTask<ArrayList<Integer>, Void, Boolean> {
String numReplies, seqnum, sc, topic, buildedQuotes = "";
@ -976,7 +787,7 @@ public class TopicActivity extends BaseActivity {
if (result) {
topicTask = new TopicTask();
if ((postsList.get(postsList.size() - 1).getPostNumber() + 1) % 15 == 0)
topicTask.execute(base_url + "." + 2147483647);
topicTask.execute(viewModel.getTopicTaskResultMutableLiveData().getValue().getBaseUrl() + "." + 2147483647);
else {
reloadingPage = true;
topicTask.execute(loadedPageUrl);

213
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicTask.java

@ -0,0 +1,213 @@
package gr.thmmy.mthmmy.activities.topic;
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.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 gr.thmmy.mthmmy.viewmodel.TopicViewModel;
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> {
//input data
private TopicViewModel viewModel;
private boolean reloadingPage;
private ArrayList<Post> lastPostsList;
//output data
private ResultCode resultCode;
private String topicTitle, replyPageUrl, topicTreeAndMods, topicViewers;
private ArrayList<Post> newPostsList;
private int loadedPageTopicId = -1;
private int focusedPostIndex = 0;
private SparseArray<String> pagesUrls = new SparseArray<>();
//(possibly) update data
private int currentPageIndex, pageCount;
private String baseUrl, lastPageLoadAttemptedUrl;
//consecutive load constructor
public TopicTask(boolean reloadingPage, String baseUrl, int currentPageIndex, int pageCount,
String lastPageLoadAttemptedUrl, ArrayList<Post> lastPostsList) {
this.viewModel = viewModel;
this.reloadingPage = reloadingPage;
this.baseUrl = baseUrl;
this.currentPageIndex = currentPageIndex;
this.pageCount = pageCount;
this.lastPageLoadAttemptedUrl = lastPageLoadAttemptedUrl;
this.lastPostsList = lastPostsList;
}
//first load constructor
public TopicTask() {
this.viewModel = viewModel;
this.reloadingPage = false;
this.baseUrl = "";
this.currentPageIndex = 1;
this.pageCount = 1;
this.lastPageLoadAttemptedUrl = "";
this.lastPostsList = null;
}
@Override
protected TopicTaskResult doInBackground(String... strings) {
Document document = null;
String newPageUrl = strings[0];
//Finds the index of message focus if present
int postFocus = -1;
{
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("#")));
}
}
//Checks if the page to be loaded is the one already shown
if (!reloadingPage && !Objects.equals(lastPageLoadAttemptedUrl, "") && newPageUrl.contains(baseUrl)) {
if (newPageUrl.contains("topicseen#new") || newPageUrl.contains("#new"))
if (currentPageIndex == pageCount)
resultCode = ResultCode.SAME_PAGE;
if (newPageUrl.contains("msg")) {
String tmpUrlSbstr = newPageUrl.substring(newPageUrl.indexOf("msg") + 3);
if (tmpUrlSbstr.contains("msg"))
tmpUrlSbstr = tmpUrlSbstr.substring(0, tmpUrlSbstr.indexOf("msg") - 1);
int testAgainst = Integer.parseInt(tmpUrlSbstr);
for (Post post : lastPostsList) {
if (post.getPostIndex() == testAgainst) {
resultCode = ResultCode.SAME_PAGE;
}
}
} 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="))
baseUrl = strings[0].substring(0, strings[0].lastIndexOf(".")); //New topic's base url
replyPageUrl = null;
Request request = new Request.Builder()
.url(newPageUrl)
.build();
try {
Response response = BaseApplication.getInstance().getClient().newCall(request).execute();
document = Jsoup.parse(response.body().string());
newPostsList = parse(document);
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 (ParseException e) {
if (isUnauthorized(document))
resultCode = ResultCode.UNAUTHORIZED;
Timber.e(e, "Parsing Error");
resultCode = ResultCode.PARSING_ERROR;
} catch (Exception e) {
Timber.e(e, "Exception");
resultCode = ResultCode.OTHER_ERROR;
}
return new TopicTaskResult(resultCode, baseUrl, topicTitle, replyPageUrl, newPostsList,
loadedPageTopicId, currentPageIndex, pageCount, focusedPostIndex, topicTreeAndMods,
topicViewers, lastPageLoadAttemptedUrl, pagesUrls);
}
/**
* All the parsing a topic needs.
*
* @param topic {@link Document} object containing this topic's source code
* @see org.jsoup.Jsoup Jsoup
*/
private ArrayList<Post> parse(Document topic) throws ParseException {
try {
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));
}
}
return TopicParser.parseTopic(topic, language);
} catch (Exception e) {
throw new ParseException("Parsing failed (TopicTask)");
}
}
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;
}
public enum ResultCode {
SUCCESS, NETWORK_ERROR, PARSING_ERROR, OTHER_ERROR, SAME_PAGE, UNAUTHORIZED
}
public interface OnTopicTaskCompleted {
void onTopicTaskCompleted(TopicTaskResult result);
}
}

87
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicTaskResult.java

@ -0,0 +1,87 @@
package gr.thmmy.mthmmy.activities.topic;
import android.util.SparseArray;
import java.util.ArrayList;
import gr.thmmy.mthmmy.model.Post;
public class TopicTaskResult {
private final TopicTask.ResultCode resultCode;
private final String baseUrl, topicTitle, replyPageUrl;
private final ArrayList<Post> newPostsList;
private final int loadedPageTopicId, currentPageIndex, pageCount, focusedPostIndex;
private final String topicTreeAndMods, topicViewers, 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;
}
}

6
app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java

@ -2,6 +2,7 @@ package gr.thmmy.mthmmy.base;
import android.Manifest;
import android.app.ProgressDialog;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@ -54,6 +55,7 @@ import gr.thmmy.mthmmy.model.ThmmyFile;
import gr.thmmy.mthmmy.services.DownloadHelper;
import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.FileUtils;
import gr.thmmy.mthmmy.viewmodel.BaseViewModel;
import okhttp3.OkHttpClient;
import timber.log.Timber;
@ -118,6 +120,10 @@ public abstract class BaseActivity extends AppCompatActivity {
loadSavedBookmarks();
}
BaseViewModel baseViewModel = ViewModelProviders.of(this).get(BaseViewModel.class);
baseViewModel.getCurrentPageBookmark().observe(this, thisPageBookmark -> {
setTopicBookmark(thisPageBookmarkMenuButton);
});
}
@Override

76
app/src/main/java/gr/thmmy/mthmmy/utils/HTMLUtils.java

@ -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);
}
}

18
app/src/main/java/gr/thmmy/mthmmy/viewmodel/BaseViewModel.java

@ -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;
}
}

40
app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java

@ -0,0 +1,40 @@
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 java.util.Objects;
import gr.thmmy.mthmmy.activities.topic.TopicActivity;
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 static gr.thmmy.mthmmy.services.NotificationService.NEW_POST_TAG;
public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTaskCompleted {
private MutableLiveData<TopicTaskResult> topicTaskResultMutableLiveData;
public MutableLiveData<TopicTaskResult> getTopicTaskResultMutableLiveData() {
if (topicTaskResultMutableLiveData == null) {
topicTaskResultMutableLiveData = new MutableLiveData<>();
//load topic data
}
return topicTaskResultMutableLiveData;
}
public void initialLoad(String pageUrl) {
new TopicTask().execute(pageUrl);
//load posts
}
@Override
public void onTopicTaskCompleted(TopicTaskResult result) {
topicTaskResultMutableLiveData.setValue(result);
}
}
Loading…
Cancel
Save