diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d02a2bf6..cd22f77e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,7 +2,7 @@ image: openjdk:8-jdk
variables:
ANDROID_TARGET_SDK: "25"
- ANDROID_BUILD_TOOLS: "25.0.1"
+ ANDROID_BUILD_TOOLS: "25.0.2"
ANDROID_SDK_TOOLS: "25.2.5"
before_script:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e28d7593..44be1c74 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -45,4 +45,3 @@ follow the workflow below to make a merge request:
[trello-board]: https://trello.com/invite/b/4MVlkrkg/44a931707bd0b84a5e0bdfc42b9ae4f1/mthmmy
[discord-server]: https://discord.gg/CVt3yrn
-[gitlab-contributing-guide]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md
diff --git a/README.md b/README.md
index 69c60e28..89f8a134 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,11 @@
[![build status](https://gitlab.com/ThmmyNoLife/mTHMMY/badges/develop/build.svg)](https://gitlab.com/ThmmyNoLife/mTHMMY/commits/develop)
[![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)](https://discord.gg/CVt3yrn)
+[![Discord Channel](https://img.shields.io/badge/discord-public@mTHMMY-738bd7.svg?style=flat)][discord-server]
+[![Trello Board](https://img.shields.io/badge/trello-mTHMMY-red.svg?style=flat)][trello-board]
-mTHMMY is a mobile app for thmmy.gr
+
+mTHMMY is a mobile app for the [thmmy.gr](https://www.thmmy.gr) community.
## Requirements
@@ -16,4 +18,7 @@ Please refer to [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
## Contact
-Do not hesitate to contact us for any matter at `thmmynolife@gmail.com`.
+Do not hesitate to contact us for any matter, either by sending an email to `thmmynolife@gmail.com`, or by joining our [Discord server][discord-server].
+
+[discord-server]: https://discord.gg/CVt3yrn
+[trello-board]: https://trello.com/invite/b/4MVlkrkg/44a931707bd0b84a5e0bdfc42b9ae4f1/mthmmy
diff --git a/VERSION b/VERSION
index 45a1b3f4..26aaba0e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.1.2
+1.2.0
diff --git a/app/build.gradle b/app/build.gradle
index bbf5b90d..f1403f49 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,15 +2,15 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 25
- buildToolsVersion "25.0.1"
+ buildToolsVersion "25.0.2"
defaultConfig {
vectorDrawables.useSupportLibrary = true
applicationId "gr.thmmy.mthmmy"
minSdkVersion 19
targetSdkVersion 25
- versionCode 5
- versionName "1.1.2"
+ versionCode 6
+ versionName "1.2.0"
archivesBaseName = "mTHMMY-v$versionName"
}
@@ -19,34 +19,35 @@ android {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
- debug {
- def date = new Date().format('ddMMyy_HHmm');
+ /*debug {
+ def date = new Date().format('ddMMyy_HH');
archivesBaseName = archivesBaseName + "-$date"
- }
+ }*/
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.android.support:appcompat-v7:25.1.0'
- compile 'com.android.support:design:25.1.0'
- compile 'com.android.support:support-v4:25.1.0'
- compile 'com.android.support:cardview-v7:25.1.0'
- compile 'com.android.support:recyclerview-v7:25.1.0'
- compile 'com.google.firebase:firebase-crash:10.0.1'
- compile 'com.squareup.okhttp3:okhttp:3.5.0'
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ compile 'com.android.support:design:25.3.0'
+ compile 'com.android.support:support-v4:25.3.0'
+ compile 'com.android.support:cardview-v7:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.google.firebase:firebase-crash:10.2.0'
+ compile 'com.squareup.okhttp3:okhttp:3.6.0'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
compile 'org.jsoup:jsoup:1.10.2'
compile 'com.github.franmontiel:PersistentCookieJar:v1.0.0'
compile 'com.github.PhilJay:MPAndroidChart:v3.0.1'
- compile('com.mikepenz:materialdrawer:5.8.1@aar') {
+ compile('com.mikepenz:materialdrawer:5.8.2@aar') {
transitive = true
}
compile 'com.mikepenz:fontawesome-typeface:4.7.0.0@aar'
- compile 'pl.droidsonroids.gif:android-gif-drawable:1.2.3'
+ compile 'pl.droidsonroids.gif:android-gif-drawable:1.2.5'
compile 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1'
compile 'me.zhanghai.android.materialprogressbar:library:1.3.0'
+ compile 'com.jakewharton.timber:timber:4.5.1'
}
apply plugin: 'com.google.gms.google-services'
diff --git a/app/src/debug/java/mthmmy/utils/Report.java b/app/src/debug/java/mthmmy/utils/Report.java
deleted file mode 100644
index 6e6a906f..00000000
--- a/app/src/debug/java/mthmmy/utils/Report.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package mthmmy.utils;
-
-import android.util.Log;
-
-public class Report
-{
-
- public static void v (String TAG, String message)
- {
- Log.v(TAG,message);
- }
-
- public static void v (String TAG, String message, Throwable tr)
- {
- Log.v(TAG,message + ": " + tr.getMessage(),tr);
- }
-
- public static void d (String TAG, String message)
- {
- Log.d(TAG,message);
- }
-
- public static void d (String TAG, String message, Throwable tr)
- {
- Log.d(TAG,message + ": " + tr.getMessage(),tr);
- }
-
- public static void i (String TAG, String message)
- {
- Log.i(TAG,message);
- }
-
- public static void i (String TAG, String message, Throwable tr)
- {
- Log.i(TAG,message + ": " + tr.getMessage(),tr);
- }
-
- public static void w (String TAG, String message)
- {
- Log.w(TAG,message);
- }
-
- public static void w (String TAG, String message, Throwable tr)
- {
- Log.w(TAG,message + ": " + tr.getMessage(),tr);
- }
-
- public static void e (String TAG, String message)
- {
- Log.e(TAG,message);
- }
-
- public static void e (String TAG, String message, Throwable tr)
- {
- Log.e(TAG,message + ": " + tr.getMessage(),tr);
- }
-
- public static void wtf (String TAG, String message)
- {
- Log.wtf(TAG,message);
- }
-
- public static void wtf (String TAG, String message, Throwable tr)
- {
- Log.wtf(TAG,message + ": " + tr.getMessage(),tr);
- }
-
- /**
- * Prints long messages in logcat (debug level).
- */
- public static void longMessage(String TAG, String message)
- {
- int maxLogSize = 1000;
- for(int i = 0; i <= message.length() / maxLogSize; i++) {
- int start = i * maxLogSize;
- int end = (i+1) * maxLogSize;
- end = end > message.length() ? message.length() : end;
- Report.d(TAG, message.substring(start, end));
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f95392f4..8a57fcbc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,7 @@
+ package="gr.thmmy.mthmmy"
+ android:installLocation="auto">
diff --git a/app/src/main/assets/apache_libraries.html b/app/src/main/assets/apache_libraries.html
index 10867db3..2c0ceabc 100644
--- a/app/src/main/assets/apache_libraries.html
+++ b/app/src/main/assets/apache_libraries.html
@@ -39,7 +39,7 @@
-
-
OkHttp v3.5.0 (Copyright ©2016 Square, Inc.)
+ OkHttp v3.6.0 (Copyright ©2016 Square, Inc.)
-
Picasso v2.5.2 (Copyright ©2013 Square, Inc.)
@@ -51,7 +51,7 @@
MPAndroidChart v3.0.1 (Copyright ©2016 Philipp Jahoda)
-
-
MaterialDrawer v5.8.1 (Copyright ©2016 Mike Penz)
+ MaterialDrawer v5.8.2 (Copyright ©2016 Mike Penz)
-
diff --git a/app/src/main/assets/mit_libraries.html b/app/src/main/assets/mit_libraries.html
index 6d72c330..2c6cf3bb 100644
--- a/app/src/main/assets/mit_libraries.html
+++ b/app/src/main/assets/mit_libraries.html
@@ -42,7 +42,7 @@
jsoup v1.10.2 (Copyright ©2009-2017, Jonathan Hedley <jonathan@hedley.net>)
-
-
android-gif-drawable v1.2.3 (Copyright ©2016 Karol Wrótniak, Droids on Roids)
+ android-gif-drawable v1.2.5 (Copyright ©2016 Karol Wrótniak, Droids on Roids)
-
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/BookmarkActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/BookmarkActivity.java
index 5d854eef..ec0c2b25 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/BookmarkActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/BookmarkActivity.java
@@ -114,7 +114,7 @@ public class BookmarkActivity extends BaseActivity {
Intent intent = new Intent(BookmarkActivity.this, TopicActivity.class);
Bundle extras = new Bundle();
extras.putString(BUNDLE_TOPIC_URL, "https://www.thmmy.gr/smf/index.php?topic="
- + bookmarkedTopic.getId() + ".0");
+ + bookmarkedTopic.getId() + "." + 2147483647);
extras.putString(BUNDLE_TOPIC_TITLE, bookmarkedTopic.getTitle());
intent.putExtras(extras);
startActivity(intent);
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java
index 7dacd734..39a770c2 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java
@@ -14,7 +14,8 @@ import android.widget.Toast;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.main.MainActivity;
import gr.thmmy.mthmmy.base.BaseActivity;
-import mthmmy.utils.Report;
+
+import timber.log.Timber;
import static gr.thmmy.mthmmy.session.SessionManager.CONNECTION_ERROR;
import static gr.thmmy.mthmmy.session.SessionManager.EXCEPTION;
@@ -34,12 +35,8 @@ public class LoginActivity extends BaseActivity {
private String password;
/* --Graphics End-- */
- //Other variables
- private static final String TAG = "LoginActivity";
-
private LoginTask loginTask;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -55,7 +52,7 @@ public class LoginActivity extends BaseActivity {
btnLogin.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
- Report.d(TAG, "Login");
+ Timber.d("Login");
//Get username and password strings
username = inputUsername.getText().toString().trim();
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java
index c3587b1b..d4cf2236 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java
@@ -30,16 +30,11 @@ import gr.thmmy.mthmmy.model.Bookmark;
import gr.thmmy.mthmmy.model.ThmmyPage;
import gr.thmmy.mthmmy.model.Topic;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
-import mthmmy.utils.Report;
import okhttp3.Request;
import okhttp3.Response;
+import timber.log.Timber;
public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMoreListener {
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "BoardActivity";
/**
* The key to use when putting board's url String to {@link BoardActivity}'s Bundle.
*/
@@ -76,7 +71,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
boardUrl = extras.getString(BUNDLE_BOARD_URL);
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(boardUrl));
if (!target.is(ThmmyPage.PageCategory.BOARD)) {
- Report.e(TAG, "Bundle came with a non board url!\nUrl:\n" + boardUrl);
+ Timber.e("Bundle came with a non board url!\nUrl:\n%s" , boardUrl);
Toast.makeText(this, "An error has occurred\nAborting.", Toast.LENGTH_SHORT).show();
finish();
}
@@ -92,8 +87,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
}
thisPageBookmark = new Bookmark(boardTitle, ThmmyPage.getBoardId(boardUrl));
- thisPageBookmarkButton = (ImageButton) findViewById(R.id.bookmark);
- setBoardBookmark();
+ setBoardBookmark((ImageButton) findViewById(R.id.bookmark));
createDrawer();
progressBar = (MaterialProgressBar) findViewById(R.id.progressBar);
@@ -182,13 +176,6 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
* parameter!
*/
public class BoardTask extends AsyncTask {
- //Class variables
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "BoardTask"; //Separate tag for AsyncTask
-
@Override
protected void onPreExecute() {
if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE);
@@ -201,12 +188,12 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
.url(boardUrl[0])
.build();
try {
- Response response = BaseActivity.getClient().newCall(request).execute();
+ Response response = client.newCall(request).execute();
parseBoard(Jsoup.parse(response.body().string()));
} catch (SSLHandshakeException e) {
- Report.w(TAG, "Certificate problem (please switch to unsafe connection).");
+ Timber.w("Certificate problem (please switch to unsafe connection).");
} catch (Exception e) {
- Report.e("TAG", "ERROR", e);
+ Timber.e("ERROR", e);
}
return null;
}
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 306302fe..b6b716be 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
@@ -32,7 +32,6 @@ import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
* {@link RecyclerView.Adapter} that can display a {@link gr.thmmy.mthmmy.model.Board}.
*/
class BoardAdapter extends RecyclerView.Adapter {
- private static final String TAG = "BoardAdapter";
private final int VIEW_TYPE_SUB_BOARD_TITLE = 0;
private final int VIEW_TYPE_SUB_BOARD = 1;
private final int VIEW_TYPE_TOPIC_TITLE = 2;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java
index 2343f78d..b4339f14 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java
@@ -27,16 +27,12 @@ import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.model.Download;
import gr.thmmy.mthmmy.model.ThmmyPage;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
-import mthmmy.utils.Report;
+
import okhttp3.Request;
import okhttp3.Response;
+import timber.log.Timber;
public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.OnLoadMoreListener {
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "DownloadsActivity";
/**
* The key to use when putting download's url String to {@link DownloadsActivity}'s Bundle.
*/
@@ -73,7 +69,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
if (downloadsUrl != null && !Objects.equals(downloadsUrl, "")) {
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(downloadsUrl));
if (!target.is(ThmmyPage.PageCategory.DOWNLOADS)) {
- Report.e(TAG, "Bundle came with a non board url!\nUrl:\n" + downloadsUrl);
+ Timber.e("Bundle came with a non board url!\nUrl:\n%s" , downloadsUrl);
Toast.makeText(this, "An error has occurred\nAborting.", Toast.LENGTH_SHORT).show();
finish();
}
@@ -173,10 +169,6 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
* as String parameter!
*/
class ParseDownloadPageTask extends AsyncTask {
- /**
- * Debug Tag for logging debug output to LogCat
- */
- private static final String TAG = "ParseDownloadPageTask"; //Separate tag for AsyncTask
private String thisPageUrl;
@Override
@@ -192,12 +184,12 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
.url(downloadsUrl[0])
.build();
try {
- Response response = BaseActivity.getClient().newCall(request).execute();
+ Response response = client.newCall(request).execute();
parseDownloads(Jsoup.parse(response.body().string()));
} catch (SSLHandshakeException e) {
- Report.w(TAG, "Certificate problem (please switch to unsafe connection).");
+ Timber.w("Certificate problem (please switch to unsafe connection).");
} catch (Exception e) {
- Report.e("TAG", "ERROR", e);
+ Timber.e("ERROR", e);
}
return null;
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java
index a46dfeea..0253359b 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java
@@ -29,11 +29,6 @@ import static gr.thmmy.mthmmy.activities.downloads.DownloadsActivity.BUNDLE_DOWN
import static gr.thmmy.mthmmy.activities.downloads.DownloadsActivity.BUNDLE_DOWNLOADS_URL;
class DownloadsAdapter extends RecyclerView.Adapter {
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "DownloadsAdapter";
private final int VIEW_TYPE_DOWNLOAD = 0;
private final int VIEW_TYPE_LOADING = 1;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java
index 5e701d6e..ddc604b4 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java
@@ -9,7 +9,6 @@ import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
-import android.util.Log;
import android.widget.Toast;
import gr.thmmy.mthmmy.R;
@@ -38,7 +37,6 @@ import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
public class MainActivity extends BaseActivity implements RecentFragment.RecentFragmentInteractionListener, ForumFragment.ForumFragmentInteractionListener {
//----------------------------------------CLASS VARIABLES-----------------------------------------
- private static final String TAG = "MainActivity";
private static final int TIME_INTERVAL = 2000;
private long mBackPressed;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumAdapter.java
index 40f14c93..9f896b29 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumAdapter.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumAdapter.java
@@ -36,7 +36,7 @@ class ForumAdapter extends ExpandableRecyclerAdapter {
- private static final String TAG = "ForumTask";
+ private class ForumTask extends AsyncTask {
private HttpUrl forumUrl = SessionManager.forumUrl; //may change upon collapse/expand
private Document document;
@@ -179,10 +179,10 @@ public class ForumFragment extends BaseFragment
fetchedCategories.clear();
return 0;
} catch (IOException e) {
- Report.d(TAG, "Network Error", e);
+ Timber.d("Network Error", e);
return 1;
} catch (Exception e) {
- Report.d(TAG, "Exception", e);
+ Timber.d("Exception", e);
return 2;
}
@@ -225,7 +225,7 @@ public class ForumFragment extends BaseFragment
}
}
else
- Report.e(TAG, "Parsing failed!");
+ Timber.e("Parsing failed!");
}
public void setUrl(String string)
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 7cd9ef63..47f4678d 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
@@ -29,10 +29,11 @@ import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.CustomRecyclerView;
import gr.thmmy.mthmmy.utils.exceptions.ParseException;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
-import mthmmy.utils.Report;
+
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.Response;
+import timber.log.Timber;
/**
* A {@link BaseFragment} subclass.
@@ -86,7 +87,7 @@ public class RecentFragment extends BaseFragment {
recentTask.execute();
}
- Report.d(TAG, "onActivityCreated");
+ Timber.d("onActivityCreated");
}
@@ -141,8 +142,7 @@ public class RecentFragment extends BaseFragment {
//---------------------------------------ASYNC TASK-----------------------------------
- public class RecentTask extends AsyncTask {
- private static final String TAG = "RecentTask";
+ private class RecentTask extends AsyncTask {
private final HttpUrl thmmyUrl = SessionManager.indexUrl;
private Document document;
@@ -161,13 +161,13 @@ public class RecentFragment extends BaseFragment {
parse(document);
return 0;
} catch (ParseException e) {
- Report.e(TAG, "ParseException", e);
+ Timber.e("ParseException", e);
return 1;
} catch (IOException e) {
- Report.i(TAG, "Network Error", e);
+ Timber.i("Network Error", e);
return 2;
} catch (Exception e) {
- Report.e(TAG, "Exception", e);
+ Timber.e("Exception", e);
return 3;
}
@@ -179,7 +179,7 @@ public class RecentFragment extends BaseFragment {
if (result == 0)
recentAdapter.notifyDataSetChanged();
else if (result == 2)
- Toast.makeText(getActivity(), "Network error", Toast.LENGTH_SHORT).show();
+ Toast.makeText(getActivity(), "Network error", Toast.LENGTH_SHORT).show(); //Fixme, sometimes activity isn't ready
progressBar.setVisibility(ProgressBar.INVISIBLE);
swipeRefreshLayout.setRefreshing(false);
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 60fea8d3..08495dd9 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
@@ -19,6 +19,7 @@ import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
+
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
@@ -49,9 +50,10 @@ import gr.thmmy.mthmmy.model.ThmmyPage;
import gr.thmmy.mthmmy.utils.CenterVerticalSpan;
import gr.thmmy.mthmmy.utils.CircleTransform;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
-import mthmmy.utils.Report;
+
import okhttp3.Request;
import okhttp3.Response;
+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;
@@ -63,11 +65,6 @@ import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
* the username using the key {@link #BUNDLE_PROFILE_USERNAME}.
*/
public class ProfileActivity extends BaseActivity implements LatestPostsFragment.LatestPostsFragmentInteractionListener {
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "ProfileActivity";
/**
* The key to use when putting profile's url String to {@link ProfileActivity}'s Bundle.
*/
@@ -178,7 +175,7 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(profileUrl));
if (!target.is(ThmmyPage.PageCategory.PROFILE)) {
- Report.e(TAG, "Bundle came with a non profile url!\nUrl:\n" + profileUrl);
+ Timber.e("Bundle came with a non profile url!\nUrl:\n%s" , profileUrl);
Toast.makeText(this, "An error has occurred\n Aborting.", Toast.LENGTH_SHORT).show();
finish();
}
@@ -221,11 +218,6 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
*/
public class ProfileTask extends AsyncTask {
//Class variables
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "ProfileTask"; //Separate tag for AsyncTask
Document profilePage;
Spannable usernameSpan;
@@ -243,14 +235,15 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
try {
Response response = client.newCall(request).execute();
profilePage = Jsoup.parse(response.body().string());
- Elements contentsTable = profilePage.select(".bordercolor > tbody:nth-child(1) > tr:nth-child(2)");
+ Elements contentsTable = profilePage.
+ select(".bordercolor > tbody:nth-child(1) > tr:nth-child(2) tbody");
//Finds username if missing
if (username == null || Objects.equals(username, "")) {
username = contentsTable.select("tr").first().select("td").last().text();
}
if (thumbnailUrl == null || Objects.equals(thumbnailUrl, "")) { //Maybe there is an avatar
- Element profileAvatar = contentsTable.select("img.avatar").first();
+ Element profileAvatar = profilePage.select("img.avatar").first();
if (profileAvatar != null) thumbnailUrl = profileAvatar.attr("abs:src");
}
{ //Finds personal text
@@ -260,7 +253,7 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
} else {
//Should never get here!
//Something is wrong.
- Report.e(TAG, "An error occurred while trying to find profile's personal text.");
+ Timber.e("An error occurred while trying to find profile's personal text.");
personalText = null;
}
}
@@ -281,18 +274,18 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
}
return true;
} catch (SSLHandshakeException e) {
- Report.w(TAG, "Certificate problem (please switch to unsafe connection).");
+ Timber.w("Certificate problem (please switch to unsafe connection).");
} catch (Exception e) {
- Report.e("TAG", "ERROR", e);
+ Timber.e("ERROR", e);
}
return false;
}
protected void onPostExecute(Boolean result) {
- if (!result) { //Parse failed!
- Report.d(TAG, "Parse failed!");
- Toast.makeText(getBaseContext()
- , "Fatal error!\n Aborting...", Toast.LENGTH_LONG).show();
+ if (!result) { //Parse failed! //TODO report as ParseException?
+ Timber.d("Parse failed!");
+ Toast.makeText(getBaseContext(), "Fatal error!\n Aborting..."
+ , Toast.LENGTH_LONG).show();
finish();
}
//Parse was successful
@@ -342,7 +335,7 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
viewPager.setAdapter(adapter);
}
- class ViewPagerAdapter extends FragmentPagerAdapter {
+ private class ViewPagerAdapter extends FragmentPagerAdapter {
private final List mFragmentList = new ArrayList<>();
private final List mFragmentTitleList = new ArrayList<>();
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java
index 5f0f77d2..13319607 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java
@@ -21,11 +21,6 @@ import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
* specified {@link LatestPostsFragment.LatestPostsFragmentInteractionListener}.
*/
class LatestPostsAdapter extends RecyclerView.Adapter {
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "LatestPostsAdapter";
private final int VIEW_TYPE_ITEM = 0;
private final int VIEW_TYPE_LOADING = 1;
final private LatestPostsFragment.LatestPostsFragmentInteractionListener interactionListener;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java
index ccdb5593..5c3f8d40 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java
@@ -26,19 +26,15 @@ import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.PostSummary;
import gr.thmmy.mthmmy.utils.ParseHelpers;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
-import mthmmy.utils.Report;
+
import okhttp3.Request;
import okhttp3.Response;
+import timber.log.Timber;
/**
* Use the {@link LatestPostsFragment#newInstance} factory method to create an instance of this fragment.
*/
public class LatestPostsFragment extends BaseFragment implements LatestPostsAdapter.OnLoadMoreListener{
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "LatestPostsFragment";
/**
* The key to use when putting profile's url String to {@link LatestPostsFragment}'s Bundle.
*/
@@ -136,7 +132,7 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
profileLatestPostsTask.execute(profileUrl + ";sa=showPosts");
pagesLoaded = 1;
}
- Report.d(TAG, "onActivityCreated");
+ Timber.d("onActivityCreated");
}
@Override
@@ -156,14 +152,7 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
* LatestPostsTask's {@link AsyncTask#execute execute} method needs a profile's url as String
* parameter!
*/
- public class LatestPostsTask extends AsyncTask {
- //Class variables
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "LatestPostsTask"; //Separate tag for AsyncTask
-
+ private class LatestPostsTask extends AsyncTask {
protected void onPreExecute() {
if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE);
}
@@ -176,16 +165,16 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
Response response = BaseActivity.getClient().newCall(request).execute();
return parseLatestPosts(Jsoup.parse(response.body().string()));
} catch (SSLHandshakeException e) {
- Report.w(TAG, "Certificate problem (please switch to unsafe connection).");
+ Timber.w("Certificate problem (please switch to unsafe connection).");
} catch (Exception e) {
- Report.e("TAG", "ERROR", e);
+ Timber.e("ERROR", e);
}
return false;
}
protected void onPostExecute(Boolean result) {
if (!result) { //Parse failed!
- Report.d(TAG, "Parse failed!");
+ Timber.d("Parse failed!");
Toast.makeText(getContext()
, "Fatal error!\n Aborting...", Toast.LENGTH_LONG).show();
getActivity().finish();
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java
index d99f7d65..f573f3db 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java
@@ -33,6 +33,7 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import javax.net.ssl.SSLHandshakeException;
@@ -40,16 +41,12 @@ import javax.net.ssl.SSLHandshakeException;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
-import mthmmy.utils.Report;
+
import okhttp3.Request;
import okhttp3.Response;
+import timber.log.Timber;
public class StatsFragment extends Fragment {
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "StatsFragment";
/**
* The key to use when putting profile's url String to {@link StatsFragment}'s Bundle.
*/
@@ -108,7 +105,7 @@ public class StatsFragment extends Fragment {
profileStatsTask = new ProfileStatsTask();
profileStatsTask.execute(profileUrl + ";sa=statPanel");
}
- Report.d(TAG, "onActivityCreated");
+ Timber.d("onActivityCreated");
}
@Override
@@ -126,14 +123,7 @@ public class StatsFragment extends Fragment {
* Calling SummaryTask's {@link AsyncTask#execute execute} method needs to have profile's url
* as String parameter!
*/
- public class ProfileStatsTask extends AsyncTask {
- //Class variables
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "ProfileStatsTask"; //Separate tag for AsyncTask
-
+ private class ProfileStatsTask extends AsyncTask {
@Override
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
@@ -149,9 +139,9 @@ public class StatsFragment extends Fragment {
Response response = BaseActivity.getClient().newCall(request).execute();
return parseStats(Jsoup.parse(response.body().string()));
} catch (SSLHandshakeException e) {
- Report.w(TAG, "Certificate problem (please switch to unsafe connection).");
+ Timber.w("Certificate problem (please switch to unsafe connection).");
} catch (Exception e) {
- Report.e("TAG", "ERROR", e);
+ Timber.e("ERROR", e);
}
return false;
}
@@ -159,7 +149,7 @@ public class StatsFragment extends Fragment {
@Override
protected void onPostExecute(Boolean result) {
if (!result) { //Parse failed!
- Report.d(TAG, "Parse failed!");
+ Timber.d("Parse failed!");
Toast.makeText(getContext()
, "Fatal error!\n Aborting...", Toast.LENGTH_LONG).show();
getActivity().finish();
@@ -206,6 +196,7 @@ public class StatsFragment extends Fragment {
Integer.parseInt(dataCols.last().text())));
mostPopularBoardsByPostsLabels.add(dataCols.first().text());
}
+ Collections.reverse(mostPopularBoardsByPostsLabels);
}
{
Elements mostPopularBoardsByActivityRows = statsRows.last().select(">td").last()
@@ -218,6 +209,7 @@ public class StatsFragment extends Fragment {
Float.parseFloat(tmp.substring(0, tmp.indexOf("%")))));
mostPopularBoardsByActivityLabels.add(dataCols.first().text());
}
+ Collections.reverse(mostPopularBoardsByActivityLabels);
}
}
return true;
@@ -345,7 +337,7 @@ public class StatsFragment extends Fragment {
mostPopularBoardsByActivityChart.invalidate();
}
- class MyXAxisValueFormatter implements IAxisValueFormatter {
+ private class MyXAxisValueFormatter implements IAxisValueFormatter {
private final ArrayList mValues;
MyXAxisValueFormatter(ArrayList values) {
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java
index a2a0eea9..70644bf4 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java
@@ -6,6 +6,8 @@ import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Html;
+import android.text.method.LinkMovementMethod;
+
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -23,18 +25,14 @@ import java.util.Objects;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.utils.ParseHelpers;
-import mthmmy.utils.Report;
+
+import timber.log.Timber;
/**
* Use the {@link SummaryFragment#newInstance} factory method to create an instance of this fragment.
*/
public class SummaryFragment extends Fragment {
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "SummaryFragment";
/**
* The key to use when putting profile's source code String to {@link SummaryFragment}'s Bundle.
*/
@@ -94,7 +92,7 @@ public class SummaryFragment extends Fragment {
summaryTask = new SummaryTask();
summaryTask.execute(profileSummaryDocument);
}
- Report.d(TAG, "onActivityCreated");
+ Timber.d("onActivityCreated");
}
@Override
@@ -112,14 +110,7 @@ public class SummaryFragment extends Fragment {
* Calling SummaryTask's {@link AsyncTask#execute execute} method needs to have profile's url
* as String parameter!
*/
- public class SummaryTask extends AsyncTask {
- //Class variables
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "SummaryTask"; //Separate tag for AsyncTask
-
+ private class SummaryTask extends AsyncTask {
protected Void doInBackground(Document... profileSummaryPage) {
parsedProfileSummaryData = parseProfileSummary(profileSummaryPage[0]);
return null;
@@ -189,6 +180,16 @@ public class SummaryFragment extends Fragment {
}
TextView entry = new TextView(this.getContext());
+ if (profileSummaryRow.contains("@") &&
+ (profileSummaryRow.contains("Email") || profileSummaryRow.contains("E-mail"))) {
+ Timber.d("mpika");
+ Timber.d(profileSummaryRow);
+ String email = profileSummaryRow.substring(profileSummaryRow.indexOf(": ") + 6);
+ profileSummaryRow = profileSummaryRow.replace(email,
+ "" + email + "");
+ entry.setMovementMethod(LinkMovementMethod.getInstance());
+ }
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
entry.setTextColor(getResources().getColor(R.color.primary_text, null));
else
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java
new file mode 100644
index 00000000..7c42b8cd
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java
@@ -0,0 +1,263 @@
+package gr.thmmy.mthmmy.activities.topic;
+
+import android.util.Log;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+
+import okhttp3.Response;
+
+class Posting {
+ enum REPLY_STATUS {
+ SUCCESSFUL, NO_SUBJECT, EMPTY_BODY, NEW_REPLY_WHILE_POSTING, NOT_FOUND, SESSION_ENDED, OTHER_ERROR
+ }
+
+ static REPLY_STATUS replyStatus(Response response) throws IOException {
+ if (response.code() == 404) return REPLY_STATUS.NOT_FOUND;
+ if (response.code() < 200 || response.code() >= 400) return REPLY_STATUS.OTHER_ERROR;
+ String finalUrl = response.request().url().toString();
+ if (finalUrl.contains("action=post")) {
+ Document postErrorPage = Jsoup.parse(response.body().string());
+ String[] errors = postErrorPage.select("tr[id=errors] div[id=error_list]").first()
+ .toString().split("
");
+ for (int i = 0; i < errors.length; ++i) { //TODO test
+ Log.d("TAG", String.valueOf(i));
+ Log.d("TAG", errors[i]);
+ }
+ for (String error : errors) {
+ if (error.contains("Your session timed out while posting") ||
+ error.contains("Υπερβήκατε τον μέγιστο χρόνο σύνδεσης κατά την αποστολή"))
+ return REPLY_STATUS.SESSION_ENDED;
+ if (error.contains("No subject was filled in")
+ || error.contains("Δεν δόθηκε τίτλος"))
+ return REPLY_STATUS.NO_SUBJECT;
+ if (error.contains("The message body was left empty")
+ || error.contains("Δεν δόθηκε κείμενο για το μήνυμα"))
+ return REPLY_STATUS.EMPTY_BODY;
+ }
+ return REPLY_STATUS.NEW_REPLY_WHILE_POSTING;
+ }
+ return REPLY_STATUS.SUCCESSFUL;
+ }
+
+ static String htmlToBBcode(String html) {
+ Map bbMap = new HashMap<>();
+ Map smileysMap1 = new HashMap<>();
+ Map smileysMap2 = new HashMap<>();
+ smileysMap1.put("Smiley", ":)");
+ smileysMap1.put("Wink", ";)");
+ smileysMap1.put("Cheesy", ":D");
+ smileysMap1.put("Grin", ";D");
+ smileysMap1.put("Angry", ">:(");
+ smileysMap1.put("Sad", ":(");
+ smileysMap1.put("Shocked", ":o");
+ smileysMap1.put("Cool", "8))");
+ smileysMap1.put("Huh", ":???:");
+ smileysMap1.put("Roll Eyes", "::)");
+ smileysMap1.put("Tongue", ":P");
+ smileysMap1.put("Embarrassed", ":-[");
+ smileysMap1.put("Lips Sealed", ":-X");
+ smileysMap1.put("Kiss", ":-*");
+ smileysMap1.put("Cry", ":'(");
+ smileysMap1.put("heart", "<3");
+ smileysMap1.put("kleidaria", "^locked^");
+ smileysMap1.put("roll_over", "^rollover^");
+ smileysMap1.put("redface", "^redface^");
+ smileysMap1.put("confused", "^confused^");
+ smileysMap1.put("innocent", "^innocent^");
+ smileysMap1.put("sleep", "^sleep^");
+ smileysMap1.put("lips_sealed", "^sealed^");
+ smileysMap1.put("cool", "^cool^");
+ smileysMap1.put("crazy", "^crazy^");
+ smileysMap1.put("mad", "^mad^");
+ smileysMap1.put("wav", "^wav^");
+ smileysMap1.put("BinkyBaby", "^binkybaby^");
+ smileysMap1.put("DontKnow", "^dontknow^");
+ smileysMap1.put("angry4", ":angry4:");
+ smileysMap1.put("angryAndHot", "^angryhot^");
+ smileysMap1.put("angry", "^angry^");
+ smileysMap1.put("bang_head", "^banghead^");
+ smileysMap1.put("CryBaby", "^crybaby^");
+ smileysMap1.put("Hello", "^hello^");
+ smileysMap1.put("jerk", "^jerk^");
+ smileysMap1.put("NoNo", "^nono^");
+ smileysMap1.put("NotWorthy", "^notworthy^");
+ smileysMap1.put("Off-topic", "^off-topic^");
+ smileysMap1.put("Puke", "^puke^");
+ smileysMap1.put("Shout", "^shout^");
+ smileysMap1.put("Slurp", "^slurp^");
+ smileysMap1.put("SuperConfused", "^superconfused^");
+ smileysMap1.put("SuperInnocent", "^superinnocent^");
+ smileysMap1.put("CellPhone", "^cellPhone^");
+ smileysMap1.put("Idiot", "^idiot^");
+ smileysMap1.put("Knuppel", "^knuppel^");
+ smileysMap1.put("TickedOff", "^tickedOff^");
+ smileysMap1.put("Peace", "^peace^");
+ smileysMap1.put("Suspicious", "^suspicious^");
+ smileysMap1.put("Caffine", "^caffine^");
+ smileysMap1.put("argue", "^argue^");
+ smileysMap1.put("banned2", "^banned2^");
+ smileysMap1.put("banned", "^banned^");
+ smileysMap1.put("bath", "^bath^");
+ smileysMap1.put("beg", "^beg^");
+ smileysMap1.put("bluescreen", "^bluescreen^");
+ smileysMap1.put("boil", "^boil^");
+ smileysMap1.put("bye", "^bye^");
+ smileysMap1.put("callmerip", "^callmerip^");
+ smileysMap1.put("carnaval", "^carnaval^");
+ smileysMap1.put("clap", "^clap^");
+ smileysMap1.put("coffepot", "^coffepot^");
+ smileysMap1.put("crap", "^crap^");
+ smileysMap1.put("curses", "^curses^");
+ smileysMap1.put("funny", "^funny^");
+ smileysMap1.put("guitar", "^guitar^");
+ smileysMap1.put("kissy", "^kissy^");
+ smileysMap1.put("band", "^band^");
+ smileysMap1.put("ivres", "^ivres^");
+ smileysMap1.put("kaloe", "^kaloe^");
+ smileysMap1.put("kremala", "^kremala^");
+ smileysMap1.put("moon", "^moon^");
+ smileysMap1.put("mopping", "^mopping^");
+ smileysMap1.put("mountza", "^mountza^");
+ smileysMap1.put("pcsleep", "^pcsleep^");
+ smileysMap1.put("pinokio", "^pinokio^");
+ smileysMap1.put("poke", "^poke^");
+ smileysMap1.put("seestars", "^seestars^");
+ smileysMap1.put("sfyri", "^sfyri^");
+ smileysMap1.put("spam", "^spam^");
+ smileysMap1.put("super", "^super^");
+ smileysMap1.put("tafos", "^tafos^");
+ smileysMap1.put("tomato", "^tomato^");
+ smileysMap1.put("ytold", "^ytold^");
+ smileysMap1.put("beer", "^beer^");
+ smileysMap1.put("ο fritz!!!", "^fritz^");
+ smileysMap1.put("o Wade!!!", "^wade^");
+ smileysMap1.put("bonjour", "^hat^");
+ smileysMap1.put("bonjour2", "^miss^");
+ smileysMap1.put("question", "^que^");
+ smileysMap1.put("shifty", "^shifty^");
+ smileysMap1.put("shy", "^shy^");
+ smileysMap1.put("music_listenning", "^music_listen^");
+ smileysMap1.put("bag_face", "^bagface^");
+ smileysMap1.put("rotation", "^rotate^");
+ smileysMap1.put("love", "^love^");
+ smileysMap1.put("speech", "^speech^");
+ smileysMap1.put("shocked", "^shocked^");
+ smileysMap1.put("extremely_shocked", "^ex_shocked^");
+ smileysMap1.put("smurf", "^smurf^");
+ smileysMap1.put("monster", "^monster^");
+ smileysMap1.put("pig", "^pig^");
+ smileysMap1.put("lol", "^lol^");
+
+ smileysMap2.put("Police", "^Police^");
+ smileysMap2.put("foyska", "^fouska^");
+ smileysMap2.put("nista", "^nysta^");
+ smileysMap2.put("10_7_3", "^sfinaki^");
+ smileysMap2.put("yu", "^yue^");
+ smileysMap2.put("a-eatpaper", "^eatpaper^");
+ smileysMap2.put("lypi", "^lypi^");
+ smileysMap2.put("megashok1wq", "^aytoxeir^");
+ smileysMap2.put("victory", "^victory^");
+ smileysMap2.put("filarakia", "^filarakia^");
+ smileysMap2.put("rofl", "^rolfmao^");
+ smileysMap2.put("locked", "^lock^");
+ smileysMap2.put("facepalm", "^facepalm^");
+
+ //html stuff on the beginning
+ bbMap.put("\n ", "");
+ //quotes and code headers
+ bbMap.put("\n\\s+?", "");
+ bbMap.put("\n\\s+?", "");
+ bbMap.put("\n\\s+?\n (.+?)\n
", "");
+ bbMap.put("
", "\n");
+ //bold
+ bbMap.put("\n\\s+?(.+?)", "\\[b\\]$1\\[/b\\]");
+ //italics
+ bbMap.put("\n\\s+?(.+?)", "\\[i\\]$1\\[/i\\]");
+ //underline
+ bbMap.put("\n\\s+?(.+?)", "\\[u\\]$1\\[/u\\]");
+ //deleted
+ bbMap.put("\n\\s+?(.+?)", "\\[s\\]$1\\[/s\\]");
+ //text color
+ bbMap.put("\n\\s+?(.+?)", "\\[color=$1\\]$2\\[/color\\]");
+ //glow
+ bbMap.put("\n\\s+?(.+?)", "\\[glow=$1,2,300\\]$2\\[/glow\\]");
+ //shadow
+ bbMap.put("\n\\s+?(.+?)", "\\[shadow=$1,$2\\]$3\\[/shadow\\]");
+ //running text
+ bbMap.put("\\s+?", "\\[move\\]$1\\[/move\\]");
+ //alignment
+ bbMap.put("\n\\s+?\n (.+?)\n
", "\\[center\\]$1\\[/center\\]");
+ bbMap.put("\n\\s+?\n (.+?)\n
", "\\[$1\\]$2\\[/$1\\]");
+ //preformated
+ bbMap.put("\n\\s+?(.+?)
", "\\[pre\\]$1\\[/pre\\]");
+ //horizontal rule
+ bbMap.put("\n\\s+?
", "\\[hr\\]");
+ //resize
+ bbMap.put("\n\\s+?(.+?)", "\\[size=$1\\]$3\\[/size\\]");
+ //font
+ bbMap.put("\n\\s+?(.+?)", "\\[font=$1\\]$2\\[/font\\]");
+ //lists
+ bbMap.put("\\s+(.+?)", "\\[li\\]$1\\[/li\\]");
+ bbMap.put("\n\\s+",
+ "\\[list\\]\n$1\n\\[/list\\]");
+ //latex code
+ bbMap.put("\n\\s+?", "\\[tex\\]$1\\[/tex\\]");
+ //code
+ bbMap.put("\n\\s+?\n (.+?)\n
", "\\[code\\]$1\\[/code\\]");
+ //teletype
+ bbMap.put("\n\\s+?(.+?)", "\\[tt\\]$1\\[/tt\\]");
+ //superscript/subscript
+ bbMap.put("\n\\s+?(.+?)", "\\[sub\\]$1\\[/sub\\]");
+ bbMap.put("\n\\s+?(.+?)", "\\[sup\\]$1\\[/sup\\]");
+ //tables
+ bbMap.put("\\s+?([\\S\\s]+?)", "\\[td\\]$1\\[/td\\]");
+ bbMap.put("([\\S\\s]+?)\n
", "\\[tr\\]$1\\[/tr\\]");
+ bbMap.put("\n\\s+?"
+ , "\\[table\\]$2\\[/table\\]");
+ //videos
+ bbMap.put("\n\\s+?\n",
+ "[youtube]https://www.youtube.com/watch?v=$1[/youtube]");
+ //ftp
+ bbMap.put("([\\S\\s]+?)", "\\[fpt=ftp:$1\\]$2\\[/ftp\\]");
+ //mailto
+ bbMap.put("\n\\s+?
([\\S\\s]+?)", "\\[email\\]$2\\[/email\\]");
+ //links
+ bbMap.put("\n\\s+?
([\\S\\s]+?)", "\\[url=$1\\]$2\\[/url\\]");
+ //smileys
+ for (Map.Entry entry : smileysMap1.entrySet()) {
+ bbMap.put("\n
", entry.getValue().toString());
+ }
+ for (Map.Entry entry : smileysMap2.entrySet()) { //Those that have empty alt tag
+ bbMap.put("\n
", entry.getValue().toString());
+ }
+
+ bbMap.put("\n
"
+ , Matcher.quoteReplacement(":-\\"));
+
+ //html stuff on the end
+ bbMap.put("\n
", "");
+
+ for (Map.Entry entry : bbMap.entrySet()) {
+ html = html.replaceAll(entry.getKey().toString(), entry.getValue().toString());
+ }
+
+ //img need to be done last or it messes up everything else
+ html = html.replaceAll("\\s+",
+ "\\[img width=$2 height=$3\\]$1\\[/img\\]");
+ html = html.replaceAll("\\s+",
+ "\\[img height=$2 width=$3\\]$1\\[/img\\]");
+ html = html.replaceAll("\\s+", "\\[img width=$2\\]$1\\[/img\\]");
+ html = html.replaceAll("\\s+", "\\[img height=$2\\]$1\\[/img\\]");
+ html = html.replaceAll("\\s+", "\\[img\\]$1\\[/img\\]");
+
+ return html;
+ }
+}
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 8ca6f707..a733caf5 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
@@ -1,40 +1,68 @@
package gr.thmmy.mthmmy.activities.topic;
+import android.content.Context;
+import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.design.widget.FloatingActionButton;
+import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
-import android.util.Log;
+import android.text.Html;
+import android.text.SpannableStringBuilder;
+import android.text.method.LinkMovementMethod;
+import android.text.method.ScrollingMovementMethod;
+import android.text.style.ClickableSpan;
+import android.text.style.URLSpan;
import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
+import android.view.inputmethod.InputMethodManager;
import android.widget.ImageButton;
+import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Selector;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;
import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.activities.board.BoardActivity;
+import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.model.Bookmark;
import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.ThmmyPage;
import gr.thmmy.mthmmy.utils.ParseHelpers;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
-import mthmmy.utils.Report;
+
+import okhttp3.MultipartBody;
import okhttp3.Request;
+import okhttp3.RequestBody;
import okhttp3.Response;
+import timber.log.Timber;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_TITLE;
+import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_URL;
+import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_THUMBNAIL_URL;
+import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_URL;
+import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME;
+import static gr.thmmy.mthmmy.activities.topic.Posting.replyStatus;
/**
* Activity for topics. When creating an Intent of this activity you need to bundle a String
@@ -44,11 +72,6 @@ import okhttp3.Response;
@SuppressWarnings("unchecked")
public class TopicActivity extends BaseActivity {
//Class variables
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "TopicActivity";
/**
* The key to use when putting topic's url String to {@link TopicActivity}'s Bundle.
*/
@@ -62,10 +85,11 @@ public class TopicActivity extends BaseActivity {
private TopicAdapter topicAdapter;
private ArrayList postsList;
private static final int NO_POST_FOCUS = -1;
- private static int postFocus = NO_POST_FOCUS;
+ private int postFocus = NO_POST_FOCUS;
private static int postFocusPosition = 0;
- //Quotes
- public static final ArrayList toQuoteList = new ArrayList<>();
+ //Reply
+ private FloatingActionButton replyFAB;
+ private String replyPageUrl = null;
//Topic's pages
private int thisPage = 1;
private int numberOfPages = 1;
@@ -79,19 +103,24 @@ public class TopicActivity extends BaseActivity {
private static final int LARGE_STEP = 10;
private Integer pageRequestValue;
//Bottom navigation graphics
+ private LinearLayout bottomNavBar;
private ImageButton firstPage;
private ImageButton previousPage;
private TextView pageIndicator;
private ImageButton nextPage;
private ImageButton lastPage;
+ //Topic's info
+ private SpannableStringBuilder topicTreeAndMods = new SpannableStringBuilder("Loading..."),
+ topicViewers = new SpannableStringBuilder("Loading...");
//Other variables
- private FloatingActionButton replyFAB;
private MaterialProgressBar progressBar;
+ TextView toolbarTitle;
private static String base_url = "";
private String topicTitle;
private String parsedTitle;
private RecyclerView recyclerView;
- private String loadedPageUrl = "";
+ String loadedPageUrl = "";
+ private boolean reloadingPage = false;
@Override
@@ -105,23 +134,27 @@ public class TopicActivity extends BaseActivity {
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(
Uri.parse(topicPageUrl));
if (!target.is(ThmmyPage.PageCategory.TOPIC)) {
- Report.e(TAG, "Bundle came with a non topic url!\nUrl:\n" + topicPageUrl);
+ Timber.e("Bundle came with a non topic url!\nUrl:\n" + topicPageUrl);
Toast.makeText(this, "An error has occurred\n Aborting.", Toast.LENGTH_SHORT).show();
finish();
}
+ thisPageBookmark = new Bookmark(topicTitle, ThmmyPage.getTopicId(topicPageUrl));
+
//Initializes graphics
toolbar = (Toolbar) findViewById(R.id.toolbar);
- toolbar.setTitle(topicTitle);
+ toolbarTitle = (TextView) toolbar.findViewById(R.id.toolbar_title);
+ toolbarTitle.setText(topicTitle);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
- thisPageBookmark = new Bookmark(topicTitle, ThmmyPage.getTopicId(topicPageUrl));
- thisPageBookmarkButton = (ImageButton) findViewById(R.id.bookmark);
- setTopicBookmark();
+ //Makes title scrollable
+ toolbarTitle.setHorizontallyScrolling(true);
+ toolbarTitle.setMovementMethod(new ScrollingMovementMethod());
+
createDrawer();
progressBar = (MaterialProgressBar) findViewById(R.id.progressBar);
@@ -132,42 +165,27 @@ public class TopicActivity extends BaseActivity {
recyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(layoutManager);
- topicAdapter = new TopicAdapter(this, postsList,
- topicTask);
+ topicAdapter = new TopicAdapter(this, postsList, topicTask, topicTitle, loadedPageUrl);
recyclerView.setAdapter(topicAdapter);
replyFAB = (FloatingActionButton) findViewById(R.id.topic_fab);
replyFAB.setEnabled(false);
- replyFAB.hide();
- /*if (!sessionManager.isLoggedIn()) replyFAB.hide();
+ bottomNavBar = (LinearLayout) findViewById(R.id.bottom_navigation_bar);
+ if (!sessionManager.isLoggedIn()) replyFAB.hide();
else {
replyFAB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (sessionManager.isLoggedIn()) {
- //TODO Reply
- } else {
- new AlertDialog.Builder(TopicActivity.this)
- .setMessage("You need to be logged in to reply!")
- .setPositiveButton("Login", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- Intent intent = new Intent(TopicActivity.this, LoginActivity.class);
- startActivity(intent);
- finish();
- overridePendingTransition(R.anim.push_right_in, R.anim.push_right_out);
- }
- })
- .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- }
- })
- .show();
+ postsList.add(null);
+ topicAdapter.prepareForReply(new ReplyTask());
+ replyFAB.hide();
+ bottomNavBar.setVisibility(View.GONE);
+ topicAdapter.notifyItemInserted(postsList.size());
}
}
});
- }*/
+ }
//Sets bottom navigation bar
firstPage = (ImageButton) findViewById(R.id.page_first_button);
@@ -187,6 +205,43 @@ public class TopicActivity extends BaseActivity {
topicTask.execute(extras.getString(BUNDLE_TOPIC_URL)); //Attempt data parsing
}
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflates the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.topic_menu, menu);
+ setTopicBookmark(menu.getItem(0));
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle presses on the action bar items
+ switch (item.getItemId()) {
+ case R.id.menu_bookmark:
+ topicMenuBookmarkClick();
+ return true;
+ case R.id.menu_info:
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ LayoutInflater inflater = this.getLayoutInflater();
+ LinearLayout infoDialog = (LinearLayout) inflater.inflate(R.layout.dialog_topic_info
+ , null);
+ ((TextView) infoDialog.findViewById(R.id.dialog_title)).setText("Info");
+ TextView treeAndMods = (TextView) infoDialog.findViewById(R.id.topic_tree_and_mods);
+ treeAndMods.setText(topicTreeAndMods);
+ treeAndMods.setMovementMethod(LinkMovementMethod.getInstance());
+ TextView usersViewing = (TextView) infoDialog.findViewById(R.id.users_viewing);
+ usersViewing.setText(topicViewers);
+ usersViewing.setMovementMethod(LinkMovementMethod.getInstance());
+
+ builder.setView(infoDialog);
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
@Override
public void onBackPressed() {
if (drawer.isDrawerOpen()) {
@@ -216,7 +271,7 @@ public class TopicActivity extends BaseActivity {
* This class is used to implement the repetitive incrementPageRequestValue/decrementPageRequestValue
* of page value when long pressing one of the page navigation buttons.
*/
- class RepetitiveUpdater implements Runnable {
+ private class RepetitiveUpdater implements Runnable {
private final int step;
/**
@@ -352,7 +407,8 @@ public class TopicActivity extends BaseActivity {
paginationEnabled(true);
changePage(pageRequestValue - 1);
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
- if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) {
+ if (rect != null &&
+ !rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) {
autoIncrement = false;
incrementPageRequestValue(thisPage - pageRequestValue);
paginationEnabled(true);
@@ -400,11 +456,6 @@ public class TopicActivity extends BaseActivity {
* as String parameter!
*/
class TopicTask extends AsyncTask {
- //Class variables
- /**
- * Debug Tag for logging debug output to LogCat
- */
- private static final String TAG = "TopicTask"; //Separate tag for AsyncTask
private static final int SUCCESS = 0;
private static final int NETWORK_ERROR = 1;
private static final int OTHER_ERROR = 2;
@@ -419,7 +470,6 @@ public class TopicActivity extends BaseActivity {
protected Integer doInBackground(String... strings) {
Document document;
- base_url = strings[0].substring(0, strings[0].lastIndexOf(".")); //New topic's base url
String newPageUrl = strings[0];
//Finds the index of message focus if present
@@ -434,7 +484,7 @@ public class TopicActivity extends BaseActivity {
}
}
//Checks if the page to be loaded is the one already shown
- if (!Objects.equals(loadedPageUrl, "") && loadedPageUrl.contains(base_url)) {
+ if (!reloadingPage && !Objects.equals(loadedPageUrl, "") && newPageUrl.contains(base_url)) {
if (newPageUrl.contains("topicseen#new") || newPageUrl.contains("#new"))
if (thisPage == numberOfPages)
return SAME_PAGE;
@@ -448,11 +498,16 @@ public class TopicActivity extends BaseActivity {
return SAME_PAGE;
}
}
- } else if (Integer.parseInt(newPageUrl.substring(base_url.length() + 1)) / 15 + 1 == thisPage)
+ } else if ((Objects.equals(newPageUrl, base_url) && thisPage == 1) ||
+ Integer.parseInt(newPageUrl.substring(base_url.length() + 1)) / 15 + 1 == thisPage)
return SAME_PAGE;
} else if (!Objects.equals(loadedPageUrl, "")) topicTitle = null;
+ if (reloadingPage) reloadingPage = !reloadingPage;
loadedPageUrl = newPageUrl;
+ if (strings[0].substring(0, strings[0].lastIndexOf(".")).contains("topic="))
+ base_url = strings[0].substring(0, strings[0].lastIndexOf(".")); //New topic's base url
+ replyPageUrl = null;
Request request = new Request.Builder()
.url(newPageUrl)
.build();
@@ -462,10 +517,10 @@ public class TopicActivity extends BaseActivity {
parse(document);
return SUCCESS;
} catch (IOException e) {
- Report.i(TAG, "IO Exception", e);
+ Timber.i("IO Exception", e);
return NETWORK_ERROR;
} catch (Exception e) {
- Report.e(TAG, "Exception", e);
+ Timber.e("Exception", e);
return OTHER_ERROR;
}
}
@@ -483,11 +538,13 @@ public class TopicActivity extends BaseActivity {
case SUCCESS:
if (topicTitle == null || Objects.equals(topicTitle, "")) {
thisPageBookmark = new Bookmark(parsedTitle, ThmmyPage.getTopicId(loadedPageUrl));
- setTopicBookmark();
+ invalidateOptionsMenu();
}
progressBar.setVisibility(ProgressBar.INVISIBLE);
topicAdapter.customNotifyDataSetChanged(new TopicTask());
+ topicAdapter.setTopicInfo(parsedTitle, loadedPageUrl);
+ if (replyPageUrl == null) replyFAB.hide();
if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true);
//Set current page
@@ -496,8 +553,10 @@ public class TopicActivity extends BaseActivity {
paginationEnabled(true);
+ if (topicTitle != null)
+ if (parsedTitle != null)
if (topicTitle == null || Objects.equals(topicTitle, ""))
- toolbar.setTitle(parsedTitle);
+ toolbarTitle.setText(parsedTitle);
break;
case NETWORK_ERROR:
Toast.makeText(getBaseContext(), "Network Error", Toast.LENGTH_SHORT).show();
@@ -512,7 +571,7 @@ public class TopicActivity extends BaseActivity {
break;
default:
//Parse failed - should never happen
- Report.d(TAG, "Parse failed!");
+ Timber.d("Parse failed!"); //TODO report ParseException?
Toast.makeText(getBaseContext(), "Fatal Error", Toast.LENGTH_SHORT).show();
finish();
break;
@@ -528,6 +587,20 @@ public class TopicActivity extends BaseActivity {
private void parse(Document topic) {
ParseHelpers.Language language = ParseHelpers.Language.getLanguage(topic);
+ //Finds topic's tree, mods and users viewing
+ {
+ topicTreeAndMods = getSpannableFromHtml(topic.select("div.nav").first().html());
+ topicViewers = getSpannableFromHtml(TopicParser.parseUsersViewingThisTopic(topic, language));
+ }
+
+ //Finds reply page url
+ {
+ Element replyButton = topic.select("a:has(img[alt=Reply])").first();
+ if (replyButton == null)
+ replyButton = topic.select("a:has(img[alt=Απάντηση])").first();
+ if (replyButton != null) replyPageUrl = replyButton.attr("href");
+ }
+
//Finds topic title if missing
if (topicTitle == null || Objects.equals(topicTitle, "")) {
parsedTitle = topic.select("td[id=top_subject]").first().text();
@@ -537,7 +610,7 @@ public class TopicActivity extends BaseActivity {
} else {
parsedTitle = parsedTitle.substring(parsedTitle.indexOf("Θέμα:") + 6
, parsedTitle.indexOf("(Αναγνώστηκε") - 2);
- Report.d(TAG, parsedTitle);
+ Timber.d(parsedTitle);
}
}
@@ -555,7 +628,150 @@ public class TopicActivity extends BaseActivity {
postsList.clear();
postsList.addAll(TopicParser.parseTopic(topic, language));
- //postsList = TopicParser.parseTopic(topic, language);
+ }
+
+ private void makeLinkClickable(SpannableStringBuilder strBuilder, final URLSpan span) {
+ int start = strBuilder.getSpanStart(span);
+ int end = strBuilder.getSpanEnd(span);
+ int flags = strBuilder.getSpanFlags(span);
+ ClickableSpan clickable = new ClickableSpan() {
+ @Override
+ public void onClick(View view) {
+ ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(span.getURL()));
+ if (target.is(ThmmyPage.PageCategory.BOARD)) {
+ Intent intent = new Intent(getApplicationContext(), BoardActivity.class);
+ Bundle extras = new Bundle();
+ extras.putString(BUNDLE_BOARD_URL, span.getURL());
+ extras.putString(BUNDLE_BOARD_TITLE, "");
+ intent.putExtras(extras);
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+ getApplicationContext().startActivity(intent);
+ } else if (target.is(ThmmyPage.PageCategory.PROFILE)) {
+ Intent intent = new Intent(getApplicationContext(), ProfileActivity.class);
+ Bundle extras = new Bundle();
+ extras.putString(BUNDLE_PROFILE_URL, span.getURL());
+ extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, "");
+ extras.putString(BUNDLE_PROFILE_USERNAME, "");
+ intent.putExtras(extras);
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+ getApplicationContext().startActivity(intent);
+ } else if (target.is(ThmmyPage.PageCategory.INDEX))
+ finish();
+ }
+ };
+ strBuilder.setSpan(clickable, start, end, flags);
+ strBuilder.removeSpan(span);
+ }
+
+ private SpannableStringBuilder getSpannableFromHtml(String html) {
+ CharSequence sequence = Html.fromHtml(html);
+ SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
+ URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
+ for (URLSpan span : urls) {
+ makeLinkClickable(strBuilder, span);
+ }
+ return strBuilder;
+ }
+ }
+
+ class ReplyTask extends AsyncTask {
+
+ @Override
+ protected void onPreExecute() {
+ progressBar.setVisibility(ProgressBar.VISIBLE);
+ paginationEnabled(false);
+ replyFAB.setEnabled(false);
+ }
+
+ @Override
+ protected Boolean doInBackground(String... message) {
+ Document document;
+ String numReplies, seqnum, sc, subject, topic;
+
+ Request request = new Request.Builder()
+ .url(replyPageUrl + ";wap2")
+ .build();
+ try {
+ Response response = client.newCall(request).execute();
+ document = Jsoup.parse(response.body().string());
+
+ numReplies = replyPageUrl.substring(replyPageUrl.indexOf("num_replies=") + 12);
+ seqnum = document.select("input[name=seqnum]").first().attr("value");
+ sc = document.select("input[name=sc]").first().attr("value");
+ //subject = document.select("input[name=subject]").first().attr("value");
+ topic = document.select("input[name=topic]").first().attr("value");
+ } catch (IOException e) {
+ Timber.e("Post failed.", e);
+ return false;
+ } catch (Selector.SelectorParseException e) {
+ Timber.e("Post failed.", e);
+ return false;
+ }
+
+ RequestBody postBody = new MultipartBody.Builder()
+ .setType(MultipartBody.FORM)
+ .addFormDataPart("message", message[1])
+ .addFormDataPart("num_replies", numReplies)
+ .addFormDataPart("seqnum", seqnum)
+ .addFormDataPart("sc", sc)
+ .addFormDataPart("subject", message[0])
+ .addFormDataPart("topic", topic)
+ .build();
+
+ Request post = new Request.Builder()
+ .url("https://www.thmmy.gr/smf/index.php?action=post2")
+ .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36")
+ .post(postBody)
+ .build();
+
+ try {
+ client.newCall(post).execute();
+ Response response = client.newCall(post).execute();
+ switch (replyStatus(response)) {
+ case SUCCESSFUL:
+ return true;
+ case NEW_REPLY_WHILE_POSTING:
+ //TODO this...
+ return true;
+ default:
+ Timber.e("Malformed post. Request string:\n" + post.toString());
+ return true;
+ }
+ } catch (IOException e) {
+ Timber.e("Post failed.", e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ View view = getCurrentFocus();
+ if (view != null) {
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
+
+ postsList.remove(postsList.size() - 1);
+ topicAdapter.notifyItemRemoved(postsList.size());
+
+ progressBar.setVisibility(ProgressBar.GONE);
+ replyFAB.setVisibility(View.VISIBLE);
+ bottomNavBar.setVisibility(View.VISIBLE);
+
+ if (!result)
+ Toast.makeText(TopicActivity.this, "Post failed!", Toast.LENGTH_SHORT).show();
+ paginationEnabled(true);
+ replyFAB.setEnabled(true);
+
+ if (result) {
+ topicTask = new TopicTask();
+ if ((postsList.get(postsList.size() - 1).getPostNumber() + 1) % 15 == 0)
+ topicTask.execute(base_url + "." + 2147483647);
+ else {
+ reloadingPage = true;
+ topicTask.execute(loadedPageUrl);
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
index 2ea65fb9..b06b684a 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
@@ -10,10 +10,15 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.v4.content.res.ResourcesCompat;
+import android.support.v7.widget.AppCompatImageButton;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
+import android.text.Editable;
import android.text.TextUtils;
+import android.text.TextWatcher;
+
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -21,6 +26,7 @@ import android.view.ViewGroup;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
+import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
@@ -30,8 +36,14 @@ import android.widget.TextView;
import com.squareup.picasso.Picasso;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
import gr.thmmy.mthmmy.R;
@@ -42,7 +54,8 @@ import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.ThmmyFile;
import gr.thmmy.mthmmy.model.ThmmyPage;
import gr.thmmy.mthmmy.utils.CircleTransform;
-import mthmmy.utils.Report;
+
+import timber.log.Timber;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_TITLE;
@@ -50,21 +63,20 @@ import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_THUMBNAIL_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME;
-import static gr.thmmy.mthmmy.activities.topic.TopicActivity.toQuoteList;
+import static gr.thmmy.mthmmy.activities.topic.Posting.htmlToBBcode;
+import static gr.thmmy.mthmmy.base.BaseActivity.getSessionManager;
/**
* Custom {@link android.support.v7.widget.RecyclerView.Adapter} used for topics.
*/
-class TopicAdapter extends RecyclerView.Adapter {
- /**
- * Debug Tag for logging debug output to LogCat
- */
- private static final String TAG = "TopicAdapter";
+class TopicAdapter extends RecyclerView.Adapter {
/**
* Int that holds thumbnail's size defined in R.dimen
*/
private static int THUMBNAIL_SIZE;
private final Context context;
+ private String topicTitle;
+ private ArrayList toQuoteList = new ArrayList<>();
private final List postsList;
/**
* Used to hold the state of visibility and other attributes for views that are animated or
@@ -87,69 +99,20 @@ class TopicAdapter extends RecyclerView.Adapter {
*/
private static final int isQuoteButtonChecked = 2;
private TopicActivity.TopicTask topicTask;
+ private TopicActivity.ReplyTask replyTask;
+ private final int VIEW_TYPE_POST = 0;
+ private final int VIEW_TYPE_QUICK_REPLY = 1;
- /**
- * Custom {@link RecyclerView.ViewHolder} implementation
- */
- class MyViewHolder extends RecyclerView.ViewHolder {
- final CardView cardView;
- final LinearLayout cardChildLinear;
- final FrameLayout postDateAndNumberExp;
- final TextView postDate, postNum, username, subject;
- final ImageView thumbnail;
- final public WebView post;
- final ImageButton quoteToggle;
- final RelativeLayout header;
- final LinearLayout userExtraInfo;
- final View bodyFooterDivider;
- final LinearLayout postFooter;
-
- final TextView specialRank, rank, gender, numberOfPosts, personalText, stars;
-
- MyViewHolder(View view) {
- super(view);
- //Initializes layout's graphic elements
- //Standard stuff
- cardView = (CardView) view.findViewById(R.id.card_view);
- cardChildLinear = (LinearLayout) view.findViewById(R.id.card_child_linear);
- postDateAndNumberExp = (FrameLayout) view.findViewById(R.id.post_date_and_number_exp);
- postDate = (TextView) view.findViewById(R.id.post_date);
- postNum = (TextView) view.findViewById(R.id.post_number);
- thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
- username = (TextView) view.findViewById(R.id.username);
- subject = (TextView) view.findViewById(R.id.subject);
- post = (WebView) view.findViewById(R.id.post);
- post.setBackgroundColor(Color.argb(1, 255, 255, 255));
- quoteToggle = (ImageButton) view.findViewById(R.id.toggle_quote_button);
- bodyFooterDivider = view.findViewById(R.id.body_footer_divider);
- postFooter = (LinearLayout) view.findViewById(R.id.post_footer);
-
- //User's extra info
- header = (RelativeLayout) view.findViewById(R.id.header);
- userExtraInfo = (LinearLayout) view.findViewById(R.id.user_extra_info);
- specialRank = (TextView) view.findViewById(R.id.special_rank);
- rank = (TextView) view.findViewById(R.id.rank);
- gender = (TextView) view.findViewById(R.id.gender);
- numberOfPosts = (TextView) view.findViewById(R.id.number_of_posts);
- personalText = (TextView) view.findViewById(R.id.personal_text);
- stars = (TextView) view.findViewById(R.id.stars);
- }
-
- /**
- * Cancels all pending Picasso requests
- */
- void cleanup() {
- Picasso.with(context).cancelRequest(thumbnail);
- thumbnail.setImageDrawable(null);
- }
- }
+ private String[] replyDataHolder = new String[2];
+ private int replySubject = 0, replyText = 1;
+ private String loadedPageUrl = "";
/**
* @param context the context of the {@link RecyclerView}
* @param postsList List of {@link Post} objects to use
*/
- TopicAdapter(Context context, List postsList,
- TopicActivity.TopicTask topicTask) {
+ TopicAdapter(Context context, List postsList, TopicActivity.TopicTask topicTask
+ , String topicTitle, String loadedPageUrl) {
this.context = context;
this.postsList = postsList;
@@ -159,177 +122,225 @@ class TopicAdapter extends RecyclerView.Adapter {
viewProperties.add(new boolean[3]);
}
this.topicTask = topicTask;
+ this.topicTitle = topicTitle;
+ this.loadedPageUrl = loadedPageUrl;
}
- @Override
- public void onViewRecycled(final MyViewHolder holder) {
- holder.cleanup();
+ void prepareForReply(TopicActivity.ReplyTask replyTask) {
+ this.replyTask = replyTask;
+ }
+
+ void setTopicInfo(String topicTitle, String loadedPageUrl) {
+ this.topicTitle = topicTitle;
+ this.loadedPageUrl = loadedPageUrl;
}
@Override
- public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View itemView = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.activity_topic_post_row, parent, false);
- return new MyViewHolder(itemView);
+ public int getItemViewType(int position) {
+ return postsList.get(position) == null ? VIEW_TYPE_QUICK_REPLY : VIEW_TYPE_POST;
}
- @SuppressLint("SetJavaScriptEnabled")
@Override
- public void onBindViewHolder(final MyViewHolder holder, final int position) {
- final Post currentPost = postsList.get(position);
-
- //Post's WebView parameters
- holder.post.setClickable(true);
- holder.post.setWebViewClient(new LinkLauncher());
-
- //Avoids errors about layout having 0 width/height
- holder.thumbnail.setMinimumWidth(1);
- holder.thumbnail.setMinimumHeight(1);
- //Sets thumbnail size
- holder.thumbnail.setMaxWidth(THUMBNAIL_SIZE);
- holder.thumbnail.setMaxHeight(THUMBNAIL_SIZE);
-
- //noinspection ConstantConditions
- Picasso.with(context)
- .load(currentPost.getThumbnailUrl())
- .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
- .centerCrop()
- .error(ResourcesCompat.getDrawable(context.getResources()
- , R.drawable.ic_default_user_thumbnail, null))
- .placeholder(ResourcesCompat.getDrawable(context.getResources()
- , R.drawable.ic_default_user_thumbnail, null))
- .transform(new CircleTransform())
- .into(holder.thumbnail);
-
- //Sets username,submit date, index number, subject, post's and attached files texts
- holder.username.setText(currentPost.getAuthor());
- holder.postDate.setText(currentPost.getPostDate());
- if (currentPost.getPostNumber() != 0)
- holder.postNum.setText(context.getString(
- R.string.user_number_of_posts, currentPost.getPostNumber()));
- else
- holder.postNum.setText("");
- holder.subject.setText(currentPost.getSubject());
- holder.post.loadDataWithBaseURL("file:///android_asset/", currentPost.getContent(), "text/html", "UTF-8", null);
- if (currentPost.getAttachedFiles() != null && currentPost.getAttachedFiles().size() != 0) {
- holder.bodyFooterDivider.setVisibility(View.VISIBLE);
- int filesTextColor;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- filesTextColor = context.getResources().getColor(R.color.accent, null);
- } else //noinspection deprecation
- filesTextColor = context.getResources().getColor(R.color.accent);
-
- holder.postFooter.removeAllViews();
- for (final ThmmyFile attachedFile : currentPost.getAttachedFiles()) {
- final TextView attached = new TextView(context);
- attached.setTextSize(10f);
- attached.setClickable(true);
- attached.setTypeface(Typeface.createFromAsset(context.getAssets()
- , "fonts/fontawesome-webfont.ttf"));
- attached.setText(faIconFromFilename(attachedFile.getFilename()) + " "
- + attachedFile.getFilename() + attachedFile.getFileInfo());
- attached.setTextColor(filesTextColor);
- attached.setPadding(0, 3, 0, 3);
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ if (viewType == VIEW_TYPE_POST) {
+ View itemView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.activity_topic_post_row, parent, false);
+ return new PostViewHolder(itemView);
+ } else if (viewType == VIEW_TYPE_QUICK_REPLY) {
+ View view = LayoutInflater.from(parent.getContext()).
+ inflate(R.layout.activity_topic_quick_reply_row, parent, false);
+ view.findViewById(R.id.quick_reply_submit).setEnabled(true);
+ //Default post subject
+ replyDataHolder[replySubject] = "Re: " + topicTitle;
+ //Build quotes
+ String quotes = "";
+ for (int quotePosition : toQuoteList) {
+ quotes += buildQuote(quotePosition);
+ }
+ if (!Objects.equals(quotes, ""))
+ replyDataHolder[replyText] = htmlToBBcode(quotes);
+ return new QuickReplyViewHolder(view, new CustomEditTextListener(replySubject),
+ new CustomEditTextListener(replyText));
+ }
+ return null;
+ }
- attached.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- ((BaseActivity) context).launchDownloadService(attachedFile);
+ @SuppressLint({"SetJavaScriptEnabled", "SetTextI18n"})
+ @Override
+ public void onBindViewHolder(final RecyclerView.ViewHolder currentHolder, final int position) {
+ if (currentHolder instanceof PostViewHolder) {
+ final Post currentPost = postsList.get(position);
+ final PostViewHolder holder = (PostViewHolder) currentHolder;
+
+ //Post's WebView parameters
+ holder.post.setClickable(true);
+ holder.post.setWebViewClient(new LinkLauncher());
+
+ //Avoids errors about layout having 0 width/height
+ holder.thumbnail.setMinimumWidth(1);
+ holder.thumbnail.setMinimumHeight(1);
+ //Sets thumbnail size
+ holder.thumbnail.setMaxWidth(THUMBNAIL_SIZE);
+ holder.thumbnail.setMaxHeight(THUMBNAIL_SIZE);
+
+ //noinspection ConstantConditions
+ Picasso.with(context)
+ .load(currentPost.getThumbnailUrl())
+ .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
+ .centerCrop()
+ .error(ResourcesCompat.getDrawable(context.getResources()
+ , R.drawable.ic_default_user_thumbnail, null))
+ .placeholder(ResourcesCompat.getDrawable(context.getResources()
+ , R.drawable.ic_default_user_thumbnail, null))
+ .transform(new CircleTransform())
+ .into(holder.thumbnail);
+
+ //Sets username,submit date, index number, subject, post's and attached files texts
+ holder.username.setText(currentPost.getAuthor());
+ holder.postDate.setText(currentPost.getPostDate());
+ if (currentPost.getPostNumber() != 0)
+ holder.postNum.setText(context.getString(
+ R.string.user_number_of_posts, currentPost.getPostNumber()));
+ else
+ holder.postNum.setText("");
+ holder.subject.setText(currentPost.getSubject());
+ holder.post.loadDataWithBaseURL("file:///android_asset/", currentPost.getContent(), "text/html", "UTF-8", null);
+ if ((currentPost.getAttachedFiles() != null && currentPost.getAttachedFiles().size() != 0)
+ || (currentPost.getLastEdit() != null)) {
+ holder.bodyFooterDivider.setVisibility(View.VISIBLE);
+ holder.postFooter.removeAllViews();
+
+ if (currentPost.getAttachedFiles() != null && currentPost.getAttachedFiles().size() != 0) {
+ int filesTextColor;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ filesTextColor = context.getResources().getColor(R.color.accent, null);
+ } else //noinspection deprecation
+ filesTextColor = context.getResources().getColor(R.color.accent);
+
+ for (final ThmmyFile attachedFile : currentPost.getAttachedFiles()) {
+ final TextView attached = new TextView(context);
+ attached.setTextSize(10f);
+ attached.setClickable(true);
+ attached.setTypeface(Typeface.createFromAsset(context.getAssets()
+ , "fonts/fontawesome-webfont.ttf"));
+ attached.setText(faIconFromFilename(attachedFile.getFilename()) + " "
+ + attachedFile.getFilename() + attachedFile.getFileInfo());
+ attached.setTextColor(filesTextColor);
+ attached.setPadding(0, 3, 0, 3);
+
+ attached.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ ((BaseActivity) context).launchDownloadService(attachedFile);
+ }
+ });
+
+ holder.postFooter.addView(attached);
}
- });
+ }
+ if (currentPost.getLastEdit() != null && currentPost.getLastEdit().length() > 0) {
+ int lastEditTextColor;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ lastEditTextColor = context.getResources().getColor(R.color.white, null);
+ } else //noinspection deprecation
+ lastEditTextColor = context.getResources().getColor(R.color.white);
+
+ final TextView lastEdit = new TextView(context);
+ lastEdit.setTextSize(12f);
+ lastEdit.setText(currentPost.getLastEdit());
+ lastEdit.setTextColor(lastEditTextColor);
+ lastEdit.setPadding(0, 3, 0, 3);
+ holder.postFooter.addView(lastEdit);
+ }
+ } else {
+ holder.bodyFooterDivider.setVisibility(View.GONE);
+ holder.postFooter.removeAllViews();
+ }
- holder.postFooter.addView(attached);
+ String mSpecialRank, mRank, mGender, mNumberOfPosts, mPersonalText;
+ int mNumberOfStars, mUserColor;
+
+ if (!currentPost.isDeleted()) { //Sets user's extra info
+ mSpecialRank = currentPost.getSpecialRank();
+ mRank = currentPost.getRank();
+ mGender = currentPost.getGender();
+ mNumberOfPosts = currentPost.getNumberOfPosts();
+ mPersonalText = currentPost.getPersonalText();
+ mNumberOfStars = currentPost.getNumberOfStars();
+ mUserColor = currentPost.getUserColor();
+ } else {
+ mSpecialRank = null;
+ mRank = null;
+ mGender = null;
+ mNumberOfPosts = null;
+ mPersonalText = null;
+ mNumberOfStars = 0;
+ mUserColor = 0;
}
- } else {
- holder.bodyFooterDivider.setVisibility(View.GONE);
- holder.postFooter.removeAllViews();
- }
- String mSpecialRank, mRank, mGender, mNumberOfPosts, mPersonalText;
- int mNumberOfStars, mUserColor;
-
- if (!currentPost.isDeleted()) { //Sets user's extra info
- mSpecialRank = currentPost.getSpecialRank();
- mRank = currentPost.getRank();
- mGender = currentPost.getGender();
- mNumberOfPosts = currentPost.getNumberOfPosts();
- mPersonalText = currentPost.getPersonalText();
- mNumberOfStars = currentPost.getNumberOfStars();
- mUserColor = currentPost.getUserColor();
- } else {
- mSpecialRank = null;
- mRank = null;
- mGender = null;
- mNumberOfPosts = null;
- mPersonalText = null;
- mNumberOfStars = 0;
- mUserColor = 0;
- }
+ if (!Objects.equals(mSpecialRank, "") && mSpecialRank != null) {
+ holder.specialRank.setText(mSpecialRank);
+ holder.specialRank.setVisibility(View.VISIBLE);
+ } else
+ holder.specialRank.setVisibility(View.GONE);
+ if (!Objects.equals(mRank, "") && mRank != null) {
+ holder.rank.setText(mRank);
+ holder.rank.setVisibility(View.VISIBLE);
+ } else
+ holder.rank.setVisibility(View.GONE);
+ if (!Objects.equals(mGender, "") && mGender != null) {
+ holder.gender.setText(mGender);
+ holder.gender.setVisibility(View.VISIBLE);
+ } else
+ holder.gender.setVisibility(View.GONE);
+ if (!Objects.equals(mNumberOfPosts, "") && mNumberOfPosts != null) {
+ holder.numberOfPosts.setText(mNumberOfPosts);
+ holder.numberOfPosts.setVisibility(View.VISIBLE);
+ } else
+ holder.numberOfPosts.setVisibility(View.GONE);
+ if (!Objects.equals(mPersonalText, "") && mPersonalText != null) {
+ holder.personalText.setText("\"" + mPersonalText + "\"");
+ holder.personalText.setVisibility(View.VISIBLE);
+ } else
+ holder.personalText.setVisibility(View.GONE);
+ if (mNumberOfStars > 0) {
+ holder.stars.setTypeface(Typeface.createFromAsset(context.getAssets()
+ , "fonts/fontawesome-webfont.ttf"));
- if (!Objects.equals(mSpecialRank, "") && mSpecialRank != null) {
- holder.specialRank.setText(mSpecialRank);
- holder.specialRank.setVisibility(View.VISIBLE);
- } else
- holder.specialRank.setVisibility(View.GONE);
- if (!Objects.equals(mRank, "") && mRank != null) {
- holder.rank.setText(mRank);
- holder.rank.setVisibility(View.VISIBLE);
- } else
- holder.rank.setVisibility(View.GONE);
- if (!Objects.equals(mGender, "") && mGender != null) {
- holder.gender.setText(mGender);
- holder.gender.setVisibility(View.VISIBLE);
- } else
- holder.gender.setVisibility(View.GONE);
- if (!Objects.equals(mNumberOfPosts, "") && mNumberOfPosts != null) {
- holder.numberOfPosts.setText(mNumberOfPosts);
- holder.numberOfPosts.setVisibility(View.VISIBLE);
- } else
- holder.numberOfPosts.setVisibility(View.GONE);
- if (!Objects.equals(mPersonalText, "") && mPersonalText != null) {
- holder.personalText.setText("\"" + mPersonalText + "\"");
- holder.personalText.setVisibility(View.VISIBLE);
- } else
- holder.personalText.setVisibility(View.GONE);
- if (mNumberOfStars > 0) {
- holder.stars.setTypeface(Typeface.createFromAsset(context.getAssets()
- , "fonts/fontawesome-webfont.ttf"));
-
- String aStar = context.getResources().getString(R.string.fa_icon_star);
- String usersStars = "";
- for (int i = 0; i < mNumberOfStars; ++i) {
- usersStars += aStar;
+ String aStar = context.getResources().getString(R.string.fa_icon_star);
+ String usersStars = "";
+ for (int i = 0; i < mNumberOfStars; ++i) {
+ usersStars += aStar;
+ }
+ holder.stars.setText(usersStars);
+ holder.stars.setTextColor(mUserColor);
+ holder.stars.setVisibility(View.VISIBLE);
+ } else
+ holder.stars.setVisibility(View.GONE);
+ //Special card for special member of the month!
+ if (mUserColor == TopicParser.USER_COLOR_PINK) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ holder.cardChildLinear.setBackground(context.getResources().
+ getDrawable(R.drawable.member_of_the_month_card, null));
+ } else //noinspection deprecation
+ holder.cardChildLinear.setBackground(context.getResources().
+ getDrawable(R.drawable.member_of_the_month_card));
+ } else holder.cardChildLinear.setBackground(null);
+
+ //Avoid's view's visibility recycling
+ if (!currentPost.isDeleted() && viewProperties.get(position)[isUserExtraInfoVisibile]) {
+ holder.userExtraInfo.setVisibility(View.VISIBLE);
+ holder.userExtraInfo.setAlpha(1.0f);
+ } else {
+ holder.userExtraInfo.setVisibility(View.GONE);
+ holder.userExtraInfo.setAlpha(0.0f);
}
- holder.stars.setText(usersStars);
- holder.stars.setTextColor(mUserColor);
- holder.stars.setVisibility(View.VISIBLE);
- } else
- holder.stars.setVisibility(View.GONE);
- //Special card for special member of the month!
- if (mUserColor == TopicParser.USER_COLOR_PINK) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- holder.cardChildLinear.setBackground(context.getResources().
- getDrawable(R.drawable.member_of_the_month_card, null));
- } else //noinspection deprecation
- holder.cardChildLinear.setBackground(context.getResources().
- getDrawable(R.drawable.member_of_the_month_card));
- } else holder.cardChildLinear.setBackground(null);
-
- //Avoid's view's visibility recycling
- if (!currentPost.isDeleted() && viewProperties.get(position)[isUserExtraInfoVisibile]) {
- holder.userExtraInfo.setVisibility(View.VISIBLE);
- holder.userExtraInfo.setAlpha(1.0f);
- } else {
- holder.userExtraInfo.setVisibility(View.GONE);
- holder.userExtraInfo.setAlpha(0.0f);
- }
- if (!currentPost.isDeleted()) {
- //Sets graphics behavior
- holder.header.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- //Clicking an expanded header starts profile activity
- if (viewProperties.get(holder.getAdapterPosition())[isUserExtraInfoVisibile]) {
+ if (!currentPost.isDeleted()) {
+ //Sets graphics behavior
+ holder.thumbnail.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ //Clicking the thumbnail opens user's profile
Intent intent = new Intent(context, ProfileActivity.class);
Bundle extras = new Bundle();
extras.putString(BUNDLE_PROFILE_URL, currentPost.getProfileURL());
@@ -342,99 +353,138 @@ class TopicAdapter extends RecyclerView.Adapter {
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
+ });
+ holder.header.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ //Clicking the header makes it expand/collapse
+ boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
+ tmp[isUserExtraInfoVisibile] = !tmp[isUserExtraInfoVisibile];
+ viewProperties.set(holder.getAdapterPosition(), tmp);
+ TopicAnimations.animateUserExtraInfoVisibility(holder.userExtraInfo);
+ }
+ });
+ //Clicking the expanded part of a header (the extra info) makes it collapse
+ holder.userExtraInfo.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
+ tmp[isUserExtraInfoVisibile] = false;
+ viewProperties.set(holder.getAdapterPosition(), tmp);
+
+ TopicAnimations.animateUserExtraInfoVisibility(v);
+ }
+ });
+ } else {
+ holder.header.setOnClickListener(null);
+ holder.userExtraInfo.setOnClickListener(null);
+ }
- boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
- tmp[isUserExtraInfoVisibile] = !tmp[isUserExtraInfoVisibile];
- viewProperties.set(holder.getAdapterPosition(), tmp);
- TopicAnimations.animateUserExtraInfoVisibility(holder.userExtraInfo);
- }
- });
- //Clicking the expanded part of a header (the extra info) makes it collapse
- holder.userExtraInfo.setOnClickListener(new View.OnClickListener() {
+ //Avoid's view's visibility recycling
+ if (viewProperties.get(position)[isPostDateAndNumberVisibile]) { //Expanded
+ holder.postDateAndNumberExp.setVisibility(View.VISIBLE);
+ holder.postDateAndNumberExp.setAlpha(1.0f);
+ holder.postDateAndNumberExp.setTranslationY(0);
+
+ holder.username.setMaxLines(Integer.MAX_VALUE);
+ holder.username.setEllipsize(null);
+
+ holder.subject.setTextColor(Color.parseColor("#FFFFFF"));
+ holder.subject.setMaxLines(Integer.MAX_VALUE);
+ holder.subject.setEllipsize(null);
+ } else { //Collapsed
+ holder.postDateAndNumberExp.setVisibility(View.GONE);
+ holder.postDateAndNumberExp.setAlpha(0.0f);
+ holder.postDateAndNumberExp.setTranslationY(holder.postDateAndNumberExp.getHeight());
+
+ holder.username.setMaxLines(1);
+ holder.username.setEllipsize(TextUtils.TruncateAt.END);
+
+ holder.subject.setTextColor(Color.parseColor("#757575"));
+ holder.subject.setMaxLines(1);
+ holder.subject.setEllipsize(TextUtils.TruncateAt.END);
+ }
+ //noinspection PointlessBooleanExpression,ConstantConditions
+ if (!BaseActivity.getSessionManager().isLoggedIn())
+ holder.quoteToggle.setVisibility(View.GONE);
+ else {
+ if (viewProperties.get(position)[isQuoteButtonChecked])
+ holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked);
+ else
+ holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked);
+ //Sets graphics behavior
+ holder.quoteToggle.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
+ if (tmp[isQuoteButtonChecked]) {
+ if (toQuoteList.contains(postsList.indexOf(currentPost))) {
+ toQuoteList.remove(toQuoteList.indexOf(postsList.indexOf(currentPost)));
+ } else
+ Timber.i("An error occurred while trying to exclude post from" +
+ "toQuoteList, post wasn't there!");
+ holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked);
+ } else {
+ toQuoteList.add(postsList.indexOf(currentPost));
+ holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked);
+ }
+ tmp[isQuoteButtonChecked] = !tmp[isQuoteButtonChecked];
+ viewProperties.set(holder.getAdapterPosition(), tmp);
+ }
+ });
+ }
+ //Card expand/collapse when card is touched
+ holder.cardView.setOnClickListener(new View.OnClickListener() {
@Override
- public void onClick(View v) {
+ public void onClick(View view) {
+ //Change post's viewProperties accordingly
boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
- tmp[1] = false;
+ tmp[isPostDateAndNumberVisibile] = !tmp[isPostDateAndNumberVisibile];
viewProperties.set(holder.getAdapterPosition(), tmp);
- TopicAnimations.animateUserExtraInfoVisibility(v);
+ TopicAnimations.animatePostExtraInfoVisibility(holder.postDateAndNumberExp
+ , holder.username, holder.subject
+ , Color.parseColor("#FFFFFF")
+ , Color.parseColor("#757575"));
}
});
- } else {
- holder.header.setOnClickListener(null);
- holder.userExtraInfo.setOnClickListener(null);
- }
-
- //Avoid's view's visibility recycling
- if (viewProperties.get(position)[isPostDateAndNumberVisibile]) { //Expanded
- holder.postDateAndNumberExp.setVisibility(View.VISIBLE);
- holder.postDateAndNumberExp.setAlpha(1.0f);
- holder.postDateAndNumberExp.setTranslationY(0);
-
- holder.username.setMaxLines(Integer.MAX_VALUE);
- holder.username.setEllipsize(null);
-
- holder.subject.setTextColor(Color.parseColor("#FFFFFF"));
- holder.subject.setMaxLines(Integer.MAX_VALUE);
- holder.subject.setEllipsize(null);
- } else { //Collapsed
- holder.postDateAndNumberExp.setVisibility(View.GONE);
- holder.postDateAndNumberExp.setAlpha(0.0f);
- holder.postDateAndNumberExp.setTranslationY(holder.postDateAndNumberExp.getHeight());
-
- holder.username.setMaxLines(1);
- holder.username.setEllipsize(TextUtils.TruncateAt.END);
-
- holder.subject.setTextColor(Color.parseColor("#757575"));
- holder.subject.setMaxLines(1);
- holder.subject.setEllipsize(TextUtils.TruncateAt.END);
- }
- //noinspection PointlessBooleanExpression,ConstantConditions
- if (!BaseActivity.getSessionManager().isLoggedIn() || true) //Hide it until reply is implemented
- holder.quoteToggle.setVisibility(View.GONE);
- else {
- if (viewProperties.get(position)[isQuoteButtonChecked])
- holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked);
- else
- holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked);
- //Sets graphics behavior
- holder.quoteToggle.setOnClickListener(new View.OnClickListener() {
+ //Also when post is clicked
+ holder.post.setOnTouchListener(new CustomTouchListener(holder.post, holder.cardView));
+ } else if (currentHolder instanceof QuickReplyViewHolder) {
+ final QuickReplyViewHolder holder = (QuickReplyViewHolder) currentHolder;
+
+ //noinspection ConstantConditions
+ Picasso.with(context)
+ .load(getSessionManager().getAvatarLink())
+ .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
+ .centerCrop()
+ .error(ResourcesCompat.getDrawable(context.getResources()
+ , R.drawable.ic_default_user_thumbnail, null))
+ .placeholder(ResourcesCompat.getDrawable(context.getResources()
+ , R.drawable.ic_default_user_thumbnail, null))
+ .transform(new CircleTransform())
+ .into(holder.thumbnail);
+ holder.username.setText(getSessionManager().getUsername());
+ holder.quickReplySubject.setText(replyDataHolder[replySubject]);
+
+ if (replyDataHolder[replyText] != null && !Objects.equals(replyDataHolder[replyText], ""))
+ holder.quickReply.setText(replyDataHolder[replyText]);
+
+ holder.submitButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
- if (tmp[isQuoteButtonChecked]) {
- if (toQuoteList.contains(currentPost.getPostNumber())) {
- toQuoteList.remove(toQuoteList.indexOf(currentPost.getPostNumber()));
- } else
- Report.i(TAG, "An error occurred while trying to exclude post from" +
- "toQuoteList, post wasn't there!");
- holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked);
- } else {
- toQuoteList.add(currentPost.getPostNumber());
- holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked);
- }
- tmp[isQuoteButtonChecked] = !tmp[isQuoteButtonChecked];
- viewProperties.set(holder.getAdapterPosition(), tmp);
+ if (holder.quickReplySubject.getText().toString().isEmpty()) return;
+ if (holder.quickReply.getText().toString().isEmpty()) return;
+ holder.submitButton.setEnabled(false);
+ replyTask.execute(holder.quickReplySubject.getText().toString(),
+ holder.quickReply.getText().toString());
+
+ holder.quickReplySubject.getText().clear();
+ holder.quickReplySubject.setText("Re: " + topicTitle);
+ holder.quickReply.getText().clear();
}
});
}
- //Card expand/collapse when card is touched
- holder.cardView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- //Change post's viewProperties accordingly
- boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
- tmp[isPostDateAndNumberVisibile] = !tmp[isPostDateAndNumberVisibile];
- viewProperties.set(holder.getAdapterPosition(), tmp);
-
- TopicAnimations.animatePostExtraInfoVisibility(holder.postDateAndNumberExp
- , holder.username, holder.subject
- , Color.parseColor("#FFFFFF")
- , Color.parseColor("#757575"));
- }
- });
- //Also when post is clicked
- holder.post.setOnTouchListener(new CustomTouchListener(holder.post, holder.cardView));
}
void customNotifyDataSetChanged(TopicActivity.TopicTask topicTask) {
@@ -452,6 +502,79 @@ class TopicAdapter extends RecyclerView.Adapter {
return postsList.size();
}
+ /**
+ * Custom {@link RecyclerView.ViewHolder} implementation
+ */
+ private class PostViewHolder extends RecyclerView.ViewHolder {
+ final CardView cardView;
+ final LinearLayout cardChildLinear;
+ final FrameLayout postDateAndNumberExp;
+ final TextView postDate, postNum, username, subject;
+ final ImageView thumbnail;
+ final public WebView post;
+ final ImageButton quoteToggle;
+ final RelativeLayout header;
+ final LinearLayout userExtraInfo;
+ final View bodyFooterDivider;
+ final LinearLayout postFooter;
+
+ final TextView specialRank, rank, gender, numberOfPosts, personalText, stars;
+
+ PostViewHolder(View view) {
+ super(view);
+ //Initializes layout's graphic elements
+ //Standard stuff
+ cardView = (CardView) view.findViewById(R.id.card_view);
+ cardChildLinear = (LinearLayout) view.findViewById(R.id.card_child_linear);
+ postDateAndNumberExp = (FrameLayout) view.findViewById(R.id.post_date_and_number_exp);
+ postDate = (TextView) view.findViewById(R.id.post_date);
+ postNum = (TextView) view.findViewById(R.id.post_number);
+ thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
+ username = (TextView) view.findViewById(R.id.username);
+ subject = (TextView) view.findViewById(R.id.subject);
+ post = (WebView) view.findViewById(R.id.post);
+ post.setBackgroundColor(Color.argb(1, 255, 255, 255));
+ quoteToggle = (ImageButton) view.findViewById(R.id.toggle_quote_button);
+ bodyFooterDivider = view.findViewById(R.id.body_footer_divider);
+ postFooter = (LinearLayout) view.findViewById(R.id.post_footer);
+
+ //User's extra info
+ header = (RelativeLayout) view.findViewById(R.id.header);
+ userExtraInfo = (LinearLayout) view.findViewById(R.id.user_extra_info);
+ specialRank = (TextView) view.findViewById(R.id.special_rank);
+ rank = (TextView) view.findViewById(R.id.rank);
+ gender = (TextView) view.findViewById(R.id.gender);
+ numberOfPosts = (TextView) view.findViewById(R.id.number_of_posts);
+ personalText = (TextView) view.findViewById(R.id.personal_text);
+ stars = (TextView) view.findViewById(R.id.stars);
+ }
+ }
+
+ /**
+ * Custom {@link RecyclerView.ViewHolder} implementation
+ */
+ private static class QuickReplyViewHolder extends RecyclerView.ViewHolder {
+ final ImageView thumbnail;
+ final TextView username;
+ final EditText quickReply, quickReplySubject;
+ final AppCompatImageButton submitButton;
+ final CustomEditTextListener replySubject, replyText;
+
+ QuickReplyViewHolder(View quickReply, CustomEditTextListener replySubject
+ , CustomEditTextListener replyText) {
+ super(quickReply);
+ thumbnail = (ImageView) quickReply.findViewById(R.id.thumbnail);
+ username = (TextView) quickReply.findViewById(R.id.username);
+ this.quickReply = (EditText) quickReply.findViewById(R.id.quick_reply_text);
+ this.replyText = replyText;
+ this.quickReply.addTextChangedListener(replyText);
+ quickReplySubject = (EditText) quickReply.findViewById(R.id.quick_reply_subject);
+ this.replySubject = replySubject;
+ quickReplySubject.addTextChangedListener(replySubject);
+ submitButton = (AppCompatImageButton) quickReply.findViewById(R.id.quick_reply_submit);
+ }
+ }
+
/**
* This class is a gesture detector for WebViews. It handles post's clicks, long clicks and
* touch and drag.
@@ -592,6 +715,84 @@ class TopicAdapter extends RecyclerView.Adapter {
//Method always returns true as no url should be loaded in the WebViews
return true;
}
+
+ }
+
+ private class CustomEditTextListener implements TextWatcher {
+ private final int positionInDataHolder;
+
+ CustomEditTextListener(int positionInDataHolder) {
+ this.positionInDataHolder = positionInDataHolder;
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ replyDataHolder[positionInDataHolder] = charSequence.toString();
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ }
+ }
+
+ @Nullable
+ private String buildQuote(int quotePosition) {
+ Date postDate = null;
+ {
+ String date = postsList.get(quotePosition).getPostDate();
+ if (date != null) {
+ DateFormat format = new SimpleDateFormat("MMMM d, yyyy, h:m:s a", Locale.ENGLISH);
+ date = date.replace("Ιανουαρίου", "January");
+ date = date.replace("Φεβρουαρίου", "February");
+ date = date.replace("Μαρτίου", "March");
+ date = date.replace("Απριλίου", "April");
+ date = date.replace("Μαΐου", "May");
+ date = date.replace("Ιουνίου", "June");
+ date = date.replace("Ιουλίου", "July");
+ date = date.replace("Αυγούστου", "August");
+ date = date.replace("Σεπτεμβρίου", "September");
+ date = date.replace("Οκτωβρίου", "October");
+ date = date.replace("Νοεμβρίου", "November");
+ date = date.replace("Δεκεμβρίου", "December");
+
+ if (date.contains("Today")) {
+ date = date.replace("Today at",
+ Calendar.getInstance().getDisplayName(Calendar.MONTH,
+ Calendar.LONG, Locale.ENGLISH)
+ + " " + Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
+ + ", " + Calendar.getInstance().get(Calendar.YEAR) + ",");
+ } else if (date.contains("Σήμερα")) {
+ date = date.replace("Σήμερα στις",
+ Calendar.getInstance().getDisplayName(Calendar.MONTH,
+ Calendar.LONG, Locale.ENGLISH)
+ + " " + Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
+ + ", " + Calendar.getInstance().get(Calendar.YEAR) + ",");
+ if (date.contains("πμ")) date = date.replace("πμ", "am");
+ if (date.contains("μμ")) date = date.replace("μμ", "pm");
+ }
+ try {
+ postDate = format.parse(date);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ if (postsList.get(quotePosition).getPostIndex() != 0) {
+ if (postDate != null) {
+ return "[quote author=" + postsList.get(quotePosition).getAuthor()
+ + " link=topic=" + ThmmyPage.getTopicId(loadedPageUrl) + ".msg"
+ + postsList.get(quotePosition).getPostIndex()
+ + "#msg" + postsList.get(quotePosition).getPostIndex()
+ + " date=" + postDate.getTime() / 1000 + "]"
+ + "\n" + postsList.get(quotePosition).getContent()
+ + "\n" + "[/quote]" + "\n\n";
+ }
+ }
+ return null;
}
/**
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 9cab0649..3ce9c24d 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
@@ -1,6 +1,7 @@
package gr.thmmy.mthmmy.activities.topic;
import android.graphics.Color;
+import android.util.Log;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
@@ -17,7 +18,8 @@ import java.util.Objects;
import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.ThmmyFile;
import gr.thmmy.mthmmy.utils.ParseHelpers;
-import mthmmy.utils.Report;
+import timber.log.Timber;
+
/**
* Singleton used for parsing a topic.
@@ -36,12 +38,6 @@ class TopicParser {
static final int USER_COLOR_PINK = Color.parseColor("#FF4081");
private static final int USER_COLOR_YELLOW = Color.parseColor("#FFEB3B");
- /**
- * Debug Tag for logging debug output to LogCat
- */
- @SuppressWarnings("unused")
- private static final String TAG = "TopicParser";
-
/**
* Returns users currently viewing this topic.
*
@@ -160,7 +156,7 @@ class TopicParser {
for (Element thisRow : postRows) {
//Variables for Post constructor
String p_userName, p_thumbnailUrl, p_subject, p_post, p_postDate, p_profileURL, p_rank,
- p_specialRank, p_gender, p_personalText, p_numberOfPosts;
+ p_specialRank, p_gender, p_personalText, p_numberOfPosts, p_postLastEditDate;
int p_postNum, p_postIndex, p_numberOfStars, p_userColor;
boolean p_isDeleted = false;
ArrayList p_attachedFiles;
@@ -175,6 +171,7 @@ class TopicParser {
p_numberOfStars = 0;
p_userColor = USER_COLOR_YELLOW;
p_attachedFiles = new ArrayList<>();
+ p_postLastEditDate = null;
//Language independent parsing
//Finds thumbnail url
@@ -190,21 +187,31 @@ class TopicParser {
//Finds post's text
p_post = ParseHelpers.youtubeEmbeddedFix(thisRow.select("div").select(".post").first());
- //Add stuff to make it work in WebView
+ //Adds stuff to make it work in WebView
//style.css
p_post = ("" + p_post);
- //Find post's index
+ //Finds post's index
//This is an int assigned by the forum used for post focusing and quotes, it is not
//the same as reply index.
Element postIndex = thisRow.select("a[name^=msg]").first();
- if (postIndex == null)
- p_postIndex = NO_INDEX;
- else {
+ if (postIndex != null) {
String tmp = postIndex.attr("name");
p_postIndex = Integer.parseInt(tmp.substring(tmp.indexOf("msg") + 3));
+ } else{
+ postIndex = thisRow.select("div[id^=subject]").first();
+ if (postIndex == null)
+ p_postIndex = NO_INDEX;
+ else{
+ String tmp = postIndex.attr("id");
+ p_postIndex = Integer.parseInt(tmp.substring(tmp.indexOf("subject") + 8));
+ }
}
+ Element postLastEditDate = thisRow.select("td.smalltext[id^=modified_]").first();
+ if (postLastEditDate != null && !Objects.equals(postLastEditDate.text(), ""))
+ p_postLastEditDate = postLastEditDate.text();
+
//Language dependent parsing
Element userName;
if (language.is(ParseHelpers.Language.GREEK)) {
@@ -252,7 +259,7 @@ class TopicParser {
try {
attachedUrl = new URL(tmpAttachedFileUrlAndName.attr("href"));
} catch (MalformedURLException e) {
- Report.e(TAG, "Attached file malformed url", e);
+ Timber.e("Attached file malformed url", e);
break;
}
String attachedFileName = tmpAttachedFileUrlAndName.text().substring(1);
@@ -312,7 +319,7 @@ class TopicParser {
try {
attachedUrl = new URL(tmpAttachedFileUrlAndName.attr("href"));
} catch (MalformedURLException e) {
- Report.e(TAG, "Attached file malformed url", e);
+ Timber.e("Attached file malformed url", e);
break;
}
String attachedFileName = tmpAttachedFileUrlAndName.text().substring(1);
@@ -406,12 +413,12 @@ class TopicParser {
parsedPostsList.add(new Post(p_thumbnailUrl, p_userName, p_subject, p_post, p_postIndex
, p_postNum, p_postDate, p_profileURL, p_rank, p_specialRank, p_gender
, p_numberOfPosts, p_personalText, p_numberOfStars, p_userColor
- , p_attachedFiles));
+ , p_attachedFiles, p_postLastEditDate));
} else { //Deleted user
//Add new post in postsList, only standard information needed
parsedPostsList.add(new Post(p_thumbnailUrl, p_userName, p_subject, p_post, p_postIndex
- , p_postNum, p_postDate, p_userColor, p_attachedFiles));
+ , p_postNum, p_postDate, p_userColor, p_attachedFiles, p_postLastEditDate));
}
}
return parsedPostsList;
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 501e2452..3be339d3 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
@@ -14,6 +14,7 @@ import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;
@@ -63,7 +64,8 @@ public abstract class BaseActivity extends AppCompatActivity {
private static final String BOOKMARKED_TOPICS_KEY = "bookmarkedTopicsKey";
private static final String BOOKMARKED_BOARDS_KEY = "bookmarkedBoardsKey";
protected Bookmark thisPageBookmark;
- protected ImageButton thisPageBookmarkButton;
+ private MenuItem thisPageBookmarkMenuButton;
+ private ImageButton thisPageBookmarkImageButton;
private SharedPreferences bookmarksFile;
private ArrayList topicsBookmarked;
private ArrayList boardsBookmarked;
@@ -79,6 +81,7 @@ public abstract class BaseActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
if (client == null)
client = BaseApplication.getInstance().getClient(); //must check every time - e.g.
+
// they become null when app restarts after crash
if (sessionManager == null)
sessionManager = BaseApplication.getInstance().getSessionManager();
@@ -344,7 +347,7 @@ public abstract class BaseActivity extends AppCompatActivity {
});
}
- protected void updateDrawer() {
+ private void updateDrawer() {
if (drawer != null) {
if (!sessionManager.isLoggedIn()) //When logged out or if user is guest
{
@@ -373,7 +376,7 @@ public abstract class BaseActivity extends AppCompatActivity {
* Result toast will always display a success, because when user chooses logout all data are
* cleared regardless of the actual outcome
*/
- protected class LogoutTask extends AsyncTask { //Attempt logout
+ private class LogoutTask extends AsyncTask { //Attempt logout
ProgressDialog progressDialog;
protected Integer doInBackground(Void... voids) {
@@ -407,42 +410,42 @@ public abstract class BaseActivity extends AppCompatActivity {
return topicsBookmarked;
}
- protected void setTopicBookmark() {
+ protected void setTopicBookmark(MenuItem thisPageBookmarkMenuButton) {
+ this.thisPageBookmarkMenuButton = thisPageBookmarkMenuButton;
if (thisPageBookmark.matchExists(topicsBookmarked)) {
- thisPageBookmarkButton.setImageDrawable(bookmarked);
+ thisPageBookmarkMenuButton.setIcon(bookmarked);
} else {
- thisPageBookmarkButton.setImageDrawable(notBookmarked);
+ thisPageBookmarkMenuButton.setIcon(notBookmarked);
+ }
+ }
+
+ protected void topicMenuBookmarkClick() {
+ if (thisPageBookmark.matchExists(topicsBookmarked)) {
+ thisPageBookmarkMenuButton.setIcon(notBookmarked);
+ toggleTopicToBookmarks(thisPageBookmark);
+ Toast.makeText(BaseActivity.this, "Bookmark removed", Toast.LENGTH_SHORT).show();
+ } else {
+ thisPageBookmarkMenuButton.setIcon(bookmarked);
+ toggleTopicToBookmarks(thisPageBookmark);
+ Toast.makeText(BaseActivity.this, "Bookmark added", Toast.LENGTH_SHORT).show();
}
- thisPageBookmarkButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (thisPageBookmark.matchExists(topicsBookmarked)) {
- thisPageBookmarkButton.setImageDrawable(notBookmarked);
- toggleTopicToBookmarks(thisPageBookmark);
- Toast.makeText(BaseActivity.this, "Bookmark removed", Toast.LENGTH_SHORT).show();
- } else {
- thisPageBookmarkButton.setImageDrawable(bookmarked);
- toggleTopicToBookmarks(thisPageBookmark);
- Toast.makeText(BaseActivity.this, "Bookmark added", Toast.LENGTH_SHORT).show();
- }
- }
- });
}
- protected void setBoardBookmark() {
+ protected void setBoardBookmark(final ImageButton thisPageBookmarkImageButton) {
+ this.thisPageBookmarkImageButton = thisPageBookmarkImageButton;
if (thisPageBookmark.matchExists(boardsBookmarked)) {
- thisPageBookmarkButton.setImageDrawable(bookmarked);
+ thisPageBookmarkImageButton.setImageDrawable(bookmarked);
} else {
- thisPageBookmarkButton.setImageDrawable(notBookmarked);
+ thisPageBookmarkImageButton.setImageDrawable(notBookmarked);
}
- thisPageBookmarkButton.setOnClickListener(new View.OnClickListener() {
+ thisPageBookmarkImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (thisPageBookmark.matchExists(boardsBookmarked)) {
- thisPageBookmarkButton.setImageDrawable(notBookmarked);
+ thisPageBookmarkImageButton.setImageDrawable(notBookmarked);
Toast.makeText(BaseActivity.this, "Bookmark removed", Toast.LENGTH_SHORT).show();
} else {
- thisPageBookmarkButton.setImageDrawable(bookmarked);
+ thisPageBookmarkImageButton.setImageDrawable(bookmarked);
Toast.makeText(BaseActivity.this, "Bookmark added", Toast.LENGTH_SHORT).show();
}
toggleBoardToBookmarks(thisPageBookmark);
@@ -521,7 +524,7 @@ public abstract class BaseActivity extends AppCompatActivity {
}
//Display popup gor user to grant permission
- public void requestPerms() { //Runtime permissions request for devices with API >= 23
+ private void requestPerms() { //Runtime permissions request for devices with API >= 23
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
@@ -556,7 +559,7 @@ public abstract class BaseActivity extends AppCompatActivity {
}
//Uses temp file - called after permission grant
- public void launchDownloadService() {
+ private void launchDownloadService() {
if (checkPerms())
DownloadService.startActionDownload(this, tempThmmyFile.getFileUrl().toString());
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 1e5ffdd6..3030cce0 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java
@@ -19,11 +19,20 @@ import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader;
import com.mikepenz.materialdrawer.util.DrawerImageLoader;
import com.squareup.picasso.Picasso;
+import java.io.IOException;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
+import gr.thmmy.mthmmy.BuildConfig;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.session.SessionManager;
+import gr.thmmy.mthmmy.utils.CrashReportingTree;
+import okhttp3.HttpUrl;
+import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import timber.log.Timber;
public class BaseApplication extends Application {
@@ -40,7 +49,7 @@ public class BaseApplication extends Application {
//Display Metrics
private static float dpHeight, dpWidth;
- public static BaseApplication getInstance(){
+ public static BaseApplication getInstance() {
return baseApplication;
}
@@ -49,11 +58,33 @@ public class BaseApplication extends Application {
super.onCreate();
baseApplication = this; //init singleton
+ if (BuildConfig.DEBUG) {
+ Timber.plant(new Timber.DebugTree());
+ } else {
+ Timber.plant(new CrashReportingTree());
+ }
+
SharedPreferences sharedPrefs = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);
SharedPrefsCookiePersistor sharedPrefsCookiePersistor = new SharedPrefsCookiePersistor(getApplicationContext());
PersistentCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), sharedPrefsCookiePersistor);
client = new OkHttpClient.Builder()
.cookieJar(cookieJar)
+ .addInterceptor(new Interceptor() {
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ Request request = chain.request();
+ HttpUrl oldUrl = chain.request().url();
+ if (Objects.equals(chain.request().url().host(), "www.thmmy.gr")) {
+ if (!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();
+ }
+ }
+ return chain.proceed(request);
+
+ }
+ })
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
@@ -71,6 +102,7 @@ public class BaseApplication extends Application {
public void set(ImageView imageView, Uri uri, Drawable placeholder) {
Picasso.with(imageView.getContext()).load(uri).placeholder(placeholder).into(imageView);
}
+
@Override
public void cancel(ImageView imageView) {
Picasso.with(imageView.getContext()).cancelRequest(imageView);
diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java
index c7b3d477..20bd8da2 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java
@@ -5,8 +5,8 @@ import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
-import mthmmy.utils.Report;
import okhttp3.OkHttpClient;
+import timber.log.Timber;
public abstract class BaseFragment extends Fragment {
protected static final String ARG_SECTION_NUMBER = "SectionNumber";
@@ -15,7 +15,7 @@ public abstract class BaseFragment extends Fragment {
protected FragmentInteractionListener fragmentInteractionListener;
private String TAG;
- protected int sectionNumber;
+ private int sectionNumber;
protected static OkHttpClient client;
@Override
@@ -23,34 +23,34 @@ public abstract class BaseFragment extends Fragment {
super.onCreate(savedInstanceState);
TAG = getArguments().getString(ARG_TAG);
sectionNumber = getArguments().getInt(ARG_SECTION_NUMBER);
- if(client==null)
+ if (client == null)
client = BaseApplication.getInstance().getClient(); //must check every time - e.g.
// becomes null when app restarts after crash
- Report.d(TAG, "onCreate");
+ Timber.d("onCreate");
}
@Override
public void onStart() {
super.onStart();
- Report.d(TAG, "onStart");
+ Timber.d("onStart");
}
@Override
public void onResume() {
super.onResume();
- Report.d(TAG, "onResume");
+ Timber.d("onResume");
}
@Override
public void onPause() {
super.onPause();
- Report.d(TAG, "onPause");
+ Timber.d("onPause");
}
@Override
public void onStop() {
super.onStop();
- Report.d(TAG, "onStop");
+ Timber.d("onStop");
}
@Override
@@ -76,5 +76,6 @@ public abstract class BaseFragment extends Fragment {
* the activity that contains it, to allow communication upon interaction,
* between the fragment and the activity/ other fragments
*/
- public interface FragmentInteractionListener {}
+ public interface FragmentInteractionListener {
+ }
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/Category.java b/app/src/main/java/gr/thmmy/mthmmy/model/Category.java
index ce43c42d..46142bbf 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/model/Category.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/model/Category.java
@@ -7,8 +7,7 @@ import java.util.List;
import static android.R.attr.id;
-public class Category implements Parent
-{
+public class Category implements Parent {
private final String title;
private final String categoryURL;
private boolean expanded = false;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java b/app/src/main/java/gr/thmmy/mthmmy/model/Post.java
index 15b6afe6..baf8cc20 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/model/Post.java
@@ -27,6 +27,7 @@ public class Post {
private final boolean isDeleted;
private final int userColor;
private final ArrayList attachedFiles;
+ private final String lastEdit;
//Extra info
private final String profileURL;
@@ -57,6 +58,7 @@ public class Post {
personalText = "";
numberOfStars = 0;
attachedFiles = null;
+ lastEdit = null;
}
/**
@@ -80,12 +82,13 @@ public class Post {
* @param numberOfStars author's number of stars
* @param userColor author's user color
* @param attachedFiles post's attached files
+ * @param lastEdit post's last edit date
*/
public Post(@Nullable String thumbnailUrl, String author, String subject, String content
, int postIndex, int postNumber, String postDate, String profileURl, @Nullable String rank
, @Nullable String special_rank, @Nullable String gender, @Nullable String numberOfPosts
, @Nullable String personalText, int numberOfStars, int userColor
- , @Nullable ArrayList attachedFiles) {
+ , @Nullable ArrayList attachedFiles, @Nullable String lastEdit) {
if (Objects.equals(thumbnailUrl, "")) this.thumbnailUrl = null;
else this.thumbnailUrl = thumbnailUrl;
this.author = author;
@@ -97,6 +100,7 @@ public class Post {
this.isDeleted = false;
this.userColor = userColor;
this.attachedFiles = attachedFiles;
+ this.lastEdit = lastEdit;
this.profileURL = profileURl;
this.rank = rank;
this.specialRank = special_rank;
@@ -120,10 +124,11 @@ public class Post {
* @param postDate date of submission
* @param userColor author's user color
* @param attachedFiles post's attached files
+ * @param lastEdit post's last edit date
*/
public Post(@Nullable String thumbnailUrl, String author, String subject, String content
, int postIndex, int postNumber, String postDate, int userColor
- , @Nullable ArrayList attachedFiles) {
+ , @Nullable ArrayList attachedFiles, @Nullable String lastEdit) {
if (Objects.equals(thumbnailUrl, "")) this.thumbnailUrl = null;
else this.thumbnailUrl = thumbnailUrl;
this.author = author;
@@ -135,6 +140,7 @@ public class Post {
this.isDeleted = true;
this.userColor = userColor;
this.attachedFiles = attachedFiles;
+ this.lastEdit = lastEdit;
profileURL = null;
rank = "Rank";
specialRank = "Special rank";
@@ -310,4 +316,14 @@ public class Post {
public ArrayList getAttachedFiles() {
return attachedFiles;
}
+
+ /**
+ * Gets this post's last edit date or null if post hasn't been edited.
+ *
+ * @return date of last edit or null
+ */
+ @Nullable
+ public String getLastEdit() {
+ return lastEdit;
+ }
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java b/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java
index cc9a1781..9e935f69 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java
@@ -4,7 +4,7 @@ import android.net.Uri;
import java.util.Objects;
-import mthmmy.utils.Report;
+import timber.log.Timber;
/**
* This class consists exclusively of static classes (enums) and methods (excluding methods of inner
@@ -162,7 +162,7 @@ public class ThmmyPage {
|| Objects.equals(uriString, "https://www.thmmy.gr")
|| Objects.equals(uriString, "https://www.thmmy.gr/smf/index.php"))
return PageCategory.INDEX;
- Report.v(TAG, "Unknown thmmy link found, link: " + uriString);
+ Timber.v("Unknown thmmy link found, link: " + uriString);
return PageCategory.UNKNOWN_THMMY;
}
return PageCategory.NOT_THMMY;
@@ -170,7 +170,10 @@ public class ThmmyPage {
public static String getBoardId(String boardUrl) {
if (resolvePageCategory(Uri.parse(boardUrl)) == PageCategory.BOARD) {
- return boardUrl.substring(boardUrl.indexOf("board=") + 6, boardUrl.lastIndexOf("."));
+ String returnString = boardUrl.substring(boardUrl.indexOf("board=") + 6);
+ if (returnString.contains("."))
+ returnString = boardUrl.substring(boardUrl.indexOf("board=") + 6, boardUrl.lastIndexOf("."));
+ return returnString;
}
return null;
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/receiver/Receiver.java b/app/src/main/java/gr/thmmy/mthmmy/receiver/Receiver.java
index 3e811134..d93f4272 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/receiver/Receiver.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/receiver/Receiver.java
@@ -14,7 +14,8 @@ import android.webkit.MimeTypeMap;
import java.io.File;
import gr.thmmy.mthmmy.R;
-import mthmmy.utils.Report;
+
+import timber.log.Timber;
import static gr.thmmy.mthmmy.services.DownloadService.ACTION_DOWNLOAD;
import static gr.thmmy.mthmmy.services.DownloadService.COMPLETED;
@@ -28,7 +29,6 @@ import static gr.thmmy.mthmmy.services.DownloadService.SAVE_DIR;
import static gr.thmmy.mthmmy.services.DownloadService.STARTED;
public class Receiver extends BroadcastReceiver {
- private static final String TAG = "BroadcastReceiver";
public Receiver() {
}
@@ -72,7 +72,7 @@ public class Receiver extends BroadcastReceiver {
builder.setContentIntent(pendingIntent);
} else
- Report.w(TAG, "File doesn't exist.");
+ Timber.w("File doesn't exist.");
}
Notification notification = builder.build();
notificationManager.notify(id, notification);
diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/DownloadService.java b/app/src/main/java/gr/thmmy/mthmmy/services/DownloadService.java
index 7339fa73..52883ef4 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/services/DownloadService.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/services/DownloadService.java
@@ -13,12 +13,13 @@ import java.io.IOException;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.receiver.Receiver;
-import mthmmy.utils.Report;
+
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okio.BufferedSink;
import okio.Okio;
+import timber.log.Timber;
/**
* An {@link IntentService} subclass for handling asynchronous task requests in
@@ -26,7 +27,7 @@ import okio.Okio;
*/
public class DownloadService extends IntentService {
private static final String TAG = "DownloadService";
- private static int sDownloadId =0;
+ private static int sDownloadId = 0;
private Receiver receiver;
@@ -47,7 +48,6 @@ public class DownloadService extends IntentService {
public static final String FAILED = "Failed";
-
public DownloadService() {
super("DownloadService");
}
@@ -108,37 +108,31 @@ public class DownloadService extends IntentService {
Response response = client.newCall(request).execute();
String contentType = response.headers("Content-Type").toString(); //check if link provides a binary file
- if(contentType.equals("[application/octet-stream]"))
- {
+ if (contentType.equals("[application/octet-stream]")) {
fileName = response.headers("Content-Disposition").toString().split("\"")[1];
File dirPath = new File(SAVE_DIR);
- if(!dirPath.isDirectory())
- {
- if(dirPath.mkdirs())
- Report.i(TAG, "mTHMMY's directory created successfully!");
+ if (!dirPath.isDirectory()) {
+ if (dirPath.mkdirs())
+ Timber.i("mTHMMY's directory created successfully!");
else
- Report.e(TAG, "Couldn't create mTHMMY's directory...");
+ Timber.e("Couldn't create mTHMMY's directory...");
}
String nameFormat;
String[] tokens = fileName.split("\\.(?=[^\\.]+$)");
- if(tokens.length!=2)
- {
- Report.w(TAG, "Couldn't get file extension...");
+ if (tokens.length != 2) {
+ Timber.w("Couldn't get file extension...");
nameFormat = fileName + "(%d)";
- }
- else
+ } else
nameFormat = tokens[0] + "(%d)." + tokens[1];
-
-
File file = new File(dirPath, fileName);
- for (int i = 1;;i++) {
+ for (int i = 1; ; i++) {
if (!file.exists()) {
break;
}
@@ -148,29 +142,26 @@ public class DownloadService extends IntentService {
fileName = file.getName();
- Report.v(TAG, "Started saving file " + fileName);
+ Timber.v("Started saving file " + fileName);
sendNotification(downloadId, STARTED, fileName);
sink = Okio.buffer(Okio.sink(file));
sink.writeAll(response.body().source());
sink.flush();
- Report.i(TAG, "Download OK!");
+ Timber.i("Download OK!");
sendNotification(downloadId, COMPLETED, fileName);
- }
- else
- Report.e(TAG, "Response not a binary file!");
- }
- catch (FileNotFoundException e){
- Report.i(TAG, "Download failed...");
- Report.e(TAG, "FileNotFound", e);
+ } else
+ Timber.e("Response not a binary file!");
+ } catch (FileNotFoundException e) {
+ Timber.i("Download failed...");
+ Timber.e("FileNotFound", e);
sendNotification(downloadId, FAILED, fileName);
- }
- catch (IOException e){
- Report.i(TAG, "Download failed...");
- Report.e(TAG, "IOException", e);
+ } catch (IOException e) {
+ Timber.i("Download failed...");
+ Timber.e("IOException", e);
sendNotification(downloadId, FAILED, fileName);
} finally {
- if (sink!= null) {
+ if (sink != null) {
try {
sink.close();
} catch (IOException e) {
@@ -180,9 +171,8 @@ public class DownloadService extends IntentService {
}
}
- private void sendNotification(int downloadId, String type, @NonNull String fileName)
- {
- Intent intent = new Intent(ACTION_DOWNLOAD);
+ private void sendNotification(int downloadId, String type, @NonNull String fileName) {
+ Intent intent = new Intent(ACTION_DOWNLOAD);
switch (type) {
case STARTED: {
intent.putExtra(EXTRA_NOTIFICATION_TITLE, "Download Started");
@@ -202,8 +192,8 @@ public class DownloadService extends IntentService {
intent.putExtra(EXTRA_NOTIFICATION_TICKER, "Download Failed");
break;
}
- default:{
- Report.wtf(TAG, "Invalid notification case!");
+ default: {
+ Timber.e("Invalid notification case!");
return;
}
}
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 2321b88a..1455fb27 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java
@@ -18,7 +18,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import gr.thmmy.mthmmy.utils.exceptions.ParseException;
-import mthmmy.utils.Report;
import okhttp3.Cookie;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
@@ -26,15 +25,13 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
+import timber.log.Timber;
/**
* This class handles all session related operations (e.g. login, logout)
* and stores data to SharedPreferences (session information and cookies).
*/
public class SessionManager {
- //Class TAG
- private static final String TAG = "SessionManager";
-
//Generic constants
public static final HttpUrl indexUrl = HttpUrl.parse("https://www.thmmy.gr/smf/index.php?theme=4");
public static final HttpUrl forumUrl = HttpUrl.parse("https://www.thmmy.gr/smf/index.php?action=forum;theme=4");
@@ -51,12 +48,12 @@ public class SessionManager {
public static final int EXCEPTION = 6;
// Client & Cookies
- private OkHttpClient client;
- private PersistentCookieJar cookieJar;
- private SharedPrefsCookiePersistor cookiePersistor; //Used to explicitly edit cookies in cookieJar
+ private final OkHttpClient client;
+ private final PersistentCookieJar cookieJar;
+ private final SharedPrefsCookiePersistor cookiePersistor; //Used to explicitly edit cookies in cookieJar
//Shared Preferences & its keys
- private SharedPreferences sharedPrefs;
+ private final SharedPreferences sharedPrefs;
private static final String USERNAME = "Username";
private static final String AVATAR_LINK = "AvatarLink";
private static final String HAS_AVATAR = "HasAvatar";
@@ -80,7 +77,7 @@ public class SessionManager {
* Always call it in a separate thread.
*/
public int login(String... strings) {
- Report.i(TAG, "Logging in...");
+ Timber.i("Logging in...");
//Build the login request for each case
Request request;
@@ -112,9 +109,9 @@ public class SessionManager {
Elements unreadRepliesLinks = document.select("a[href=https://www.thmmy.gr/smf/index.php?action=unreadreplies]");
- if (unreadRepliesLinks.size()>=2) //Normally it's just == 2, but who knows what can be posted by users
+ if (unreadRepliesLinks.size() >= 2) //Normally it's just == 2, but who knows what can be posted by users
{
- Report.i(TAG, "Login successful!");
+ Timber.i("Login successful!");
setPersistentCookieSession(); //Store cookies
//Edit SharedPreferences, save session's data
@@ -133,18 +130,18 @@ public class SessionManager {
return SUCCESS;
} else {
- Report.i(TAG, "Login failed.");
+ Timber.i("Login failed.");
//Investigate login failure
Elements error = document.select("b:contains(That username does not exist.)");
if (error.size() == 1) { //Wrong username
- Report.i(TAG, "Wrong Username");
+ Timber.i("Wrong Username");
return WRONG_USER;
}
error = document.select("body:contains(Password incorrect)");
if (error.size() == 1) { //Wrong password
- Report.i(TAG, "Wrong Password");
+ Timber.i("Wrong Password");
return WRONG_PASSWORD;
}
@@ -154,13 +151,13 @@ public class SessionManager {
}
//Handle exception
} catch (InterruptedIOException e) {
- Report.i(TAG, "Login InterruptedIOException"); //users cancels LoginTask
+ Timber.i("Login InterruptedIOException"); //users cancels LoginTask
return CANCELLED;
} catch (IOException e) {
- Report.w(TAG, "Login IOException", e);
+ Timber.w("Login IOException", e);
return CONNECTION_ERROR;
} catch (Exception e) {
- Report.w(TAG, "Login Exception (other)", e);
+ Timber.w("Login Exception (other)", e);
return EXCEPTION;
}
}
@@ -175,7 +172,7 @@ public class SessionManager {
* fragments' data are retrieved).
*/
public void validateSession() {
- Report.i(TAG, "Validating session...");
+ Timber.i("Validating session...");
if (isLoggedIn()) {
int loginResult = login();
@@ -192,7 +189,7 @@ public class SessionManager {
* Call this function when user explicitly chooses to continue as a guest (UI thread).
*/
public void guestLogin() {
- Report.i("TAG", "Continuing as a guest, as chosen by the user.");
+ Timber.i("Continuing as a guest, as chosen by the user.");
clearSessionData();
sharedPrefs.edit().putBoolean(LOGIN_SCREEN_AS_DEFAULT, false).apply();
}
@@ -202,7 +199,7 @@ public class SessionManager {
* Logout function. Always call it in a separate thread.
*/
public int logout() {
- Report.i(TAG, "Logging out...");
+ Timber.i("Logging out...");
Request request = new Request.Builder()
.url(sharedPrefs.getString(LOGOUT_LINK, "LogoutLink"))
@@ -216,17 +213,17 @@ public class SessionManager {
Elements loginButton = document.select("[value=Login]"); //Attempt to find login button
if (!loginButton.isEmpty()) //If login button exists, logout was successful
{
- Report.i(TAG, "Logout successful!");
+ Timber.i("Logout successful!");
return SUCCESS;
} else {
- Report.i(TAG, "Logout failed.");
+ Timber.i("Logout failed.");
return FAILURE;
}
} catch (IOException e) {
- Report.w(TAG, "Logout IOException", e);
+ Timber.w("Logout IOException", e);
return CONNECTION_ERROR;
} catch (Exception e) {
- Report.w(TAG, "Logout Exception", e);
+ Timber.w("Logout Exception", e);
return EXCEPTION;
} finally {
//All data should always be cleared from device regardless the result of logout
@@ -288,11 +285,11 @@ public class SessionManager {
sharedPrefs.edit().clear().apply(); //Clear session data
sharedPrefs.edit().putString(USERNAME, guestName).apply();
sharedPrefs.edit().putBoolean(LOGGED_IN, false).apply(); //User logs out
- Report.i(TAG, "Session data cleared.");
+ Timber.i("Session data cleared.");
}
@NonNull
- private String extractUserName(@NonNull Document doc){
+ private String extractUserName(@NonNull Document doc) {
//Scribbles2 Theme
Elements user = doc.select("div[id=myuser] > h3");
String userName = null;
@@ -304,8 +301,7 @@ public class SessionManager {
Matcher matcher = pattern.matcher(txt);
if (matcher.find())
userName = matcher.group(1);
- }
- else {
+ } else {
//Helios_Multi and SMF_oneBlue
user = doc.select("td.smalltext[width=100%] b");
if (user.size() == 1)
@@ -317,22 +313,22 @@ public class SessionManager {
userName = user.first().ownText();
}
}
-
- if(userName != null && !userName.isEmpty())
+
+ if (userName != null && !userName.isEmpty())
return userName;
- Report.e(TAG, "ParseException", new ParseException("Parsing failed(username extraction)"));
- return "User"; //return a default username
+ Timber.e("ParseException", new ParseException("Parsing failed(username extraction)"));
+ return "User"; //return a default username
}
@Nullable
private String extractAvatarLink(@NonNull Document doc) {
- Elements avatar = doc.getElementsByClass("avatar");
- if (!avatar.isEmpty())
- return avatar.first().attr("src");
+ Elements avatar = doc.getElementsByClass("avatar");
+ if (!avatar.isEmpty())
+ return avatar.first().attr("src");
- Report.e(TAG, "Extracting avatar's link failed!");
+ Timber.i("Extracting avatar's link failed!");
return null;
}
@@ -340,14 +336,13 @@ public class SessionManager {
private String extractLogoutLink(@NonNull Document doc) {
Elements logoutLink = doc.select("a[href^=https://www.thmmy.gr/smf/index.php?action=logout;sesc=]");
- if (!logoutLink.isEmpty())
- {
+ if (!logoutLink.isEmpty()) {
String link = logoutLink.first().attr("href");
- if(link != null && !link.isEmpty())
+ if (link != null && !link.isEmpty())
return link;
}
- Report.e(TAG, "ParseException", new ParseException("Parsing failed(logoutLink extraction)"));
- return "https://www.thmmy.gr/smf/index.php?action=logout"; //return a default link
+ Timber.e("ParseException", new ParseException("Parsing failed(logoutLink extraction)"));
+ return "https://www.thmmy.gr/smf/index.php?action=logout"; //return a default link
}
//----------------------------------OTHER FUNCTIONS END-----------------------------------------
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/CenterVerticalSpan.java b/app/src/main/java/gr/thmmy/mthmmy/utils/CenterVerticalSpan.java
index 949a9736..12f0a9e6 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/CenterVerticalSpan.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/CenterVerticalSpan.java
@@ -1,20 +1,23 @@
package gr.thmmy.mthmmy.utils;
-import android.text.TextPaint;
-import android.text.style.SuperscriptSpan;
-import android.util.Log;
-
-public class CenterVerticalSpan extends SuperscriptSpan {
- public CenterVerticalSpan() {
- }
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.text.style.ReplacementSpan;
+public class CenterVerticalSpan extends ReplacementSpan {
@Override
- public void updateDrawState(TextPaint textPaint) {
- textPaint.baselineShift -= 7f;
+ public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
+ text = text.subSequence(start, end);
+ return (int) paint.measureText(text.toString());
}
@Override
- public void updateMeasureState(TextPaint tp) {
- updateDrawState(tp);
+ public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom,@NonNull Paint paint) {
+ text = text.subSequence(start, end);
+ Rect charSize = new Rect();
+ paint.getTextBounds(text.toString(), 0, 1, charSize);
+ canvas.drawText(text.toString(), x, (bottom + charSize.height()) / 2f, paint);
}
}
\ No newline at end of file
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java b/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java
new file mode 100644
index 00000000..2aeca9a2
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java
@@ -0,0 +1,34 @@
+package gr.thmmy.mthmmy.utils;
+
+import android.util.Log;
+
+import com.google.firebase.crash.FirebaseCrash;
+
+import gr.thmmy.mthmmy.utils.exceptions.UnknownException;
+import timber.log.Timber;
+
+public class CrashReportingTree extends Timber.Tree {
+ @Override
+ protected void log(int priority, String tag, String message, Throwable t) {
+ if (priority == Log.VERBOSE || priority == Log.DEBUG) {
+ return;
+ }
+
+ String level="A";
+
+ if (priority == Log.INFO)
+ level = "I";
+ else if (priority == Log.WARN)
+ level = "W";
+ else if(priority == Log.ERROR)
+ level = "E";
+
+ FirebaseCrash.log(level + "/" + tag + ": " + message);
+
+ if(t==null)
+ t = new UnknownException("UnknownException");
+
+ if ((priority == Log.ERROR))
+ FirebaseCrash.report(t);
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/ParseHelpers.java b/app/src/main/java/gr/thmmy/mthmmy/utils/ParseHelpers.java
index 2520cc89..75a9fa72 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/ParseHelpers.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ParseHelpers.java
@@ -153,15 +153,16 @@ public class ParseHelpers {
fixed = fixed.replace(
fixed.substring(fixed.indexOf("