diff --git a/.gitignore b/.gitignore
index 3f32039e..8b2da229 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ bin/
gen/
out/
output.json
+output-metadata.json
# Gradle files
.gradle/
diff --git a/README.md b/README.md
index fc28062a..96b10849 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
# mTHMMY
+[![GitHub release](https://img.shields.io/github/release/ThmmyNoLife/mTHMMY.svg)](https://github.com/ThmmyNoLife/mTHMMY/releases)
[![API](https://img.shields.io/badge/API-19%2B-blue.svg?style=flat)](https://android-arsenal.com/api?level=19)
[![Discord Channel](https://img.shields.io/badge/discord-public@mTHMMY-738bd7.svg?style=flat)][discord-server]
![Last Commit](https://img.shields.io/github/last-commit/ThmmyNoLife/mTHMMY/develop.svg?style=flat)
diff --git a/app/build.gradle b/app/build.gradle
index afd40760..603d2651 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -3,19 +3,20 @@ import groovy.json.JsonSlurper
apply from: 'gradle/grgit.gradle'
apply plugin: 'com.android.application'
-apply plugin: 'io.fabric'
+apply plugin: 'com.google.gms.google-services'
+apply plugin: 'com.google.firebase.crashlytics'
android {
compileSdkVersion 29
buildToolsVersion = '29.0.3'
defaultConfig {
- vectorDrawables.useSupportLibrary = true
+ vectorDrawables.useSupportLibrary = true //TODO: Remove when minSdkVersion >= 21
applicationId "gr.thmmy.mthmmy"
minSdkVersion 19
targetSdkVersion 29
- versionCode 24
- versionName "1.8.1"
+ versionCode 25
+ versionName "1.8.2"
archivesBaseName = "mTHMMY-v$versionName"
buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\""
buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\""
@@ -24,17 +25,19 @@ android {
buildTypes {
release {
+ multiDexEnabled true //TODO: Remove when minSdkVersion >= 21
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
multiDexKeepProguard file('proguard-rules.pro') //TODO: Remove when minSdkVersion >= 21
}
debug {
- multiDexEnabled true
+ multiDexEnabled true //TODO: Remove when minSdkVersion >= 21
def date = new Date().format('ddMMyy_HHmmss')
archivesBaseName = archivesBaseName + "-$date"
- // Disable fabric build ID generation for debug builds
- ext.enableCrashlytics = false
+ firebaseCrashlytics {
+ mappingFileUploadEnabled false // Disable mapping file uploading for debug builds
+ }
}
}
@@ -75,7 +78,7 @@ tasks.whenTaskAdded { task ->
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(":emojis")
- implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.appcompat:appcompat:1.3.0-alpha01'
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
@@ -86,9 +89,9 @@ dependencies {
implementation 'androidx.exifinterface:exifinterface:1.2.0'
implementation 'androidx.multidex:multidex:2.0.1' //TODO: Remove when minSdkVersion >= 21
implementation 'com.google.android.material:material:1.1.0'
- implementation 'com.google.firebase:firebase-analytics:17.4.3'
- implementation 'com.google.firebase:firebase-messaging:20.2.0'
- implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
+ implementation 'com.google.firebase:firebase-analytics:17.4.4'
+ implementation 'com.google.firebase:firebase-crashlytics:17.1.1'
+ implementation 'com.google.firebase:firebase-messaging:20.2.3'
implementation 'com.snatik:storage:2.1.0'
implementation ('com.squareup.okhttp3:okhttp:3.12.12') { //TODO: Warning: OkHttp has dropped support for Android 19 since OkHttp 3.13!
force = true //TODO: Remove when minSdkVersion >= 21
@@ -116,5 +119,3 @@ dependencies {
testImplementation 'org.powermock:powermock-api-mockito2:2.0.2'
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
}
-
-apply plugin: 'com.google.gms.google-services'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e36ad051..7c7ab811 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -7,6 +7,7 @@
+
@@ -19,7 +20,7 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:requestLegacyExternalStorage="true"
- android:networkSecurityConfig="@xml/network_security_config"
+ android:usesCleartextTraffic="true"
android:theme="@style/AppTheme"
tools:ignore="UnusedAttribute">
table>tbody>tr");
if (subBoardRows != null && !subBoardRows.isEmpty()) {
@@ -237,39 +239,61 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
if (!Objects.equals(subBoardRow.className(), "titlebg")) {
String pUrl = "", pTitle = "", pMods = "", pStats = "",
pLastPost = "No posts yet", pLastPostUrl = "";
+ boolean parsingFailed = false;
Elements subBoardColumns = subBoardRow.select(">td");
for (Element subBoardCol : subBoardColumns) {
- if (Objects.equals(subBoardCol.className(), "windowbg"))
+ if (Objects.equals(subBoardCol.className(), "windowbg")){
pStats = subBoardCol.text();
+ if(pStats.equals("--"))
+ pStats = "";
+ }
+
else if (Objects.equals(subBoardCol.className(), "smalltext")) {
pLastPost = subBoardCol.text();
- if (pLastPost.contains(" in ")) {
- pLastPost = pLastPost.substring(0, pLastPost.indexOf(" in ")) +
- "\n" +
- pLastPost.substring(pLastPost.indexOf(" in ") + 1, pLastPost.indexOf(" by ")) +
- "\n" +
- pLastPost.substring(pLastPost.lastIndexOf(" by ") + 1);
- pLastPostUrl = subBoardCol.select("a").first().attr("href");
- } else if (pLastPost.contains(" σε ")) {
- pLastPost = pLastPost.substring(0, pLastPost.indexOf(" σε ")) +
- "\n" +
- pLastPost.substring(pLastPost.indexOf(" σε ") + 1, pLastPost.indexOf(" από ")) +
- "\n" +
- pLastPost.substring(pLastPost.lastIndexOf(" από ") + 1);
- pLastPostUrl = subBoardCol.select("a").first().attr("href");
- } else {
- pLastPost = "No posts yet.";
- pLastPostUrl = "";
- }
+ if (pLastPost.contains(" in ") || pLastPost.contains(" σε ")) {
+ Pattern pattern = Pattern.compile("(?:Last post on |Τελευταίο μήνυμα στις )((?:(?!(?:in|σε)).)*)\\s(?:in|σε)\\s.*");
+ Matcher matcher = pattern.matcher(pLastPost);
+ if (matcher.find()){
+ String pLastPostDateTime = matcher.group(1);
+ String pSubject = subBoardCol.select("a").first().attr("title");
+
+ // Purification for extreme edge cases
+ String pSubjectConcat = subBoardCol.select("a").first().text();
+ pLastPost = pLastPost.replace(pSubjectConcat, "");
+
+ String pLastUser;
+ matcher = pLastPostPattern.matcher(pLastPost); //Don't even try simply grabbing , user might be guest
+ if (matcher.find())
+ pLastUser = matcher.group(2);
+ else {
+ parsingFailed = true;
+ break;
+ }
+
+ pLastPost = "Last post on: " + pLastPostDateTime + "\nin: " + pSubject + "\nby " +pLastUser;
+
+ pLastPostUrl = subBoardCol.select("a").first().attr("href");
+ }
+ else {
+ parsingFailed = true;
+ break;
+ }
+
+ } else if (pLastPost.contains("redirected clicks")||pLastPost.contains("N/A"))
+ pLastPost = "";
+ else
+ pLastPost = "No posts yet";
} else {
pUrl = subBoardCol.select("a").first().attr("href");
pTitle = subBoardCol.select("a").first().text();
- if (subBoardCol.select("div.smalltext").first() != null) {
+ if (subBoardCol.select("div.smalltext").first() != null)
pMods = subBoardCol.select("div.smalltext").first().text();
- }
}
}
- tempSubboards.add(new Board(pUrl, pTitle, pMods, pStats, pLastPost, pLastPostUrl));
+ if(!parsingFailed)
+ tempSubboards.add(new Board(pUrl, pTitle, pMods, pStats, pLastPost, pLastPostUrl));
+ else
+ Timber.e("Parsing failed (pLastPost came with: \"%s\", subBoardColumns html was \"%s\")", pLastPost, subBoardColumns);
}
}
}
@@ -282,30 +306,31 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
String pTopicUrl, pSubject, pStarter, pLastUser="", pLastPostDateTime="00:00:00", pLastPost, pLastPostUrl, pStats;
boolean pLocked = false, pSticky = false, pUnread = false;
Elements topicColumns = topicRow.select(">td");
- {
- Element column = topicColumns.get(2);
- Element tmp = column.select("span[id^=msg_] a").first();
- pTopicUrl = tmp.attr("href");
- pSubject = tmp.text();
- if (column.select("img[id^=stickyicon]").first() != null)
- pSticky = true;
- if (column.select("img[id^=lockicon]").first() != null)
- pLocked = true;
- if (column.select("a[id^=newicon]").first() != null)
- pUnread = true;
- }
+
+ Element column = topicColumns.get(2);
+ Element tmp = column.select("span[id^=msg_] a").first();
+ pTopicUrl = tmp.attr("href");
+ pSubject = tmp.text();
+ if (column.select("img[id^=stickyicon]").first() != null)
+ pSticky = true;
+ if (column.select("img[id^=lockicon]").first() != null)
+ pLocked = true;
+ if (column.select("a[id^=newicon]").first() != null)
+ pUnread = true;
+
pStarter = topicColumns.get(3).text();
pStats = "Replies: " + topicColumns.get(4).text() + ", Views: " + topicColumns.get(5).text();
pLastPost = topicColumns.last().text();
- Pattern pattern = Pattern.compile("(.+)\\s(by|από)\\s(.+)$");
- Matcher matcher = pattern.matcher(pLastPost);
+ Matcher matcher = pLastPostPattern.matcher(pLastPost);
if (matcher.find()){
pLastPostDateTime = matcher.group(1);
- pLastUser = matcher.group(3);
+ pLastUser = matcher.group(2);
+ }
+ else{
+ Timber.e("Parsing failed (pLastPost came with: \"%s\", topicColumns html was \"%s\")", pLastPost, topicColumns);
+ continue;
}
- else
- throw new ParseException("Parsing failed (pLastPost came with: \"" + pLastPost + "\")");
pLastPostUrl = topicColumns.last().select("a:has(img)").first().attr("href");
tempTopics.add(new Topic(pTopicUrl, pSubject, pStarter, pLastUser, pLastPostDateTime, pLastPostUrl,
@@ -323,7 +348,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
|| !Objects.equals(boardTitle, parsedTitle)) {
boardTitle = parsedTitle;
toolbar.setTitle(boardTitle);
- thisPageBookmark = new Bookmark(boardTitle, ThmmyPage.getBoardId(boardUrl), true);
+ thisPageBookmark = new Bookmark(boardTitle, thisPageBookmark.getId(), thisPageBookmark.isNotificationsEnabled());
setBoardBookmark(findViewById(R.id.bookmark));
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java
index c508fdc2..0a697706 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java
@@ -161,9 +161,25 @@ class BoardAdapter extends RecyclerView.Adapter {
boardExpandableVisibility.set(subBoardViewHolder.getAdapterPosition() - 1, !visible);
});
subBoardViewHolder.boardTitle.setText(subBoard.getTitle());
- subBoardViewHolder.boardMods.setText(subBoard.getMods());
- subBoardViewHolder.boardStats.setText(subBoard.getStats());
- subBoardViewHolder.boardLastPost.setText(subBoard.getLastPost());
+ String mods = subBoard.getMods();
+ String stats = subBoard.getStats();
+ String lastPost = subBoard.getLastPost();
+
+ if(!mods.isEmpty()){
+ subBoardViewHolder.boardMods.setText(mods);
+ subBoardViewHolder.boardMods.setVisibility(View.VISIBLE);
+ }
+
+ if(!stats.isEmpty()){
+ subBoardViewHolder.boardStats.setText(stats);
+ subBoardViewHolder.boardStats.setVisibility(View.VISIBLE);
+ }
+
+ if(!lastPost.isEmpty()){
+ subBoardViewHolder.boardLastPost.setText(lastPost);
+ subBoardViewHolder.boardLastPost.setVisibility(View.VISIBLE);
+ }
+
if (!Objects.equals(subBoard.getLastPostUrl(), "")) {
subBoardViewHolder.boardLastPost.setOnClickListener(view -> {
Intent intent = new Intent(context, TopicActivity.class);
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java
index ef2b79b6..7054db43 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java
@@ -26,8 +26,6 @@ import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_URL;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_TITLE;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
-//TODO proper handling with adapter etc.
-//TODO after clicking bookmark and then back button should return to this activity
public class BookmarksActivity extends BaseActivity {
private static final String TOPIC_URL = "https://www.thmmy.gr/smf/index.php?topic=";
private static final String BOARD_URL = "https://www.thmmy.gr/smf/index.php?board=";
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksFragment.java
index 23562e0d..cdd6f714 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksFragment.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.model.Bookmark;
+//TODO refactor using RecyclerView
public class BookmarksFragment extends Fragment {
enum Type {TOPIC, BOARD}
private static final String ARG_SECTION_NUMBER = "SECTION_NUMBER";
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java
index 061db9d2..3344d089 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java
@@ -30,7 +30,7 @@ import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.Board;
import gr.thmmy.mthmmy.model.Category;
import gr.thmmy.mthmmy.session.SessionManager;
-import gr.thmmy.mthmmy.utils.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
import gr.thmmy.mthmmy.utils.parsing.NewParseTask;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.views.CustomRecyclerView;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java
index 5b27c570..625f7440 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java
@@ -26,7 +26,7 @@ import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary;
import gr.thmmy.mthmmy.session.SessionManager;
-import gr.thmmy.mthmmy.utils.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
import gr.thmmy.mthmmy.utils.parsing.NewParseTask;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.views.CustomRecyclerView;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
index c3088094..661ed1be 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
@@ -26,18 +26,17 @@ import java.util.ArrayList;
import java.util.List;
import gr.thmmy.mthmmy.R;
-import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary;
+import gr.thmmy.mthmmy.session.InvalidSessionException;
+import gr.thmmy.mthmmy.session.MarkAsReadTask;
import gr.thmmy.mthmmy.session.SessionManager;
-import gr.thmmy.mthmmy.session.ValidateSessionTask;
-import gr.thmmy.mthmmy.utils.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
import gr.thmmy.mthmmy.utils.parsing.NewParseTask;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.views.CustomRecyclerView;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.Response;
-import timber.log.Timber;
/**
* A {@link BaseFragment} subclass.
@@ -59,13 +58,11 @@ public class UnreadFragment extends BaseFragment {
private UnreadAdapter unreadAdapter;
private List topicSummaries;
- private String markAsReadUrl;
private int numberOfPages = 0;
private int loadedPages = 0;
private UnreadTask unreadTask;
- private MarkReadTask markReadTask;
- private ValidateSessionTask validateSessionTask;
+ private MarkAsReadTask markAsReadTask;
// Required empty public constructor
public UnreadFragment() {}
@@ -89,22 +86,20 @@ public class UnreadFragment extends BaseFragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
topicSummaries = new ArrayList<>();
- markAsReadUrl = BaseApplication.getInstance().getSessionManager().getMarkAllAsReadLink();
- if(markAsReadUrl==null){
- Timber.i("MarkAsRead URL is null.");
- startValidateSessionTask();
- }
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (topicSummaries.isEmpty()){
+ hideMarkAsReadFAB();
unreadTask = new UnreadTask(this::onUnreadTaskStarted, UnreadFragment.this::onUnreadTaskCancelled, this::onUnreadTaskFinished);
assert SessionManager.unreadUrl != null;
unreadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, SessionManager.unreadUrl.toString());
}
- markReadTask = new MarkReadTask(this::onMarkReadTaskStarted, this::onMarkReadTaskFinished);
+ else
+ showMarkAsReadFAB();
+ markAsReadTask = new MarkAsReadTask(UnreadFragment.this::onMarkAsReadTaskStarted, UnreadFragment.this::onMarkAsReadTaskFinished);
}
@@ -119,16 +114,7 @@ public class UnreadFragment extends BaseFragment {
progressBar = rootView.findViewById(R.id.progressBar);
noUnreadTopicsTextView = rootView.findViewById(R.id.no_unread_topics);
markAsReadFAB = rootView.findViewById(R.id.unread_fab);
-
- if(topicSummaries.isEmpty()){
- hideMarkAsReadFAB();
- noUnreadTopicsTextView.setVisibility(View.VISIBLE);
- }
- else{
- noUnreadTopicsTextView.setVisibility(View.INVISIBLE);
- showMarkAsReadFAB();
- }
-
+
unreadAdapter = new UnreadAdapter(topicSummaries, fragmentInteractionListener);
CustomRecyclerView recyclerView = rootView.findViewById(R.id.list);
@@ -146,7 +132,6 @@ public class UnreadFragment extends BaseFragment {
this::startUnreadTask
);
}
-
return rootView;
}
@@ -154,17 +139,10 @@ public class UnreadFragment extends BaseFragment {
public void onDestroy() {
super.onDestroy();
cancelUnreadTaskIfRunning();
- if (markReadTask!=null){
- try{
- if(markReadTask.isRunning())
- markReadTask.cancel(true);
- } // Yes, it happens even though we checked
- catch (NullPointerException ignored){ }
- }
- if (validateSessionTask!=null){
+ if (markAsReadTask !=null){
try{
- if(validateSessionTask.isRunning())
- validateSessionTask.cancel(true);
+ if(markAsReadTask.isRunning())
+ markAsReadTask.cancel(true);
} // Yes, it happens even though we checked
catch (NullPointerException ignored){ }
}
@@ -187,11 +165,6 @@ public class UnreadFragment extends BaseFragment {
}
}
- private void startValidateSessionTask(){
- validateSessionTask = new ValidateSessionTask();
- validateSessionTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
private void cancelUnreadTaskIfRunning(){
if (unreadTask!=null){
try{
@@ -223,9 +196,9 @@ public class UnreadFragment extends BaseFragment {
builder.setTitle("Mark all as read");
builder.setMessage("Are you sure that you want to mark ALL topics as read?");
builder.setPositiveButton("Yep", (dialogInterface, i) -> {
- if (!markReadTask.isRunning() && markAsReadUrl!=null){
- markReadTask = new MarkReadTask(this::onMarkReadTaskStarted, this::onMarkReadTaskFinished);
- markReadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, markAsReadUrl);
+ if (!markAsReadTask.isRunning()){
+ markAsReadTask = new MarkAsReadTask(this::onMarkAsReadTaskStarted, this::onMarkAsReadTaskFinished);
+ markAsReadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
builder.setNegativeButton("Nope", (dialogInterface, i) -> {});
@@ -275,6 +248,8 @@ public class UnreadFragment extends BaseFragment {
hideProgressUI();
if (resultCode == NetworkResultCodes.NETWORK_ERROR)
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
+ else if (resultCode == SessionManager.INVALID_SESSION)
+ Toast.makeText(getContext(), "Session verification failed. Please try logging in again.", Toast.LENGTH_LONG).show();
else
Toast.makeText(getContext(), "Unexpected error," +
" please contact the developers with the details", Toast.LENGTH_LONG).show();
@@ -287,7 +262,10 @@ public class UnreadFragment extends BaseFragment {
}
@Override
- protected ArrayList parse(Document document, Response response) throws ParseException {
+ protected ArrayList parse(Document document, Response response) throws ParseException, InvalidSessionException {
+ if(!document.select("td:containsOwn(Only registered members are allowed to access this section.)").isEmpty())
+ throw new InvalidSessionException();
+
Elements unread = document.select("table.bordercolor[cellspacing=1] tr:not(.titlebg)");
ArrayList fetchedTopicSummaries = new ArrayList<>();
if (!unread.isEmpty()) {
@@ -307,12 +285,10 @@ public class UnreadFragment extends BaseFragment {
}
Element topBar = document.select("table:not(.bordercolor):not(#bodyarea):has(td.middletext)").first();
- Element pagesElement = null, markRead = null;
- if (topBar != null) {
+ Element pagesElement = null;
+ if (topBar != null)
pagesElement = topBar.select("td.middletext").first();
- markRead = document.select("table:not(.bordercolor):not([width])").select("a")
- .first();
- }
+
if (numberOfPages == 0 && pagesElement != null) {
Elements pages = pagesElement.select("a");
@@ -322,14 +298,6 @@ public class UnreadFragment extends BaseFragment {
numberOfPages = 1;
}
- if (markRead != null && loadedPages == numberOfPages - 1){
- String retrievedMarkAsReadUrl = markRead.attr("href");
- if(!retrievedMarkAsReadUrl.equals(markAsReadUrl)) {
- markAsReadUrl = retrievedMarkAsReadUrl;
- BaseApplication.getInstance().getSessionManager().refreshSescFromUrl(retrievedMarkAsReadUrl);
- }
- }
-
return fetchedTopicSummaries;
}
return new ArrayList<>();
@@ -341,49 +309,25 @@ public class UnreadFragment extends BaseFragment {
}
}
- //---------------------------------------MARKREAD TASK------------------------------------------
- private void onMarkReadTaskStarted() {
+ //---------------------------------------MARK AS READ TASK------------------------------------------
+ private void onMarkAsReadTaskStarted() {
cancelUnreadTaskIfRunning();
progressBar.setVisibility(ProgressBar.VISIBLE);
}
- private void onMarkReadTaskFinished(int resultCode, Boolean isSessionVerified) {
+ private void onMarkAsReadTaskFinished(int resultCode, Void v) {
hideProgressUI();
- if (resultCode == NetworkResultCodes.SUCCESSFUL) {
- if (!isSessionVerified){
- Toast.makeText(getContext(), "Session verification failed", Toast.LENGTH_SHORT).show();
- startValidateSessionTask();
- }
- else
+ if (resultCode == NetworkResultCodes.SUCCESSFUL)
startUnreadTask();
- }
else{
hideProgressUI();
if (resultCode == NetworkResultCodes.NETWORK_ERROR)
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
+ else if (resultCode == SessionManager.INVALID_SESSION)
+ Toast.makeText(getContext(), "Session verification failed. Please try logging out and back in again", Toast.LENGTH_LONG).show();
else
Toast.makeText(getContext(), "Unexpected error," +
" please contact the developers with the details", Toast.LENGTH_LONG).show();
}
}
-
- private class MarkReadTask extends NewParseTask {
- MarkReadTask(OnTaskStartedListener onTaskStartedListener, OnNetworkTaskFinishedListener onParseTaskFinishedListener) {
- super(onTaskStartedListener, onParseTaskFinishedListener);
- }
-
- @Override
- protected Boolean parse(Document document, Response response) throws ParseException {
- Elements sessionVerificationFailed = document.select("td:containsOwn(Session " +
- "verification failed. Please try logging out and back in again, and then try " +
- "again.), td:containsOwn(Η επαλήθευση συνόδου απέτυχε. Παρακαλούμε κάντε " +
- "αποσύνδεση, επανασύνδεση και ξαναδοκιμάστε.)");
- return sessionVerificationFailed.isEmpty();
- }
-
- @Override
- protected int getResultCode(Response response, Boolean isSessionVerified) {
- return NetworkResultCodes.SUCCESSFUL;
- }
- }
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java
index 81f70fae..223f36e9 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java
@@ -45,8 +45,8 @@ import gr.thmmy.mthmmy.activities.topic.TopicActivity;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.model.PostSummary;
import gr.thmmy.mthmmy.model.ThmmyPage;
-import gr.thmmy.mthmmy.utils.NetworkResultCodes;
import gr.thmmy.mthmmy.utils.Parcel;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
import gr.thmmy.mthmmy.utils.parsing.NewParseTask;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.utils.ui.CenterVerticalSpan;
@@ -57,6 +57,7 @@ import timber.log.Timber;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_TITLE;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.emojiTagToHtml;
+import static gr.thmmy.mthmmy.utils.ui.GlideUtils.isValidContextForGlide;
import static gr.thmmy.mthmmy.utils.ui.PhotoViewUtils.displayPhotoViewImage;
/**
@@ -222,12 +223,16 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
avatarView.setOnClickListener(v -> displayPhotoViewImage(ProfileActivity.this, avatarUrl));
}
- Glide.with(this)
- .load(avatarUri)
- .circleCrop()
- .error(R.drawable.ic_default_user_avatar)
- .placeholder(R.drawable.ic_default_user_avatar)
- .into(avatarView);
+ if(isValidContextForGlide(this)){
+ Glide.with(this)
+ .load(avatarUri)
+ .circleCrop()
+ .error(R.drawable.ic_default_user_avatar)
+ .placeholder(R.drawable.ic_default_user_avatar)
+ .into(avatarView);
+ }
+ else
+ Timber.d("Will not load Glide image (invalid context)");
}
/**
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java
index edd06356..ef2d1693 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java
@@ -200,14 +200,15 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Shared
if (key.equals(getString(R.string.pref_privacy_crashlytics_enable_key))) {
enabled = sharedPreferences.getBoolean(key, false);
if(enabled)
- BaseApplication.getInstance().startFirebaseCrashlyticsCollection();
+ BaseApplication.getInstance().setFirebaseCrashlyticsEnabled(true);
else {
Timber.i("Crashlytics collection will be disabled after restarting.");
+ BaseApplication.getInstance().setFirebaseCrashlyticsEnabled(false);
displayRestartAppToTakeEffectToast();
}
} else if (key.equals(getString(R.string.pref_privacy_analytics_enable_key))) {
enabled = sharedPreferences.getBoolean(key, false);
- BaseApplication.getInstance().setFirebaseAnalyticsCollection(enabled);
+ BaseApplication.getInstance().setFirebaseAnalyticsEnabled(enabled);
if(enabled)
Timber.i("Analytics collection enabled.");
else
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/SendShoutTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/SendShoutTask.java
index 3b2af22f..171be1e3 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/SendShoutTask.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/SendShoutTask.java
@@ -4,8 +4,8 @@ import org.jsoup.nodes.Document;
import java.io.IOException;
-import gr.thmmy.mthmmy.utils.NetworkResultCodes;
-import gr.thmmy.mthmmy.utils.NetworkTask;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkTask;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java
index 0049fa6b..e776423f 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java
@@ -21,7 +21,7 @@ import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.Shout;
import gr.thmmy.mthmmy.model.Shoutbox;
-import gr.thmmy.mthmmy.utils.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
import gr.thmmy.mthmmy.viewmodel.ShoutboxViewModel;
import gr.thmmy.mthmmy.views.CustomRecyclerView;
import gr.thmmy.mthmmy.views.editorview.EditorView;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxTask.java
index 8a1d3768..5fbb1415 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxTask.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxTask.java
@@ -7,7 +7,7 @@ import java.util.ArrayList;
import gr.thmmy.mthmmy.model.Shout;
import gr.thmmy.mthmmy.model.Shoutbox;
-import gr.thmmy.mthmmy.utils.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
import gr.thmmy.mthmmy.utils.parsing.NewParseTask;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
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 f9145be5..3621057f 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
@@ -54,7 +54,7 @@ import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.ThmmyPage;
import gr.thmmy.mthmmy.model.TopicItem;
import gr.thmmy.mthmmy.utils.HTMLUtils;
-import gr.thmmy.mthmmy.utils.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import gr.thmmy.mthmmy.viewmodel.TopicViewModel;
import gr.thmmy.mthmmy.views.CustomLinearLayoutManager;
@@ -155,6 +155,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
}
topicPageUrl = ThmmyPage.sanitizeTopicUrl(topicPageUrl);
+ //TODO if topicTitle provided is null make bookmark button unclickable until title is fetched (also for BoardActivity)
thisPageBookmark = new Bookmark(topicTitle, ThmmyPage.getTopicId(topicPageUrl), true);
//Initializes graphics
@@ -671,8 +672,10 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
});
viewModel.getTopicTitle().observe(this, newTopicTitle -> {
if (newTopicTitle == null) return;
- if (!TextUtils.equals(toolbarTitle.getText(), newTopicTitle))
+ if (!TextUtils.equals(toolbarTitle.getText(), newTopicTitle)) {
+ thisPageBookmark = new Bookmark(newTopicTitle, thisPageBookmark.getId(), thisPageBookmark.isNotificationsEnabled());
toolbarTitle.setText(newTopicTitle);
+ }
});
viewModel.getPageTopicId().observe(this, pageTopicId -> {
if (pageTopicId == null) return;
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 213de341..def64530 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
@@ -88,6 +88,7 @@ import static gr.thmmy.mthmmy.activities.topic.TopicParser.USER_COLOR_WHITE;
import static gr.thmmy.mthmmy.activities.topic.TopicParser.USER_COLOR_YELLOW;
import static gr.thmmy.mthmmy.base.BaseActivity.getSessionManager;
import static gr.thmmy.mthmmy.utils.FileUtils.faIconFromFilename;
+import static gr.thmmy.mthmmy.utils.ui.GlideUtils.isValidContextForGlide;
/**
* Custom {@link RecyclerView.Adapter} used for topics.
@@ -810,12 +811,14 @@ class TopicAdapter extends RecyclerView.Adapter {
if(imageUrl!=null)
imageUrl = imageUrl.trim();
- Glide.with(context)
- .load(imageUrl)
- .circleCrop()
- .error(R.drawable.ic_default_user_avatar_darker)
- .placeholder(R.drawable.ic_default_user_avatar_darker)
- .into(imageView);
+ if(isValidContextForGlide(context)) {
+ Glide.with(context)
+ .load(imageUrl)
+ .circleCrop()
+ .error(R.drawable.ic_default_user_avatar_darker)
+ .placeholder(R.drawable.ic_default_user_avatar_darker)
+ .into(imageView);
+ }
}
@Override
@@ -979,7 +982,6 @@ class TopicAdapter extends RecyclerView.Adapter {
for (int i = 0; i < topicItems.size(); i++) {
if (topicItems.get(i) instanceof Post && ((Post) topicItems.get(i)).getPostIndex() == testAgainst) {
//same page
- Timber.d(Integer.toString(i));
postFocusListener.onPostFocusChange(i);
return true;
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java
index 901ce553..7f28e8dc 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java
@@ -18,7 +18,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import gr.thmmy.mthmmy.base.BaseActivity;
-import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.Poll;
import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.ThmmyFile;
@@ -26,8 +25,6 @@ import gr.thmmy.mthmmy.model.TopicItem;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import timber.log.Timber;
-import static gr.thmmy.mthmmy.utils.parsing.ThmmyDateTimeParser.convertToTimestamp;
-
/**
* Singleton used for parsing a topic.
@@ -178,7 +175,6 @@ public class TopicParser {
p_specialRank, p_gender, p_personalText, p_numberOfPosts, p_postLastEditDate,
p_postURL, p_deletePostURL, p_editPostURL;
int p_postNum, p_postIndex, p_numberOfStars, p_userColor;
- long p_timestamp;
boolean p_isDeleted = false, p_isUserMentionedInPost = false;
ArrayList p_attachedFiles;
@@ -195,7 +191,6 @@ public class TopicParser {
p_postLastEditDate = null;
p_deletePostURL = null;
p_editPostURL = null;
- p_timestamp = 0;
//Language independent parsing
//Finds thumbnail url
@@ -272,12 +267,6 @@ public class TopicParser {
p_postDate = p_postDate.substring(p_postDate.indexOf("στις:") + 6
, p_postDate.indexOf(" »"));
- if (BaseApplication.getInstance().isDisplayRelativeTimeEnabled()) {
- String timestamp = convertToTimestamp(p_postDate);
- if(timestamp!=null)
- p_timestamp = Long.valueOf(timestamp);
- }
-
//Finds post's reply index number
Element postNum = thisRow.select("div.smalltext:matches(Απάντηση #)").first();
if (postNum == null) { //Topic starter
@@ -305,7 +294,7 @@ public class TopicParser {
Timber.e(e, "Attached file malformed url");
break;
}
- String attachedFileName = tmpAttachedFileUrlAndName.text().substring(1);
+ String attachedFileName = tmpAttachedFileUrlAndName.wholeText().substring(1);
//Gets file's info (size and download count)
String postAttachmentsTextSbstr = postAttachmentsText.substring(
@@ -377,7 +366,7 @@ public class TopicParser {
Timber.e(e, "Attached file malformed url");
break;
}
- String attachedFileName = tmpAttachedFileUrlAndName.text().substring(1);
+ String attachedFileName = tmpAttachedFileUrlAndName.wholeText().substring(1);
//Gets file's info (size and download count)
String postAttachmentsTextSbstr = postAttachmentsText.substring(
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java
index b277d2bd..96600b7b 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java
@@ -4,8 +4,8 @@ import org.jsoup.nodes.Document;
import java.io.IOException;
-import gr.thmmy.mthmmy.utils.NetworkResultCodes;
-import gr.thmmy.mthmmy.utils.NetworkTask;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkTask;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/RemoveVoteTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/RemoveVoteTask.java
index f94ae027..484522c1 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/RemoveVoteTask.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/RemoveVoteTask.java
@@ -2,8 +2,8 @@ package gr.thmmy.mthmmy.activities.topic.tasks;
import org.jsoup.nodes.Document;
-import gr.thmmy.mthmmy.utils.NetworkResultCodes;
-import gr.thmmy.mthmmy.utils.NetworkTask;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkTask;
import okhttp3.Response;
public class RemoveVoteTask extends NetworkTask {
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/SubmitVoteTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/SubmitVoteTask.java
index ec3773f7..75dc37e3 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/SubmitVoteTask.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/SubmitVoteTask.java
@@ -4,8 +4,8 @@ import org.jsoup.nodes.Document;
import java.io.IOException;
-import gr.thmmy.mthmmy.utils.NetworkResultCodes;
-import gr.thmmy.mthmmy.utils.NetworkTask;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkTask;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java
index 14899fa0..c3e78d8d 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java
@@ -11,6 +11,9 @@ import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import gr.thmmy.mthmmy.activities.topic.TopicParser;
import gr.thmmy.mthmmy.base.BaseApplication;
@@ -29,6 +32,8 @@ import timber.log.Timber;
* parameter.
*/
public class TopicTask extends AsyncTask {
+ private static final Pattern msgPattern = Pattern.compile("msg(\\d+)");
+
private TopicTaskObserver topicTaskObserver;
private OnTopicTaskCompleted finishListener;
@@ -58,15 +63,9 @@ public class TopicTask extends AsyncTask {
//Finds the index of message focus if present
int postFocus = 0;
-
- //TODO: Better parseInt handling - may rarely fail
- 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('#')));
- }
+ Matcher matcher = msgPattern.matcher(newPageUrl);
+ if (matcher.find())
+ postFocus = Integer.parseInt(Objects.requireNonNull(matcher.group(1)));
Request request = new Request.Builder()
.url(newPageUrl)
diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
index 37f88876..733e12b8 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
@@ -62,9 +62,11 @@ import gr.thmmy.mthmmy.model.Bookmark;
import gr.thmmy.mthmmy.model.ThmmyFile;
import gr.thmmy.mthmmy.services.DownloadHelper;
import gr.thmmy.mthmmy.services.UploadsReceiver;
+import gr.thmmy.mthmmy.session.LogoutTask;
import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.FileUtils;
import gr.thmmy.mthmmy.utils.io.AssetUtils;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
import gr.thmmy.mthmmy.viewmodel.BaseViewModel;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.OkHttpClient;
@@ -82,7 +84,6 @@ import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_
import static gr.thmmy.mthmmy.activities.settings.SettingsActivity.DEFAULT_HOME_TAB;
import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR;
import static gr.thmmy.mthmmy.services.UploadsReceiver.UPLOAD_ID_KEY;
-import static gr.thmmy.mthmmy.session.SessionManager.SUCCESS;
import static gr.thmmy.mthmmy.utils.FileUtils.getMimeType;
public abstract class BaseActivity extends AppCompatActivity {
@@ -205,7 +206,7 @@ public abstract class BaseActivity extends AppCompatActivity {
*/
protected void createDrawer() {
final int primaryColor = ContextCompat.getColor(this, R.color.iron);
- final int selectedPrimaryColor = ContextCompat.getColor(this, R.color.primary_dark);
+ final int selectedPrimaryColor = ContextCompat.getColor(this, R.color.primary_light);
final int selectedSecondaryColor = ContextCompat.getColor(this, R.color.accent);
PrimaryDrawerItem homeItem, bookmarksItem, settingsItem, aboutItem, shoutboxItem;
@@ -360,7 +361,7 @@ public abstract class BaseActivity extends AppCompatActivity {
.withActivity(this)
.withCompactStyle(true)
.withSelectionListEnabledForSingleProfile(false)
- .withHeaderBackground(R.color.primary)
+ .withHeaderBackground(R.color.primary_dark)
.withTextColor(getResources().getColor(R.color.iron))
.addProfiles(profileDrawerItem)
.withOnAccountHeaderListener((view, profile, currentProfile) -> {
@@ -389,7 +390,7 @@ public abstract class BaseActivity extends AppCompatActivity {
.withActivity(this)
.withToolbar(toolbar)
.withDrawerWidthDp((int) BaseApplication.getInstance().getWidthInDp() / 2)
- .withSliderBackgroundColor(ContextCompat.getColor(this, R.color.primary_light))
+ .withSliderBackgroundColor(ContextCompat.getColor(this, R.color.primary_lighter))
.withAccountHeader(accountHeader)
.withOnDrawerItemClickListener((view, position, drawerItem) -> {
if (drawerItem.equals(HOME_ID)) {
@@ -463,8 +464,7 @@ public abstract class BaseActivity extends AppCompatActivity {
private void updateDrawer() {
if (drawer != null) {
- if (!sessionManager.isLoggedIn()) //When logged out or if user is guest
- {
+ if (!sessionManager.isLoggedIn()){ //When logged out or if user is guest
drawer.removeItem(DOWNLOADS_ID);
drawer.removeItem(UPLOAD_ID);
loginLogoutItem.withName(R.string.login).withIcon(loginIcon); //Swap logout with login
@@ -486,60 +486,43 @@ public abstract class BaseActivity extends AppCompatActivity {
}
accountHeader.updateProfile(profileDrawerItem);
drawer.updateItem(loginLogoutItem);
-
}
}
private void setDefaultAvatar() {
- profileDrawerItem.withIcon(new IconicsDrawable(this)
- .icon(FontAwesome.Icon.faw_user)
- .paddingDp(10)
- .color(ContextCompat.getColor(this, R.color.iron))
- .backgroundColor(ContextCompat.getColor(this, R.color.primary_light)));
+ profileDrawerItem.withIcon(R.drawable.ic_default_user_avatar);
}
//-------------------------------------------LOGOUT-------------------------------------------------
-
- /**
- * Result toast will always display a success, because when user chooses logout all data are
- * cleared regardless of the actual outcome
- */
- private class LogoutTask extends AsyncTask { //Attempt logout
- ProgressDialog progressDialog;
-
- protected Integer doInBackground(Void... voids) {
- return sessionManager.logout();
- }
-
- protected void onPreExecute() { //Show a progress dialog until done
- progressDialog = new ProgressDialog(BaseActivity.this,
- R.style.AppTheme_Dark_Dialog);
- progressDialog.setCancelable(false);
- progressDialog.setIndeterminate(true);
- progressDialog.setMessage("Logging out...");
- progressDialog.show();
- }
-
- protected void onPostExecute(Integer result) {
- if (result == SUCCESS) {
- SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
- if (sharedPrefs.getString(DEFAULT_HOME_TAB, "0").equals("2")) {
- SharedPreferences.Editor editor = sharedPrefs.edit();
- editor.putString(DEFAULT_HOME_TAB, "0").apply();
- }
+ private ProgressDialog progressDialog;
+ private void onLogoutTaskStarted() {
+ progressDialog = new ProgressDialog(BaseActivity.this,
+ R.style.AppTheme_Dark_Dialog);
+ progressDialog.setCancelable(false);
+ progressDialog.setIndeterminate(true);
+ progressDialog.setMessage("Logging out...");
+ progressDialog.show();
+ }
+
+ private void onLogoutTaskFinished(int resultCode, Void v) {
+ if (resultCode == NetworkResultCodes.SUCCESSFUL) {
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ if (sharedPrefs.getString(DEFAULT_HOME_TAB, "0").equals("2")) {
+ SharedPreferences.Editor editor = sharedPrefs.edit();
+ editor.putString(DEFAULT_HOME_TAB, "0").apply();
}
-
- updateDrawer();
- if (mainActivity != null)
- mainActivity.updateTabs();
- progressDialog.dismiss();
- //TODO: Redirect to Main only for some Activities (e.g. Topic, Board, Downloads)
- //if (BaseActivity.this instanceof TopicActivity){
- Intent intent = new Intent(BaseActivity.this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- //}
}
+
+ updateDrawer();
+ if (mainActivity != null)
+ mainActivity.updateTabs();
+ progressDialog.dismiss();
+ //TODO: Redirect to Main only for some Activities (e.g. Topic, Board, Downloads)
+ //if (BaseActivity.this instanceof TopicActivity){
+ Intent intent = new Intent(BaseActivity.this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ //}
}
private void showLogoutDialog() {
@@ -547,7 +530,7 @@ public abstract class BaseActivity extends AppCompatActivity {
builder.setTitle("Logout");
builder.setMessage("Are you sure that you want to logout?");
builder.setPositiveButton("Yep", (dialogInterface, i) -> {
- new LogoutTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); //Avoid delays between onPreExecute() and doInBackground()
+ new LogoutTask(this::onLogoutTaskStarted, this::onLogoutTaskFinished).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); //Avoid delays between onPreExecute() and doInBackground()
});
builder.setNegativeButton("Nope", (dialogInterface, i) -> {});
builder.create().show();
@@ -807,8 +790,8 @@ public abstract class BaseActivity extends AppCompatActivity {
builder.setPositiveButton("Yes, I want to help", (dialogInterface, i) -> {
addUserConsent();
FirebaseMessaging.getInstance().setAutoInitEnabled(true);
- BaseApplication.getInstance().startFirebaseCrashlyticsCollection();
- BaseApplication.getInstance().setFirebaseAnalyticsCollection(true);
+ BaseApplication.getInstance().setFirebaseCrashlyticsEnabled(true);
+ BaseApplication.getInstance().setFirebaseAnalyticsEnabled(true);
setUserDataShareEnabled(true);
});
builder.setNegativeButton("Nope, leave me alone", (dialogInterface, i) -> {
diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java
index 755b539a..feed7496 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java
@@ -14,13 +14,12 @@ import androidx.multidex.MultiDexApplication;
import androidx.preference.PreferenceManager;
import com.bumptech.glide.Glide;
-import com.crashlytics.android.Crashlytics;
-import com.crashlytics.android.core.CrashlyticsCore;
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
import com.google.firebase.FirebaseApp;
import com.google.firebase.analytics.FirebaseAnalytics;
+import com.google.firebase.crashlytics.FirebaseCrashlytics;
import com.itkacher.okhttpprofiler.OkHttpProfilerInterceptor;
import com.mikepenz.fontawesome_typeface_library.FontAwesome;
import com.mikepenz.iconics.IconicsDrawable;
@@ -40,7 +39,6 @@ import gr.thmmy.mthmmy.BuildConfig;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.crashreporting.CrashReportingTree;
-import io.fabric.sdk.android.Fabric;
import okhttp3.CipherSuite;
import okhttp3.ConnectionSpec;
import okhttp3.HttpUrl;
@@ -50,9 +48,12 @@ import timber.log.Timber;
import static gr.thmmy.mthmmy.activities.settings.SettingsActivity.DISPLAY_RELATIVE_TIME;
+// TODO: Replace MultiDexApplication with Application after KitKat support is dropped
public class BaseApplication extends MultiDexApplication {
private static BaseApplication baseApplication; //BaseApplication singleton
+ private CrashReportingTree crashReportingTree;
+
//Firebase
private static String firebaseProjectId;
private FirebaseAnalytics firebaseAnalytics;
@@ -63,9 +64,6 @@ public class BaseApplication extends MultiDexApplication {
private boolean displayRelativeTime;
- //TODO: maybe use PreferenceManager.getDefaultSharedPreferences here as well?
- private static final String SHARED_PREFS = "ThmmySharedPrefs";
-
//Display Metrics
private static float widthDp;
private static int widthPxl, heightPxl;
@@ -84,26 +82,52 @@ public class BaseApplication extends MultiDexApplication {
Timber.plant(new Timber.DebugTree());
//Shared Preferences
- SharedPreferences sharedPrefs = getSharedPreferences(SHARED_PREFS, MODE_PRIVATE);
+ SharedPreferences sessionSharedPrefs = getSharedPreferences(getString(R.string.session_shared_prefs), MODE_PRIVATE);
SharedPreferences settingsSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences draftsPrefs = getSharedPreferences(getString(R.string.pref_topic_drafts_key), MODE_PRIVATE);
- if (settingsSharedPrefs.getBoolean(getString(R.string.pref_privacy_crashlytics_enable_key), false))
- startFirebaseCrashlyticsCollection();
- else
- Timber.i("Starting app with Crashlytics disabled.");
+ initFirebase(settingsSharedPrefs);
+
+ SharedPrefsCookiePersistor sharedPrefsCookiePersistor = new SharedPrefsCookiePersistor(getApplicationContext());
+ PersistentCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), sharedPrefsCookiePersistor);
+
+ initOkHttp(cookieJar);
+
+ sessionManager = new SessionManager(client, cookieJar, sharedPrefsCookiePersistor, sessionSharedPrefs, draftsPrefs);
+
+ //Sets up upload service
+ UploadService.NAMESPACE = BuildConfig.APPLICATION_ID;
+ UploadService.HTTP_STACK = new OkHttpStack(client);
+
+ //Initialize and create the image loader logic for the drawer
+ initDrawerImageLoader();
+
+ setDisplayMetrics();
+
+ displayRelativeTime = settingsSharedPrefs.getBoolean(DISPLAY_RELATIVE_TIME, true);
+ }
+
+ private void initFirebase(SharedPreferences settingsSharedPrefs){
+ if (settingsSharedPrefs.getBoolean(getString(R.string.pref_privacy_crashlytics_enable_key), false)){
+ Timber.i("Starting app with Firebase Crashlytics enabled.");
+ setFirebaseCrashlyticsEnabled(true);
+ }
+ else {
+ Timber.i("Starting app with Firebase Crashlytics disabled.");
+ setFirebaseCrashlyticsEnabled(false);
+ }
firebaseProjectId = FirebaseApp.getInstance().getOptions().getProjectId();
firebaseAnalytics = FirebaseAnalytics.getInstance(this);
boolean enableAnalytics = settingsSharedPrefs.getBoolean(getString(R.string.pref_privacy_analytics_enable_key), false);
firebaseAnalytics.setAnalyticsCollectionEnabled(enableAnalytics);
if (enableAnalytics)
- Timber.i("Starting app with Analytics enabled.");
+ Timber.i("Starting app with Firebase Analytics enabled.");
else
- Timber.i("Starting app with Analytics disabled.");
+ Timber.i("Starting app with Firebase Analytics disabled.");
+ }
- SharedPrefsCookiePersistor sharedPrefsCookiePersistor = new SharedPrefsCookiePersistor(getApplicationContext());
- PersistentCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), sharedPrefsCookiePersistor);
+ private void initOkHttp(PersistentCookieJar cookieJar){
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.cookieJar(cookieJar)
.addInterceptor(chain -> {
@@ -111,9 +135,9 @@ public class BaseApplication extends MultiDexApplication {
HttpUrl oldUrl = chain.request().url();
if (Objects.equals(chain.request().url().host(), "www.thmmy.gr")
&& !oldUrl.toString().contains("theme=4")) {
- //Probably works but needs more testing:
- HttpUrl newUrl = oldUrl.newBuilder().addQueryParameter("theme", "4").build();
- request = request.newBuilder().url(newUrl).build();
+ //Probably works but needs more testing:
+ HttpUrl newUrl = oldUrl.newBuilder().addQueryParameter("theme", "4").build();
+ request = request.newBuilder().url(newUrl).build();
}
return chain.proceed(request);
})
@@ -139,14 +163,9 @@ public class BaseApplication extends MultiDexApplication {
builder.addInterceptor(new OkHttpProfilerInterceptor());
client = builder.build();
+ }
- sessionManager = new SessionManager(client, cookieJar, sharedPrefsCookiePersistor, sharedPrefs, draftsPrefs);
-
- //Sets up upload service
- UploadService.NAMESPACE = BuildConfig.APPLICATION_ID;
- UploadService.HTTP_STACK = new OkHttpStack(client);
-
- //Initialize and create the image loader logic
+ private void initDrawerImageLoader(){
DrawerImageLoader.init(new AbstractDrawerImageLoader() {
@Override
public void set(ImageView imageView, Uri uri, Drawable placeholder, String tag) {
@@ -160,27 +179,32 @@ public class BaseApplication extends MultiDexApplication {
@Override
public Drawable placeholder(Context ctx, String tag) {
- if (DrawerImageLoader.Tags.PROFILE.name().equals(tag)) {
- return new IconicsDrawable(ctx).icon(FontAwesome.Icon.faw_user)
- .paddingDp(10)
- .color(ContextCompat.getColor(ctx, R.color.primary_light))
- .backgroundColor(ContextCompat.getColor(ctx, R.color.primary));
+ if (DrawerImageLoader.Tags.PROFILE.name().equals(tag)){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ return ContextCompat.getDrawable(BaseApplication.getInstance(), R.drawable.ic_default_user_avatar);
+ else { // Just for KitKats
+ return new IconicsDrawable(ctx).icon(FontAwesome.Icon.faw_user)
+ .paddingDp(10)
+ .color(ContextCompat.getColor(ctx, R.color.iron))
+ .backgroundColor(ContextCompat.getColor(ctx, R.color.primary_lighter));
+ }
}
return super.placeholder(ctx, tag);
}
});
+ }
+ private void setDisplayMetrics(){
DisplayMetrics displayMetrics = getApplicationContext().getResources().getDisplayMetrics();
widthPxl = displayMetrics.widthPixels;
widthDp = widthPxl / displayMetrics.density;
heightPxl = displayMetrics.heightPixels;
-
- displayRelativeTime = settingsSharedPrefs.getBoolean(DISPLAY_RELATIVE_TIME, true);
}
- //Getters
+
+ //-------------------- Getters --------------------
public Context getContext() {
return getApplicationContext();
}
@@ -209,30 +233,38 @@ public class BaseApplication extends MultiDexApplication {
return displayRelativeTime;
}
- //--------------------Firebase--------------------
+ //-------------------- Firebase --------------------
public void logFirebaseAnalyticsEvent(String event, Bundle params) {
firebaseAnalytics.logEvent(event, params);
}
- public void setFirebaseAnalyticsCollection(boolean enabled) {
+ public void setFirebaseAnalyticsEnabled(boolean enabled) {
firebaseAnalytics.setAnalyticsCollectionEnabled(enabled);
if (!enabled)
firebaseAnalytics.resetAnalyticsData();
+
+ if(enabled)
+ Timber.i("Firebase Analytics enabled.");
+ else
+ Timber.i("Firebase Analytics disabled.");
}
- // Set up Crashlytics, disabled for debug builds
- public void startFirebaseCrashlyticsCollection() {
- if (!Fabric.isInitialized()) {
- Crashlytics crashlyticsKit = new Crashlytics.Builder()
- .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
- .build();
- // Initialize Fabric with the debug-disabled Crashlytics.
- Fabric.with(this, crashlyticsKit);
- Timber.plant(new CrashReportingTree());
- Timber.i("Crashlytics enabled.");
- } else
- Timber.i("Crashlytics were already initialized for this app session.");
+ public void setFirebaseCrashlyticsEnabled(boolean enable) {
+ FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(enable);
+ if(enable){
+ crashReportingTree = new CrashReportingTree();
+ Timber.plant(crashReportingTree);
+ Timber.i("CrashReporting tree planted.");
+ Timber.i("Firebase Crashlytics enabled.");
+ }
+ else{
+ if(crashReportingTree!=null) {
+ Timber.uproot(crashReportingTree);
+ Timber.i("CrashReporting tree uprooted.");
+ }
+ Timber.i("Firebase Crashlytics disabled.");
+ }
}
public static String getFirebaseProjectId(){
diff --git a/app/src/main/java/gr/thmmy/mthmmy/session/InvalidSessionException.java b/app/src/main/java/gr/thmmy/mthmmy/session/InvalidSessionException.java
new file mode 100644
index 00000000..37bc2ddd
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/session/InvalidSessionException.java
@@ -0,0 +1,13 @@
+package gr.thmmy.mthmmy.session;
+
+public class InvalidSessionException extends RuntimeException {
+ public InvalidSessionException() {}
+
+ public InvalidSessionException(String message) {
+ super(message);
+ }
+
+ public InvalidSessionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/session/LogoutTask.java b/app/src/main/java/gr/thmmy/mthmmy/session/LogoutTask.java
new file mode 100644
index 00000000..66333739
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/session/LogoutTask.java
@@ -0,0 +1,86 @@
+package gr.thmmy.mthmmy.session;
+
+import org.jsoup.nodes.Document;
+import org.jsoup.select.Elements;
+
+import gr.thmmy.mthmmy.base.BaseApplication;
+import gr.thmmy.mthmmy.utils.Parcel;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkTask;
+import gr.thmmy.mthmmy.utils.parsing.ParseException;
+import okhttp3.Response;
+import timber.log.Timber;
+
+import static gr.thmmy.mthmmy.session.SessionManager.baseLogoutLink;
+import static gr.thmmy.mthmmy.session.SessionManager.indexUrl;
+
+
+public class LogoutTask extends NetworkTask {
+ private String logoutLink;
+
+ public LogoutTask(OnTaskStartedListener onTaskStartedListener, OnNetworkTaskFinishedListener onParseTaskFinishedListener) {
+ super(onTaskStartedListener, onParseTaskFinishedListener);
+ }
+
+ @Override
+ protected Parcel doInBackground(String... input) {
+ /* Firstly we will find the logout link
+ Keep in mind, server changes sesc at will over time for a given session!
+ */
+ Parcel parcel = executeInBackground(indexUrl.toString());
+ if(parcel.getResultCode() == NetworkResultCodes.SUCCESSFUL)
+ return executeInBackground(logoutLink); // Now we will attempt to logout
+ else return parcel;
+ }
+
+ @Override
+ protected Void performTask(Document document, Response response) {
+ try {
+ if(logoutLink==null)
+ logoutLink = extractLogoutLink(document);
+ else { // Just for logging purposes
+ Elements sessionVerificationFailed = document.select("td:containsOwn(Session " +
+ "verification failed. Please try logging out and back in again, and then try " +
+ "again.), td:containsOwn(Η επαλήθευση συνόδου απέτυχε. Παρακαλούμε κάντε " +
+ "αποσύνδεση, επανασύνδεση και ξαναδοκιμάστε.)");
+ if(!sessionVerificationFailed.isEmpty()){
+ Timber.i("Logout failed (invalid session)");
+ throw new InvalidSessionException();
+ }
+ Elements loginButton = document.select("[value=Login]"); //Attempt to find login button
+ if (!loginButton.isEmpty()) //If login button exists, logout was successful
+ Timber.i("Logout successful!");
+ else
+ Timber.i("Logout failed");
+ }
+ } catch (InvalidSessionException ise) {
+ throw ise;
+ } catch (Exception e) {
+ throw new ParseException("Parsing failed", e);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Parcel voidParcel) {
+ super.onPostExecute(voidParcel);
+ //All data should always be cleared from device regardless the result of logout
+ BaseApplication.getInstance().getSessionManager().logoutCleanup();
+ }
+
+ @Override
+ protected int getResultCode(Response response, Void v) {
+ return NetworkResultCodes.SUCCESSFUL;
+ }
+
+ private String extractLogoutLink(Document document){
+ Elements logoutLink = document.select("a[href^=" + baseLogoutLink + "]");
+
+ if (!logoutLink.isEmpty()) {
+ String link = logoutLink.first().attr("href");
+ if (link != null && !link.isEmpty())
+ return link;
+ }
+ throw new ParseException("Parsing failed (logoutLink extraction)");
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/session/MarkAsReadTask.java b/app/src/main/java/gr/thmmy/mthmmy/session/MarkAsReadTask.java
new file mode 100644
index 00000000..684f87ba
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/session/MarkAsReadTask.java
@@ -0,0 +1,65 @@
+package gr.thmmy.mthmmy.session;
+
+import org.jsoup.nodes.Document;
+import org.jsoup.select.Elements;
+
+import gr.thmmy.mthmmy.utils.Parcel;
+import gr.thmmy.mthmmy.utils.networking.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.networking.NetworkTask;
+import gr.thmmy.mthmmy.utils.parsing.ParseException;
+import okhttp3.Response;
+
+import static gr.thmmy.mthmmy.session.SessionManager.baseMarkAllAsReadLink;
+import static gr.thmmy.mthmmy.session.SessionManager.unreadUrl;
+
+public class MarkAsReadTask extends NetworkTask {
+ private String markAsReadLink;
+
+ public MarkAsReadTask(OnTaskStartedListener onTaskStartedListener, OnNetworkTaskFinishedListener onParseTaskFinishedListener) {
+ super(onTaskStartedListener, onParseTaskFinishedListener);
+ }
+
+ @Override
+ protected Parcel doInBackground(String... input) {
+ Parcel parcel = executeInBackground(unreadUrl.toString());
+ if(parcel.getResultCode() == NetworkResultCodes.SUCCESSFUL)
+ return executeInBackground(markAsReadLink);
+ else return parcel;
+ }
+
+ @Override
+ protected Void performTask(Document document, Response response) {
+ try {
+ Elements sessionVerificationFailed = document.select("td:containsOwn(Session " +
+ "verification failed. Please try logging out and back in again, and then try " +
+ "again.), td:containsOwn(Η επαλήθευση συνόδου απέτυχε. Παρακαλούμε κάντε " +
+ "αποσύνδεση, επανασύνδεση και ξαναδοκιμάστε.)");
+ if(!sessionVerificationFailed.isEmpty())
+ throw new InvalidSessionException();
+ if(markAsReadLink==null)
+ markAsReadLink = extractMarkAsReadLink(document);
+
+ } catch (InvalidSessionException ise) {
+ throw ise;
+ } catch (Exception e) {
+ throw new ParseException("Parsing failed", e);
+ }
+ return null;
+ }
+
+ @Override
+ protected int getResultCode(Response response, Void v) {
+ return NetworkResultCodes.SUCCESSFUL;
+ }
+
+ private String extractMarkAsReadLink(Document document){
+ Elements markAllAsReadLink = document.select("a[href^=" + baseMarkAllAsReadLink + "]");
+
+ if (!markAllAsReadLink.isEmpty()) {
+ String link = markAllAsReadLink.first().attr("href");
+ if (link != null && !link.isEmpty())
+ return link;
+ }
+ throw new ParseException("Parsing failed (markAllAsReadLink extraction)");
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java b/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java
index 36490d56..fff30acf 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java
@@ -39,19 +39,20 @@ public class SessionManager {
private static final HttpUrl loginUrl = HttpUrl.parse("https://www.thmmy.gr/smf/index.php?action=login2");
public static final HttpUrl unreadUrl = HttpUrl.parse("https://www.thmmy.gr/smf/index.php?action=unread;all;start=0;theme=4");
public static final HttpUrl shoutboxUrl = HttpUrl.parse("https://www.thmmy.gr/smf/index.php?action=tpmod;sa=shoutbox;theme=4");
- private static final String baseLogoutLink = "https://www.thmmy.gr/smf/index.php?action=logout;sesc=";
- private static final String baseMarkAllAsReadLink = "https://www.thmmy.gr/smf/index.php?action=markasread;sa=all;sesc=";
+ static final String baseLogoutLink = "https://www.thmmy.gr/smf/index.php?action=logout;sesc=";
+ static final String baseMarkAllAsReadLink = "https://www.thmmy.gr/smf/index.php?action=markasread;sa=all;sesc=";
private static final String guestName = "Guest";
- //Response Codes
- public static final int SUCCESS = 0;
- public static final int FAILURE = 1; //Generic Error
- public static final int WRONG_USER = 2;
- public static final int WRONG_PASSWORD = 3;
- public static final int CANCELLED = 4;
- public static final int CONNECTION_ERROR = 5;
- public static final int EXCEPTION = 6;
- public static final int BANNED_USER = 7;
+ //Response Codes - make sure they do not overlap with NetworkResultCodes, just in case
+ public static final int SUCCESS = 20;
+ public static final int FAILURE = 21; //Generic Error
+ public static final int WRONG_USER = 22;
+ public static final int WRONG_PASSWORD = 23;
+ public static final int CANCELLED = 24;
+ public static final int CONNECTION_ERROR = 25;
+ public static final int EXCEPTION = 26;
+ public static final int BANNED_USER = 27;
+ public static final int INVALID_SESSION = 28;
// Client & Cookies
private final OkHttpClient client;
@@ -59,59 +60,48 @@ public class SessionManager {
private final SharedPrefsCookiePersistor cookiePersistor; //Used to explicitly edit cookies in cookieJar
//Shared Preferences & its keys
- private final SharedPreferences sharedPrefs;
+ private final SharedPreferences sessionSharedPrefs;
private final SharedPreferences draftsPrefs;
private static final String USERNAME = "Username";
private static final String USER_ID = "UserID";
private static final String AVATAR_LINK = "AvatarLink";
private static final String HAS_AVATAR = "HasAvatar";
- private static final String SESC = "Sesc";
- private static final String LOGOUT_LINK = "LogoutLink";
- private static final String MARK_ALL_AS_READ_LINK = "MarkAllAsReadLink";
private static final String LOGGED_IN = "LoggedIn";
private static final String LOGIN_SCREEN_AS_DEFAULT = "LoginScreenAsDefault";
//Constructor
public SessionManager(OkHttpClient client, PersistentCookieJar cookieJar,
- SharedPrefsCookiePersistor cookiePersistor, SharedPreferences sharedPrefs, SharedPreferences draftsPrefs) {
+ SharedPrefsCookiePersistor cookiePersistor, SharedPreferences sessionSharedPrefs, SharedPreferences draftsPrefs) {
this.client = client;
this.cookiePersistor = cookiePersistor;
this.cookieJar = cookieJar;
- this.sharedPrefs = sharedPrefs;
+ this.sessionSharedPrefs = sessionSharedPrefs;
this.draftsPrefs = draftsPrefs;
}
- //------------------------------------AUTH BEGINS----------------------------------------------
+ //------------------------------------ AUTH ----------------------------------------------
/**
* Login function with two options: (username, password) or nothing (using saved cookies).
* Always call it in a separate thread.
*/
- public int login(String... strings) {
+ public int login(String username, String password) {
Timber.d("Logging in...");
//Build the login request for each case
Request request;
- if (strings.length == 2) {
- clearSessionData();
-
- String loginName = strings[0];
- String password = strings[1];
-
- RequestBody formBody = new FormBody.Builder()
- .add("user", loginName)
- .add("passwrd", password)
- .add("cookielength", "-1") //-1 is forever
- .build();
- request = new Request.Builder()
- .url(loginUrl)
- .post(formBody)
- .build();
- } else {
- request = new Request.Builder()
- .url(loginUrl)
- .build();
- }
+ clearSessionData();
+
+ RequestBody formBody = new FormBody.Builder()
+ .add("user", username)
+ .add("passwrd", password)
+ .add("cookielength", "-1") //-1 is forever
+ .build();
+ request = new Request.Builder()
+ .url(loginUrl)
+ .post(formBody)
+ .build();
+
try {
//Make request & handle response
@@ -123,7 +113,7 @@ public class SessionManager {
setPersistentCookieSession(); //Store cookies
//Edit SharedPreferences, save session's data
- SharedPreferences.Editor editor = sharedPrefs.edit();
+ SharedPreferences.Editor editor = sessionSharedPrefs.edit();
setLoginScreenAsDefault(false);
editor.putBoolean(LOGGED_IN, true);
editor.putString(USERNAME, extractUserName(document));
@@ -132,10 +122,6 @@ public class SessionManager {
if (avatar != null)
editor.putString(AVATAR_LINK, avatar);
editor.putBoolean(HAS_AVATAR, avatar != null);
- String sesc = extractSesc(document);
- editor.putString(SESC, sesc);
- editor.putString(LOGOUT_LINK, generateLogoutLink(sesc));
- editor.putString(MARK_ALL_AS_READ_LINK, generateMarkAllAsReadLink(sesc));
editor.apply();
return SUCCESS;
@@ -178,29 +164,6 @@ public class SessionManager {
}
}
- /**
- * A function that checks the validity of the current saved session (if it exists).
- * If isLoggedIn() is true, it will call login() with cookies. On failure, this can only return
- * the code FAILURE. CANCELLED, CONNECTION_ERROR and EXCEPTION are simply considered a SUCCESS
- * (e.g. no internet connection), at least until a more thorough handling of different
- * exceptions is implemented (if considered mandatory).
- * Always call it in a separate thread in a way that won't hinder performance (e.g. after
- * fragments' data are retrieved).
- */
- void validateSession() {
- Timber.i("Validating session...");
- if (isLoggedIn()) {
- Timber.i("Refreshing session...");
- int loginResult = login();
- if (loginResult != FAILURE)
- return;
- } else if (isLoginScreenDefault())
- return;
-
- setLoginScreenAsDefault(true);
- clearSessionData();
- }
-
/**
* Call this function when user explicitly chooses to continue as a guest (UI thread).
*/
@@ -210,62 +173,32 @@ public class SessionManager {
setLoginScreenAsDefault(false);
}
- /**
- * Logout function. Always call it in a separate thread.
- */
- public int logout() {
- Timber.i("Logging out...");
- try {
- Request request = new Request.Builder()
- .url(getLogoutLink())
- .build();
- //Make request & handle response
- Response response = client.newCall(request).execute();
- Document document = Jsoup.parse(response.body().string());
-
- Elements loginButton = document.select("[value=Login]"); //Attempt to find login button
- if (!loginButton.isEmpty()) //If login button exists, logout was successful
- {
- Timber.i("Logout successful!");
- return SUCCESS;
- } else {
- Timber.i("Logout failed.");
- return FAILURE;
- }
- } catch (IOException e) {
- Timber.w(e, "Logout IOException");
- return CONNECTION_ERROR;
- } catch (Exception e) {
- Timber.e(e, "Logout Exception");
- return EXCEPTION;
- } finally {
- //All data should always be cleared from device regardless the result of logout
- clearSessionData();
- guestLogin();
- }
+ void logoutCleanup() {
+ clearSessionData();
+ guestLogin();
}
- public void refreshSescFromUrl(String url){
- String sesc = extractSescFromLink(url);
- if(sesc!=null){
- setSesc(sesc);
- setLogoutLink(generateLogoutLink(sesc));
- setMarkAsReadLink(sesc);
- }
+ private void clearSessionData() {
+ cookieJar.clear();
+ sessionSharedPrefs.edit().clear().apply(); //Clear session data
+ sessionSharedPrefs.edit().putString(USERNAME, guestName).apply();
+ sessionSharedPrefs.edit().putInt(USER_ID, -1).apply();
+ sessionSharedPrefs.edit().putBoolean(LOGGED_IN, false).apply(); //User logs out
+ draftsPrefs.edit().clear().apply(); //Clear saved drafts
+ Timber.i("Session data cleared.");
}
- //--------------------------------------AUTH ENDS-----------------------------------------------
- //---------------------------------------GETTERS------------------------------------------------
+ //--------------------------------------- GETTERS ------------------------------------------------
public String getUsername() {
- return sharedPrefs.getString(USERNAME, USERNAME);
+ return sessionSharedPrefs.getString(USERNAME, USERNAME);
}
public int getUserId() {
- return sharedPrefs.getInt(USER_ID, -1);
+ return sessionSharedPrefs.getInt(USER_ID, -1);
}
public String getAvatarLink() {
- return sharedPrefs.getString(AVATAR_LINK, AVATAR_LINK);
+ return sessionSharedPrefs.getString(AVATAR_LINK, AVATAR_LINK);
}
public Cookie getThmmyCookie() {
@@ -277,64 +210,22 @@ public class SessionManager {
return null;
}
- public String getMarkAllAsReadLink() {
- String markAsReadLink = sharedPrefs.getString(MARK_ALL_AS_READ_LINK, null);
- if(markAsReadLink == null){ //For older versions, extract it from logout link (otherwise user would have to login again)
- String sesc = extractSescFromLink(getLogoutLink());
- if(sesc!=null) {
- setSesc(sesc);
- markAsReadLink = generateMarkAllAsReadLink(sesc);
- setMarkAsReadLink(markAsReadLink);
- return markAsReadLink;
- }
- }
- return markAsReadLink; // Warning: it can be null
- }
-
- private String getLogoutLink() {
- return sharedPrefs.getString(LOGOUT_LINK, null);
- }
-
public boolean hasAvatar() {
- return sharedPrefs.getBoolean(HAS_AVATAR, false);
+ return sessionSharedPrefs.getBoolean(HAS_AVATAR, false);
}
public boolean isLoggedIn() {
- return sharedPrefs.getBoolean(LOGGED_IN, false);
+ return sessionSharedPrefs.getBoolean(LOGGED_IN, false);
}
public boolean isLoginScreenDefault() {
- return sharedPrefs.getBoolean(LOGIN_SCREEN_AS_DEFAULT, true);
+ return sessionSharedPrefs.getBoolean(LOGIN_SCREEN_AS_DEFAULT, true);
}
- //--------------------------------------GETTERS END---------------------------------------------
-
- //---------------------------------------SETTERS------------------------------------------------
- private void setSesc(String sesc){
- SharedPreferences.Editor editor = sharedPrefs.edit();
- editor.putString(SESC, sesc);
- editor.apply();
- }
-
- private void setMarkAsReadLink(String markAllAsReadLink){
- SharedPreferences.Editor editor = sharedPrefs.edit();
- editor.putString(MARK_ALL_AS_READ_LINK, markAllAsReadLink);
- editor.apply();
- }
-
- private void setLogoutLink(String logoutLink){
- SharedPreferences.Editor editor = sharedPrefs.edit();
- editor.putString(LOGOUT_LINK, logoutLink);
- editor.apply();
- }
-
- //--------------------------------------SETTERS END---------------------------------------------
-
- //------------------------------------OTHER FUNCTIONS-------------------------------------------
+ //------------------------------------ OTHER -------------------------------------------
private boolean validateRetrievedCookies() {
List cookieList = cookieJar.loadForRequest(indexUrl);
- for(Cookie cookie: cookieList)
- {
+ for(Cookie cookie: cookieList) {
if(cookie.name().equals("THMMYgrC00ki3"))
return true;
}
@@ -353,21 +244,10 @@ public class SessionManager {
cookieList.add(builder.build());
cookiePersistor.clear();
cookiePersistor.saveAll(cookieList);
-
- }
-
- private void clearSessionData() {
- cookieJar.clear();
- sharedPrefs.edit().clear().apply(); //Clear session data
- sharedPrefs.edit().putString(USERNAME, guestName).apply();
- sharedPrefs.edit().putInt(USER_ID, -1).apply();
- sharedPrefs.edit().putBoolean(LOGGED_IN, false).apply(); //User logs out
- draftsPrefs.edit().clear().apply(); //Clear saved drafts
- Timber.i("Session data cleared.");
}
private void setLoginScreenAsDefault(boolean b){
- sharedPrefs.edit().putBoolean(LOGIN_SCREEN_AS_DEFAULT, b).apply();
+ sessionSharedPrefs.edit().putBoolean(LOGIN_SCREEN_AS_DEFAULT, b).apply();
}
@NonNull
@@ -421,7 +301,6 @@ public class SessionManager {
return -1;
}
-
@Nullable
private String extractAvatarLink(@NonNull Document doc) {
Elements avatar = doc.getElementsByClass("avatar");
@@ -431,35 +310,4 @@ public class SessionManager {
Timber.i("Extracting avatar's link failed!");
return null;
}
-
- private String extractSesc(@NonNull Document doc) {
- Elements logoutLink = doc.select("a[href^=https://www.thmmy.gr/smf/index.php?action=logout;sesc=]");
- if (!logoutLink.isEmpty()) {
- String link = logoutLink.first().attr("href");
- return extractSescFromLink(link);
- }
- Timber.e(new ParseException("Parsing failed(extractSesc)"),"ParseException");
- return null;
- }
-
- private String extractSescFromLink(String link){
- if (link != null){
- Pattern pattern = Pattern.compile(".+;sesc=(\\w+)");
- Matcher matcher = pattern.matcher(link);
- if (matcher.find())
- return matcher.group(1);
- }
- Timber.e(new ParseException("Parsing failed(extractSescFromLogoutLink)"),"ParseException");
- return null;
- }
-
- private String generateLogoutLink(String sesc){
- return baseLogoutLink + sesc;
- }
-
- private String generateMarkAllAsReadLink(String sesc){
- return baseMarkAllAsReadLink + sesc;
- }
- //----------------------------------OTHER FUNCTIONS END-----------------------------------------
-
}
\ No newline at end of file
diff --git a/app/src/main/java/gr/thmmy/mthmmy/session/ValidateSessionTask.java b/app/src/main/java/gr/thmmy/mthmmy/session/ValidateSessionTask.java
deleted file mode 100644
index b5c1392f..00000000
--- a/app/src/main/java/gr/thmmy/mthmmy/session/ValidateSessionTask.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package gr.thmmy.mthmmy.session;
-
-import android.os.AsyncTask;
-
-import gr.thmmy.mthmmy.base.BaseApplication;
-
-
-public class ValidateSessionTask extends AsyncTask {
- @Override
- protected Void doInBackground(String... params) {
- BaseApplication.getInstance().getSessionManager().validateSession();
- return null;
- }
-
- public boolean isRunning(){
- return getStatus() == AsyncTask.Status.RUNNING;
- }
-}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/Parcel.java b/app/src/main/java/gr/thmmy/mthmmy/utils/Parcel.java
index 83e65285..8733bf8c 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/Parcel.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/Parcel.java
@@ -1,7 +1,6 @@
package gr.thmmy.mthmmy.utils;
public class Parcel {
-
private int resultCode;
private T data;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReporter.java b/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReporter.java
index 65011440..4842013c 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReporter.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReporter.java
@@ -1,6 +1,7 @@
package gr.thmmy.mthmmy.utils.crashreporting;
-import com.crashlytics.android.Crashlytics;
+
+import com.google.firebase.crashlytics.FirebaseCrashlytics;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
@@ -43,12 +44,14 @@ public class CrashReporter {
else
languageValue = "Unknown";
- Crashlytics.setString(themeKey, themeValue);
- Crashlytics.setString(languageKey, languageValue);
- Crashlytics.setBool("isLoggedIn", BaseApplication.getInstance().getSessionManager().isLoggedIn());
+ FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
+ crashlytics.setCustomKey(themeKey, themeValue);
+ crashlytics.setCustomKey(languageKey, languageValue);
+ crashlytics.setCustomKey("isLoggedIn", BaseApplication.getInstance().getSessionManager().isLoggedIn());
}
public static void reportDocument(Document document, String key) {
+ FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
String documentString = document.toString();
ParseHelpers.Language language = ParseHelpers.Language.getLanguage(document);
@@ -71,7 +74,7 @@ public class CrashReporter {
batch = documentString.substring(i * STRING_BATCH_LENGTH, (i + 1) * STRING_BATCH_LENGTH);
else
batch = documentString.substring(i * STRING_BATCH_LENGTH);
- Crashlytics.setString(key + "_" + i + 1, batch);
+ crashlytics.setCustomKey(key + "_" + i + 1, batch);
}
}
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReportingTree.java b/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReportingTree.java
index f378000e..83e55a37 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReportingTree.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReportingTree.java
@@ -2,12 +2,17 @@ package gr.thmmy.mthmmy.utils.crashreporting;
import android.util.Log;
-import com.crashlytics.android.Crashlytics;
+import com.google.firebase.crashlytics.FirebaseCrashlytics;
import timber.log.Timber.DebugTree;
public class CrashReportingTree extends DebugTree {
-
+ private FirebaseCrashlytics firebaseCrashlytics;
+ public CrashReportingTree() {
+ super();
+ firebaseCrashlytics = FirebaseCrashlytics.getInstance();
+ }
+
@Override
protected void log(int priority, String tag, String message, Throwable t) {
if (priority == Log.VERBOSE || priority == Log.DEBUG) {
@@ -25,14 +30,13 @@ public class CrashReportingTree extends DebugTree {
else
level = 'A';
- Crashlytics.log(level + "/" + tag + ": " + message);
+ firebaseCrashlytics.log(level + "/" + tag + ": " + message);
if(priority == Log.ERROR) {
if (t!=null)
- Crashlytics.logException(t);
+ firebaseCrashlytics.recordException(t);
else
- Crashlytics.logException(new Exception(message));
+ firebaseCrashlytics.recordException(new Exception(message));
}
-
}
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/NetworkResultCodes.java b/app/src/main/java/gr/thmmy/mthmmy/utils/networking/NetworkResultCodes.java
similarity index 94%
rename from app/src/main/java/gr/thmmy/mthmmy/utils/NetworkResultCodes.java
rename to app/src/main/java/gr/thmmy/mthmmy/utils/networking/NetworkResultCodes.java
index 90cd8771..958435e1 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/NetworkResultCodes.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/networking/NetworkResultCodes.java
@@ -1,4 +1,4 @@
-package gr.thmmy.mthmmy.utils;
+package gr.thmmy.mthmmy.utils.networking;
public class NetworkResultCodes {
/**
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/NetworkTask.java b/app/src/main/java/gr/thmmy/mthmmy/utils/networking/NetworkTask.java
similarity index 82%
rename from app/src/main/java/gr/thmmy/mthmmy/utils/NetworkTask.java
rename to app/src/main/java/gr/thmmy/mthmmy/utils/networking/NetworkTask.java
index a8c42c8b..0ba26dd3 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/NetworkTask.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/networking/NetworkTask.java
@@ -1,7 +1,8 @@
-package gr.thmmy.mthmmy.utils;
+package gr.thmmy.mthmmy.utils.networking;
import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
+
+import androidx.preference.PreferenceManager;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
@@ -10,6 +11,10 @@ import java.io.IOException;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseApplication;
+import gr.thmmy.mthmmy.session.InvalidSessionException;
+import gr.thmmy.mthmmy.session.SessionManager;
+import gr.thmmy.mthmmy.utils.ExternalAsyncTask;
+import gr.thmmy.mthmmy.utils.Parcel;
import gr.thmmy.mthmmy.utils.crashreporting.CrashReporter;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
import okhttp3.OkHttpClient;
@@ -34,7 +39,19 @@ public abstract class NetworkTask extends ExternalAsyncTask
public NetworkTask() {}
@Override
- protected final Parcel doInBackground(String... input) {
+ protected Parcel doInBackground(String... input) {
+ return executeInBackground(input);
+ }
+
+ @Override
+ protected void onPostExecute(Parcel tParcel) {
+ if (onNetworkTaskFinishedListener != null)
+ onNetworkTaskFinishedListener.onNetworkTaskFinished(tParcel.getResultCode(), tParcel.getData());
+ else
+ super.onPostExecute(tParcel);
+ }
+
+ protected Parcel executeInBackground(String... input) {
Response response;
try {
response = sendRequest(BaseApplication.getInstance().getClient(), input);
@@ -63,20 +80,17 @@ public abstract class NetworkTask extends ExternalAsyncTask
.getString(R.string.pref_privacy_crashlytics_enable_key), false))
CrashReporter.reportForumInfo(Jsoup.parse(responseBodyString));
return new Parcel<>(NetworkResultCodes.PARSE_ERROR, null);
- } catch (Exception e) {
+ } catch (InvalidSessionException ise) {
+ //TODO: Uncomment the lines below when UI is ready to auto-adjust to changes in session data
+ // BaseApplication.getInstance().getSessionManager().clearSessionData();
+ // BaseApplication.getInstance().getSessionManager().guestLogin();
+ return new Parcel<>(SessionManager.INVALID_SESSION, null);
+ }catch (Exception e) {
Timber.e(e);
return new Parcel<>(NetworkResultCodes.PERFORM_TASK_ERROR, null);
}
}
- @Override
- protected void onPostExecute(Parcel tParcel) {
- if (onNetworkTaskFinishedListener != null)
- onNetworkTaskFinishedListener.onNetworkTaskFinished(tParcel.getResultCode(), tParcel.getData());
- else
- super.onPostExecute(tParcel);
- }
-
protected Response sendRequest(OkHttpClient client, String... input) throws IOException {
String url = input[0];
Request request = new Request.Builder()
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/NewParseTask.java b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/NewParseTask.java
index def14c2f..754e7825 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/NewParseTask.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/NewParseTask.java
@@ -2,7 +2,8 @@ package gr.thmmy.mthmmy.utils.parsing;
import org.jsoup.nodes.Document;
-import gr.thmmy.mthmmy.utils.NetworkTask;
+import gr.thmmy.mthmmy.session.InvalidSessionException;
+import gr.thmmy.mthmmy.utils.networking.NetworkTask;
import okhttp3.Response;
public abstract class NewParseTask extends NetworkTask {
@@ -22,8 +23,10 @@ public abstract class NewParseTask extends NetworkTask {
protected final T performTask(Document document, Response response) {
try {
return parse(document, response);
+ } catch (InvalidSessionException ise) {
+ throw ise;
} catch (Exception e) {
- throw new ParseException("Parse failed.", e);
+ throw new ParseException("Parsing failed", e);
}
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/ui/GlideUtils.java b/app/src/main/java/gr/thmmy/mthmmy/utils/ui/GlideUtils.java
new file mode 100644
index 00000000..1f3f57c7
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ui/GlideUtils.java
@@ -0,0 +1,17 @@
+package gr.thmmy.mthmmy.utils.ui;
+
+import android.app.Activity;
+import android.content.Context;
+
+public class GlideUtils {
+ public static boolean isValidContextForGlide(final Context context) {
+ if (context == null)
+ return false;
+
+ if (context instanceof Activity) {
+ final Activity activity = (Activity) context;
+ return !activity.isDestroyed() && !activity.isFinishing();
+ }
+ return true;
+ }
+}
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 8f8f4c3a..49f619c2 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java
@@ -30,7 +30,7 @@ import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.TopicItem;
import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.ExternalAsyncTask;
-import gr.thmmy.mthmmy.utils.NetworkTask;
+import gr.thmmy.mthmmy.utils.networking.NetworkTask;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import timber.log.Timber;
diff --git a/app/src/main/res/drawable/ic_default_user_avatar.xml b/app/src/main/res/drawable/ic_default_user_avatar.xml
index aa4a9116..d2f2ecfe 100644
--- a/app/src/main/res/drawable/ic_default_user_avatar.xml
+++ b/app/src/main/res/drawable/ic_default_user_avatar.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml
index 87d99723..8bd337e9 100644
--- a/app/src/main/res/layout/activity_about.xml
+++ b/app/src/main/res/layout/activity_about.xml
@@ -28,7 +28,7 @@
android:id="@+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/primary_light"
+ android:background="@color/primary_lighter"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
+ android:textStyle="italic"
+ android:visibility="gone" />
+ android:textSize="12sp"
+ android:visibility="gone" />
+ android:textSize="12sp"
+ android:visibility="gone" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index 97676cc6..3e3f2ff5 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -28,7 +28,7 @@
android:id="@+id/pref_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/primary_light"
+ android:background="@color/primary_lighter"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_upload.xml b/app/src/main/res/layout/activity_upload.xml
index 2282efae..1760ff1f 100644
--- a/app/src/main/res/layout/activity_upload.xml
+++ b/app/src/main/res/layout/activity_upload.xml
@@ -28,7 +28,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top|start"
- android:background="@color/primary_light"
+ android:background="@color/primary_lighter"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
@@ -37,7 +37,7 @@
@@ -46,7 +46,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:background="@color/primary_light"
+ android:background="@color/primary_lighter"
android:orientation="vertical">
diff --git a/app/src/main/res/layout/fragment_bookmarks.xml b/app/src/main/res/layout/fragment_bookmarks.xml
index 8d1372c0..aa1bbaab 100644
--- a/app/src/main/res/layout/fragment_bookmarks.xml
+++ b/app/src/main/res/layout/fragment_bookmarks.xml
@@ -9,7 +9,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top|start"
- android:background="@color/primary_light"
+ android:background="@color/primary_lighter"
android:scrollbars="none"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
diff --git a/app/src/main/res/layout/fragment_forum_board_row.xml b/app/src/main/res/layout/fragment_forum_board_row.xml
index 871c5450..3f6eb4e6 100644
--- a/app/src/main/res/layout/fragment_forum_board_row.xml
+++ b/app/src/main/res/layout/fragment_forum_board_row.xml
@@ -2,7 +2,7 @@
+ android:background="@color/primary_lighter">
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_unread_row.xml b/app/src/main/res/layout/fragment_unread_row.xml
index d9da3ace..e92ac572 100644
--- a/app/src/main/res/layout/fragment_unread_row.xml
+++ b/app/src/main/res/layout/fragment_unread_row.xml
@@ -8,7 +8,7 @@
#2B2B2B
- #333333
- #3C3C3C
- #494949
+ #333333
+ #3C3C3C
+ #494949
+ #222222
#26A69A
#E7E7E7
#757575
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d1c69acb..f881b88d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -209,7 +209,7 @@
Analytics data reports
Automatically send us anonymized data for analytical purposes
-
+
Black
Red
Yellow
@@ -229,8 +229,12 @@
Link text
Required
-
+
New topic
Create topic
Link copied
+
+
+ SessionSharedPrefs
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 1e092378..28fe1690 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -2,7 +2,7 @@
@@ -86,6 +86,6 @@
diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml
deleted file mode 100644
index e87d713f..00000000
--- a/app/src/main/res/xml/network_security_config.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
- www.thmmy.gr
-
-
diff --git a/build.gradle b/build.gradle
index 0400e33c..72da44f0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,15 +3,14 @@ apply plugin: "com.github.ben-manes.versions"
buildscript {
repositories {
- maven { url "https://maven.fabric.io/public" }
google()
jcenter()
maven { url "https://jitpack.io" }
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.3'
+ classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.google.gms:google-services:4.3.3'
- classpath 'io.fabric.tools:gradle:1.31.2'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.1.1'
classpath 'org.ajoberstar.grgit:grgit-core:3.1.1' // Also change in app/gradle/grgit.gradle
classpath "com.github.ben-manes:gradle-versions-plugin:0.21.0"
}
@@ -20,6 +19,7 @@ buildscript {
allprojects {
repositories {
maven { url "https://maven.google.com" }
+ google()
jcenter()
maven { url "https://jitpack.io" }
}
diff --git a/emojis/build.gradle b/emojis/build.gradle
index 6aef90cf..46608744 100644
--- a/emojis/build.gradle
+++ b/emojis/build.gradle
@@ -4,7 +4,6 @@ android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
-
defaultConfig {
minSdkVersion 19
targetSdkVersion 29
@@ -20,10 +19,4 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
-
-}
-
-dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation 'androidx.appcompat:appcompat:1.1.0'
-}
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 669ee9b7..0e03b907 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sat May 09 21:06:12 EEST 2020
+#Mon Jul 20 13:59:13 EEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip