diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cd22f77e..b3c840ca 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.2" + ANDROID_BUILD_TOOLS: "25.0.3" ANDROID_SDK_TOOLS: "25.2.5" before_script: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 44be1c74..6f5733a4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,35 +13,33 @@ vulnerabilities, please report them in private to There are many ways of contributing to mTHMMY: -- Simply using the latest release version (anonymous reports are sent automatically) -- Becoming an alpha or beta tester +- Simply using the [latest release version][google-play] from Google Play (anonymous reports are sent automatically) - Joining our [Discord server][discord-server] -- Submiting bugs and ideas on our [Trello board][trello-board] -- Getting code access to fork mTHMMY and submit [merge requests](#merge-requests) +- Submitting bugs and ideas on our [Trello board][trello-board] +- Forking mTHMMY and submitting [merge requests](#merge-requests) - Joining our core team - Contacting us by email at `thmmynolife@gmail.com` ## Issue tracker The [mTHMMY Board on trello.com][trello-board] is used as an Issue Tracker, for bugs and improvements concerning the available mTHMMY releases. -Before creating a card to submit an issue please **[search the board][trello-board]** for similar entries. +Before creating a card to submit an issue please **search the board** for similar entries. ## Merge requests Merge requests with fixes and improvements to mTHMMY are most welcome. Any developer that wants to work independently from the core team can simply follow the workflow below to make a merge request: -1. Request and get access to the repository 1. Fork the project into your personal space on GitLab.com -1. Create a feature branch, branch away from `develop` +1. Create a feature branch, away from `develop` 1. Push the commit(s) to your fork 1. Create a merge request (MR) targeting `develop` [at mTHMMY](https://gitlab.com/ThmmyNoLife/mTHMMY/tree/develop) 1. Fill the MR title describing the change you want to make -1. Fill the MR description with a brief motive for your change and the method you used to achieve it. +1. Fill the MR description with a brief motive for your change and the method you used to achieve it 1. Submit the MR. - +[google-play]: https://play.google.com/store/apps/details?id=gr.thmmy.mthmmy [trello-board]: https://trello.com/invite/b/4MVlkrkg/44a931707bd0b84a5e0bdfc42b9ae4f1/mthmmy [discord-server]: https://discord.gg/CVt3yrn diff --git a/README.md b/README.md index 89f8a134..f27cc28a 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,12 @@ mTHMMY is a mobile app for the [thmmy.gr](https://www.thmmy.gr) community. mTHMMY can be installed on any smartphone with Android 4.4 KitKat or newer. +## Download + +The latest release version is available on Google Play: + +Get it on Google Play + ## Contributing Please refer to [CONTRIBUTING.md](/CONTRIBUTING.md) for details. @@ -20,5 +26,7 @@ Please refer to [CONTRIBUTING.md](/CONTRIBUTING.md) for details. 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]. +**Legal attribution: Google Play and the Google Play logo are trademarks of Google Inc.* + [discord-server]: https://discord.gg/CVt3yrn [trello-board]: https://trello.com/invite/b/4MVlkrkg/44a931707bd0b84a5e0bdfc42b9ae4f1/mthmmy diff --git a/VERSION b/VERSION index 6085e946..f0bb29e7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.1 +1.3.0 diff --git a/app/build.gradle b/app/build.gradle index 1517040e..cc50d899 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,21 +2,21 @@ apply plugin: 'com.android.application' android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { vectorDrawables.useSupportLibrary = true applicationId "gr.thmmy.mthmmy" minSdkVersion 19 targetSdkVersion 25 - versionCode 7 - versionName "1.2.1" + versionCode 8 + versionName "1.3.0" archivesBaseName = "mTHMMY-v$versionName" } buildTypes { release { - minifyEnabled false + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } /*debug { @@ -28,25 +28,25 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - 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.android.support:appcompat-v7:25.3.1' + compile 'com.android.support:design:25.3.1' + compile 'com.android.support:support-v4:25.3.1' + compile 'com.android.support:cardview-v7:25.3.1' + compile 'com.android.support:recyclerview-v7:25.3.1' + compile 'com.google.firebase:firebase-crash:10.2.6' + compile 'com.squareup.okhttp3:okhttp:3.8.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.2@aar') { + compile 'com.github.franmontiel:PersistentCookieJar:v1.0.1' + compile 'com.github.PhilJay:MPAndroidChart:v3.0.2' + compile('com.mikepenz:materialdrawer:5.9.2@aar') { transitive = true } compile 'com.mikepenz:fontawesome-typeface:4.7.0.0@aar' - 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 'pl.droidsonroids.gif:android-gif-drawable:1.2.7' + compile 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1' //Deprecated! + compile 'me.zhanghai.android.materialprogressbar:library:1.4.1' compile 'com.jakewharton.timber:timber:4.5.1' } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index ff7615bd..b00af7bc 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -15,3 +15,23 @@ #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} + +# OkHttp +-dontwarn okio.** +-dontwarn javax.annotation.Nullable +-dontwarn javax.annotation.ParametersAreNonnullByDefault + +# Picasso +-dontwarn com.squareup.okhttp.** + +# Android-Iconics (fontawesome-typeface) +-keep class .R +-keep class **.R$* { + ; +} + +# android-gif-drawable +-keep public class pl.droidsonroids.gif.GifIOException{(int, java.lang.String);} + +#JSoup +-keep class org.jsoup.** \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8a57fcbc..8e78f4e6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,12 +1,12 @@ + package="gr.thmmy.mthmmy" + android:installLocation="auto"> - - - - + + + + - + - + - + - - + + + android:scheme="http" /> + android:scheme="http" /> + android:scheme="https" /> + android:scheme="https" /> - + + android:windowSoftInputMode="adjustPan" /> + android:value=".activities.main.MainActivity" /> + android:value=".activities.main.MainActivity" /> + android:theme="@style/AppTheme.NoActionBar" /> + android:value=".activities.main.MainActivity" /> + android:value=".activities.main.MainActivity" /> + android:value=".activities.main.MainActivity" /> + android:resource="@xml/provider_paths" /> + android:exported="false" /> - - + + diff --git a/app/src/main/assets/YouTube_light_color_icon.png b/app/src/main/assets/YouTube_light_color_icon.png new file mode 100644 index 00000000..bfd6db39 Binary files /dev/null and b/app/src/main/assets/YouTube_light_color_icon.png differ diff --git a/app/src/main/assets/apache_libraries.html b/app/src/main/assets/apache_libraries.html index 2c0ceabc..45905fa2 100644 --- a/app/src/main/assets/apache_libraries.html +++ b/app/src/main/assets/apache_libraries.html @@ -39,25 +39,25 @@ diff --git a/app/src/main/assets/mit_libraries.html b/app/src/main/assets/mit_libraries.html index 2c6cf3bb..42b46e78 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.5 (Copyright ©2016 Karol Wrótniak, Droids on Roids)
    +
    android-gif-drawable v1.2.7 (Copyright ©2016 Karol Wrótniak, Droids on Roids)
  • Expandable RecyclerView v3.0.0-RC1 (Copyright ©2015, Big Nerd Ranch)
    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 ec0c2b25..d010eb8f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/BookmarkActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/BookmarkActivity.java @@ -21,7 +21,12 @@ import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_URL; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_TITLE; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL; +//TODO proper handling with adapter etc. +//TODO better UI +//TODO after clicking bookmark and then back button should return to this activity public class BookmarkActivity extends BaseActivity { + private TextView boardsTitle; + private TextView topicsTitle; @Override protected void onCreate(Bundle savedInstanceState) { @@ -43,93 +48,100 @@ public class BookmarkActivity extends BaseActivity { LinearLayout bookmarksLinearView = (LinearLayout) findViewById(R.id.bookmarks_container); LayoutInflater layoutInflater = getLayoutInflater(); - TextView tmp = new TextView(this); - tmp.setLayoutParams(new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT - , LinearLayout.LayoutParams.WRAP_CONTENT)); - tmp.setText(getString(R.string.board_bookmarks_title)); - tmp.setTypeface(tmp.getTypeface(), Typeface.BOLD); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - tmp.setTextColor(getColor(R.color.primary_text)); - } else { - //noinspection deprecation - tmp.setTextColor(getResources().getColor(R.color.primary_text)); - } - tmp.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); - tmp.setTextSize(20f); - bookmarksLinearView.addView(tmp); + if(!getBoardsBookmarked().isEmpty()) { + boardsTitle = new TextView(this); + boardsTitle.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT + , LinearLayout.LayoutParams.WRAP_CONTENT)); + boardsTitle.setText(getString(R.string.board_bookmarks_title)); + boardsTitle.setTypeface(boardsTitle.getTypeface(), Typeface.BOLD); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + boardsTitle.setTextColor(getColor(R.color.primary_text)); + } else { + //noinspection deprecation + boardsTitle.setTextColor(getResources().getColor(R.color.primary_text)); + } + boardsTitle.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); + boardsTitle.setTextSize(20f); + bookmarksLinearView.addView(boardsTitle); - for (final Bookmark bookmarkedBoard : getBoardsBookmarked()) { - if (bookmarkedBoard != null && bookmarkedBoard.getTitle() != null) { - final LinearLayout row = (LinearLayout) layoutInflater.inflate( - R.layout.activity_bookmark_row, bookmarksLinearView, false); - row.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Intent intent = new Intent(BookmarkActivity.this, BoardActivity.class); - Bundle extras = new Bundle(); - extras.putString(BUNDLE_BOARD_URL, "https://www.thmmy.gr/smf/index.php?board=" - + bookmarkedBoard.getId() + ".0"); - extras.putString(BUNDLE_BOARD_TITLE, bookmarkedBoard.getTitle()); - intent.putExtras(extras); - startActivity(intent); - finish(); - } - }); - ((TextView) row.findViewById(R.id.bookmark_title)).setText(bookmarkedBoard.getTitle()); - (row.findViewById(R.id.remove_bookmark)).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - removeBookmark(bookmarkedBoard); - row.setVisibility(View.GONE); - } - }); - bookmarksLinearView.addView(row); + for (final Bookmark bookmarkedBoard : getBoardsBookmarked()) { + if (bookmarkedBoard != null && bookmarkedBoard.getTitle() != null) { + final LinearLayout row = (LinearLayout) layoutInflater.inflate( + R.layout.activity_bookmark_row, bookmarksLinearView, false); + row.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(BookmarkActivity.this, BoardActivity.class); + Bundle extras = new Bundle(); + extras.putString(BUNDLE_BOARD_URL, "https://www.thmmy.gr/smf/index.php?board=" + + bookmarkedBoard.getId() + ".0"); + extras.putString(BUNDLE_BOARD_TITLE, bookmarkedBoard.getTitle()); + intent.putExtras(extras); + startActivity(intent); + finish(); + } + }); + ((TextView) row.findViewById(R.id.bookmark_title)).setText(bookmarkedBoard.getTitle()); + (row.findViewById(R.id.remove_bookmark)).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + removeBookmark(bookmarkedBoard); + row.setVisibility(View.GONE); + updateTitles(); + } + }); + bookmarksLinearView.addView(row); + } } } - tmp = new TextView(this); - tmp.setLayoutParams(new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT - , LinearLayout.LayoutParams.WRAP_CONTENT)); - tmp.setText(getString(R.string.topic_bookmarks_title)); - tmp.setTypeface(tmp.getTypeface(), Typeface.BOLD); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - tmp.setTextColor(getColor(R.color.primary_text)); - } else { - //noinspection deprecation - tmp.setTextColor(getResources().getColor(R.color.primary_text)); - } - tmp.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); - tmp.setTextSize(20f); - bookmarksLinearView.addView(tmp); - for (final Bookmark bookmarkedTopic : getTopicsBookmarked()) { - if (bookmarkedTopic != null && bookmarkedTopic.getTitle() != null) { - final LinearLayout row = (LinearLayout) layoutInflater.inflate( - R.layout.activity_bookmark_row, bookmarksLinearView, false); - row.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - 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() + "." + 2147483647); - extras.putString(BUNDLE_TOPIC_TITLE, bookmarkedTopic.getTitle()); - intent.putExtras(extras); - startActivity(intent); - finish(); - } - }); - ((TextView) row.findViewById(R.id.bookmark_title)).setText(bookmarkedTopic.getTitle()); - (row.findViewById(R.id.remove_bookmark)).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - removeBookmark(bookmarkedTopic); - row.setVisibility(View.GONE); - } - }); - bookmarksLinearView.addView(row); + if(!getTopicsBookmarked().isEmpty()) { + topicsTitle = new TextView(this); + topicsTitle.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT + , LinearLayout.LayoutParams.WRAP_CONTENT)); + topicsTitle.setText(getString(R.string.topic_bookmarks_title)); + topicsTitle.setTypeface(topicsTitle.getTypeface(), Typeface.BOLD); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + topicsTitle.setTextColor(getColor(R.color.primary_text)); + } else { + //noinspection deprecation + topicsTitle.setTextColor(getResources().getColor(R.color.primary_text)); + } + topicsTitle.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); + topicsTitle.setTextSize(20f); + bookmarksLinearView.addView(topicsTitle); + + for (final Bookmark bookmarkedTopic : getTopicsBookmarked()) { + if (bookmarkedTopic != null && bookmarkedTopic.getTitle() != null) { + final LinearLayout row = (LinearLayout) layoutInflater.inflate( + R.layout.activity_bookmark_row, bookmarksLinearView, false); + row.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + 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() + "." + 2147483647); + extras.putString(BUNDLE_TOPIC_TITLE, bookmarkedTopic.getTitle()); + intent.putExtras(extras); + startActivity(intent); + finish(); + } + }); + ((TextView) row.findViewById(R.id.bookmark_title)).setText(bookmarkedTopic.getTitle()); + (row.findViewById(R.id.remove_bookmark)).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + removeBookmark(bookmarkedTopic); + row.setVisibility(View.GONE); + updateTitles(); + } + }); + bookmarksLinearView.addView(row); + } } } } @@ -139,4 +151,12 @@ public class BookmarkActivity extends BaseActivity { drawer.setSelection(BOOKMARKS_ID); super.onResume(); } + + private void updateTitles() + { + if(getBoardsBookmarked().isEmpty()&&boardsTitle!=null) + boardsTitle.setVisibility(View.GONE); + if(getTopicsBookmarked().isEmpty()&&topicsTitle!=null) + topicsTitle.setVisibility(View.GONE); + } } 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 39a770c2..460b0b0d 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,6 @@ import android.widget.Toast; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.activities.main.MainActivity; import gr.thmmy.mthmmy.base.BaseActivity; - import timber.log.Timber; import static gr.thmmy.mthmmy.session.SessionManager.CONNECTION_ERROR; 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 00dbb69a..633ab6ec 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 @@ -8,12 +8,12 @@ import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.View; import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.Toast; -import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; @@ -21,17 +21,15 @@ import org.jsoup.select.Elements; import java.util.ArrayList; import java.util.Objects; -import javax.net.ssl.SSLHandshakeException; - import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.model.Board; import gr.thmmy.mthmmy.model.Bookmark; import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.model.Topic; +import gr.thmmy.mthmmy.utils.ParseTask; +import gr.thmmy.mthmmy.utils.exceptions.ParseException; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; -import okhttp3.Request; -import okhttp3.Response; import timber.log.Timber; public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMoreListener { @@ -164,6 +162,13 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo } } + @Override + public void onResume() { + super.onResume(); + Log.d("Boardaa", "onResume called!"); + refreshBoardBookmark((ImageButton) findViewById(R.id.bookmark)); + } + @Override public void onDestroy() { super.onDestroy(); @@ -176,47 +181,15 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo *

    BoardTask's {@link AsyncTask#execute execute} method needs a boards's url as String * parameter!

    */ - private class BoardTask extends AsyncTask { + private class BoardTask extends ParseTask { @Override protected void onPreExecute() { if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE); if (newTopicFAB.getVisibility() != View.GONE) newTopicFAB.setEnabled(false); } - @Override - protected Void doInBackground(String... boardUrl) { - Request request = new Request.Builder() - .url(boardUrl[0]) - .build(); - try { - Response response = client.newCall(request).execute(); - parseBoard(Jsoup.parse(response.body().string())); - } catch (SSLHandshakeException e) { - Timber.w("Certificate problem (please switch to unsafe connection)."); - } catch (Exception e) { - Timber.e("ERROR", e); - } - return null; - } - - @Override - protected void onPostExecute(Void voids) { - if (boardTitle == null || Objects.equals(boardTitle, "") - || !Objects.equals(boardTitle, parsedTitle)) { - boardTitle = parsedTitle; - toolbar.setTitle(boardTitle); - thisPageBookmark = new Bookmark(boardTitle, ThmmyPage.getBoardId(boardUrl)); - } - - //Parse was successful - ++pagesLoaded; - if (newTopicFAB.getVisibility() != View.GONE) newTopicFAB.setEnabled(true); - progressBar.setVisibility(ProgressBar.INVISIBLE); - boardAdapter.notifyDataSetChanged(); - isLoadingMore = false; - } - - private void parseBoard(Document boardPage) { + @Override //TODO should throw ParseException + public void parse(Document boardPage) throws ParseException { parsedTitle = boardPage.select("div.nav a.nav").last().text(); //Removes loading item @@ -320,5 +293,23 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo } } } + + @Override + protected void postParsing(ResultCode result) { + //TODO if (result == ResultCode.SUCCESS)... + if (boardTitle == null || Objects.equals(boardTitle, "") + || !Objects.equals(boardTitle, parsedTitle)) { + boardTitle = parsedTitle; + toolbar.setTitle(boardTitle); + thisPageBookmark = new Bookmark(boardTitle, ThmmyPage.getBoardId(boardUrl)); + } + + //Parse was successful + ++pagesLoaded; + if (newTopicFAB.getVisibility() != View.GONE) newTopicFAB.setEnabled(true); + progressBar.setVisibility(ProgressBar.INVISIBLE); + boardAdapter.notifyDataSetChanged(); + isLoadingMore = false; + } } } 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 b4339f14..8c944f7f 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,7 +27,6 @@ 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 okhttp3.Request; import okhttp3.Response; import timber.log.Timber; @@ -189,7 +188,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter. } catch (SSLHandshakeException e) { Timber.w("Certificate problem (please switch to unsafe connection)."); } catch (Exception e) { - Timber.e("ERROR", e); + Timber.e(e, "Exception"); } return null; } 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 ddc604b4..9e665182 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 @@ -8,15 +8,18 @@ import android.support.v4.app.Fragment; 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.widget.Toast; +import java.util.ArrayList; +import java.util.List; + import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.activities.LoginActivity; import gr.thmmy.mthmmy.activities.board.BoardActivity; import gr.thmmy.mthmmy.activities.downloads.DownloadsActivity; import gr.thmmy.mthmmy.activities.main.forum.ForumFragment; import gr.thmmy.mthmmy.activities.main.recent.RecentFragment; +import gr.thmmy.mthmmy.activities.main.unread.UnreadFragment; import gr.thmmy.mthmmy.activities.profile.ProfileActivity; import gr.thmmy.mthmmy.activities.topic.TopicActivity; import gr.thmmy.mthmmy.base.BaseActivity; @@ -34,11 +37,13 @@ import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_ import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_TITLE; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL; -public class MainActivity extends BaseActivity implements RecentFragment.RecentFragmentInteractionListener, ForumFragment.ForumFragmentInteractionListener { +public class MainActivity extends BaseActivity implements RecentFragment.RecentFragmentInteractionListener, ForumFragment.ForumFragmentInteractionListener, UnreadFragment.UnreadFragmentInteractionListener { - //----------------------------------------CLASS VARIABLES----------------------------------------- +//-----------------------------------------CLASS VARIABLES------------------------------------------ private static final int TIME_INTERVAL = 2000; private long mBackPressed; + private SectionsPagerAdapter sectionsPagerAdapter; + private ViewPager viewPager; @Override protected void onCreate(Bundle savedInstanceState) { @@ -48,7 +53,6 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF setContentView(R.layout.activity_main); if (sessionManager.isLoginScreenDefault()) - { //Go to login Intent intent = new Intent(MainActivity.this, LoginActivity.class); @@ -57,25 +61,25 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF overridePendingTransition(R.anim.push_right_in, R.anim.push_right_out); } - //Initialize toolbar - toolbar = (Toolbar) - - findViewById(R.id.toolbar); - - setSupportActionBar(toolbar); - //Initialize drawer createDrawer(); //Create the adapter that will return a fragment for each section of the activity - SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); + sectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); + sectionsPagerAdapter.addFragment(RecentFragment.newInstance(1), "RECENT"); + sectionsPagerAdapter.addFragment(ForumFragment.newInstance(2), "FORUM"); + if(sessionManager.isLoggedIn()) + sectionsPagerAdapter.addFragment(UnreadFragment.newInstance(3), "UNREAD"); + //Set up the ViewPager with the sections adapter. - ViewPager mViewPager = (ViewPager) findViewById(R.id.container); - mViewPager.setAdapter(mSectionsPagerAdapter); + viewPager = (ViewPager) findViewById(R.id.container); + viewPager.setAdapter(sectionsPagerAdapter); TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); - tabLayout.setupWithViewPager(mViewPager); + tabLayout.setupWithViewPager(viewPager); + + setMainActivity(this); } @Override @@ -88,6 +92,7 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF @Override protected void onResume() { drawer.setSelection(HOME_ID); + updateTabs(); super.onResume(); } @@ -122,6 +127,17 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF startActivity(i); } + @Override + public void onUnreadFragmentInteraction(TopicSummary topicSummary) { + if (topicSummary.getLastUser() == null && topicSummary.getDateTimeModified() == null) { + return; //TODO! + } + Intent i = new Intent(MainActivity.this, TopicActivity.class); + i.putExtra(BUNDLE_TOPIC_URL, topicSummary.getTopicUrl()); + i.putExtra(BUNDLE_TOPIC_TITLE, topicSummary.getSubject()); + startActivity(i); + } + //---------------------------------FragmentPagerAdapter--------------------------------------------- /** @@ -130,42 +146,57 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF * it may be best to switch to a * {@link android.support.v4.app.FragmentStatePagerAdapter}. */ - public class SectionsPagerAdapter extends FragmentPagerAdapter { + private class SectionsPagerAdapter extends FragmentPagerAdapter { + private final List fragmentList = new ArrayList<>(); + private final List fragmentTitleList = new ArrayList<>(); SectionsPagerAdapter(FragmentManager fm) { super(fm); } + void addFragment(Fragment fragment, String title) { + fragmentList.add(fragment); + fragmentTitleList.add(title); + notifyDataSetChanged(); + } + + void removeFragment(int position) { + fragmentList.remove(position); + fragmentTitleList.remove(position); + notifyDataSetChanged(); + if(viewPager.getCurrentItem()==position) + viewPager.setCurrentItem(position-1); + } + @Override public Fragment getItem(int position) { - switch (position) { - case 0: - return RecentFragment.newInstance(position + 1); - case 1: - return ForumFragment.newInstance(position + 1); - default: - return RecentFragment.newInstance(position + 1); //temp (?) - } + return fragmentList.get(position); } @Override public int getCount() { - // Show 2 total pages. - return 2; + return fragmentList.size(); } @Override public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return "RECENT POSTS"; - case 1: - return "FORUM"; - } + return fragmentTitleList.get(position); + } - return null; + @Override + public int getItemPosition(Object object) { + int position = fragmentList.indexOf(object); + return position == -1 ? POSITION_NONE : position; } } + + public void updateTabs() + { + if(!sessionManager.isLoggedIn()&§ionsPagerAdapter.getCount()==3) + sectionsPagerAdapter.removeFragment(2); + else if(sessionManager.isLoggedIn()&§ionsPagerAdapter.getCount()==2) + sectionsPagerAdapter.addFragment(UnreadFragment.newInstance(3), "UNREAD"); + } //-------------------------------FragmentPagerAdapter END------------------------------------------- private void redirectToActivityFromIntent(Intent intent) { 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 9f896b29..821c83a5 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 @@ -26,7 +26,6 @@ import gr.thmmy.mthmmy.model.TopicSummary; * specified {@link ForumFragment.ForumFragmentInteractionListener}. */ class ForumAdapter extends ExpandableRecyclerAdapter { - private final Context context; private final LayoutInflater layoutInflater; private final List categories; @@ -34,7 +33,6 @@ class ForumAdapter extends ExpandableRecyclerAdapter categories, BaseFragment.FragmentInteractionListener listener) { super(categories); - this.context = context; this.categories = categories; mListener = (ForumFragment.ForumFragmentInteractionListener) listener; layoutInflater = LayoutInflater.from(context); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java index 8bcecc25..75919925 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java @@ -2,24 +2,21 @@ package gr.thmmy.mthmmy.activities.main.forum; import android.os.AsyncTask; import android.os.Bundle; +import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.RelativeLayout; -import android.widget.Toast; import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter; -import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; -import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -29,11 +26,12 @@ import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.model.Board; import gr.thmmy.mthmmy.model.Category; import gr.thmmy.mthmmy.session.SessionManager; +import gr.thmmy.mthmmy.utils.CustomRecyclerView; +import gr.thmmy.mthmmy.utils.ParseTask; +import gr.thmmy.mthmmy.utils.exceptions.ParseException; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; - import okhttp3.HttpUrl; import okhttp3.Request; -import okhttp3.Response; import timber.log.Timber; /** @@ -44,12 +42,12 @@ import timber.log.Timber; * Use the {@link ForumFragment#newInstance} factory method to * create an instance of this fragment. */ -public class ForumFragment extends BaseFragment -{ +public class ForumFragment extends BaseFragment { private static final String TAG = "ForumFragment"; // Fragment initialization parameters, e.g. ARG_SECTION_NUMBER private MaterialProgressBar progressBar; + private SwipeRefreshLayout swipeRefreshLayout; private ForumAdapter forumAdapter; private List categories; @@ -57,11 +55,13 @@ public class ForumFragment extends BaseFragment private ForumTask forumTask; // Required empty public constructor - public ForumFragment() {} + public ForumFragment() { + } /** * Use ONLY this factory method to create a new instance of * this fragment using the provided parameters. + * * @return A new instance of fragment Forum. */ public static ForumFragment newInstance(int sectionNumber) { @@ -82,9 +82,8 @@ public class ForumFragment extends BaseFragment @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - if (categories.isEmpty()) - { - forumTask =new ForumTask(); + if (categories.isEmpty()) { + forumTask = new ForumTask(); forumTask.execute(); } @@ -104,11 +103,10 @@ public class ForumFragment extends BaseFragment forumAdapter.setExpandCollapseListener(new ExpandableRecyclerAdapter.ExpandCollapseListener() { @Override public void onParentExpanded(int parentPosition) { - if(BaseActivity.getSessionManager().isLoggedIn()) - { - if(forumTask.getStatus()== AsyncTask.Status.RUNNING) + if (BaseActivity.getSessionManager().isLoggedIn()) { + if (forumTask.getStatus() == AsyncTask.Status.RUNNING) forumTask.cancel(true); - forumTask =new ForumTask(); + forumTask = new ForumTask(); forumTask.setUrl(categories.get(parentPosition).getCategoryURL()); forumTask.execute(); } @@ -116,18 +114,17 @@ public class ForumFragment extends BaseFragment @Override public void onParentCollapsed(int parentPosition) { - if(BaseActivity.getSessionManager().isLoggedIn()) - { - if(forumTask.getStatus()== AsyncTask.Status.RUNNING) + if (BaseActivity.getSessionManager().isLoggedIn()) { + if (forumTask.getStatus() == AsyncTask.Status.RUNNING) forumTask.cancel(true); - forumTask =new ForumTask(); + forumTask = new ForumTask(); forumTask.setUrl(categories.get(parentPosition).getCategoryURL()); forumTask.execute(); } } }); - RecyclerView recyclerView = (RecyclerView) rootView.findViewById(R.id.list); + CustomRecyclerView recyclerView = (CustomRecyclerView) rootView.findViewById(R.id.list); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(rootView.findViewById(R.id.list).getContext()); recyclerView.setLayoutManager(linearLayoutManager); DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), @@ -135,6 +132,20 @@ public class ForumFragment extends BaseFragment recyclerView.addItemDecoration(dividerItemDecoration); recyclerView.setAdapter(forumAdapter); + swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swiperefresh); + swipeRefreshLayout.setOnRefreshListener( + new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + if (forumTask != null && forumTask.getStatus() != AsyncTask.Status.RUNNING) { + forumTask = new ForumTask(); + forumTask.execute(SessionManager.indexUrl.toString()); + } + } + + } + ); + } return rootView; } @@ -142,19 +153,18 @@ public class ForumFragment extends BaseFragment @Override public void onDestroy() { super.onDestroy(); - if(forumTask!=null&&forumTask.getStatus()!= AsyncTask.Status.RUNNING) + if (forumTask != null && forumTask.getStatus() != AsyncTask.Status.RUNNING) forumTask.cancel(true); } - public interface ForumFragmentInteractionListener extends FragmentInteractionListener{ + public interface ForumFragmentInteractionListener extends FragmentInteractionListener { void onForumFragmentInteraction(Board board); } //---------------------------------------ASYNC TASK----------------------------------- - private class ForumTask extends AsyncTask { + private class ForumTask extends ParseTask { private HttpUrl forumUrl = SessionManager.forumUrl; //may change upon collapse/expand - private Document document; private final List fetchedCategories; @@ -166,69 +176,52 @@ public class ForumFragment extends BaseFragment progressBar.setVisibility(ProgressBar.VISIBLE); } - protected Integer doInBackground(Void... voids) { - Request request = new Request.Builder() + @Override + protected Request prepareRequest(String... params) { + return new Request.Builder() .url(forumUrl) .build(); - try { - Response response = client.newCall(request).execute(); - document = Jsoup.parse(response.body().string()); - parse(document); - categories.clear(); - categories.addAll(fetchedCategories); - fetchedCategories.clear(); - return 0; - } catch (IOException e) { - Timber.d("Network Error", e); - return 1; - } catch (Exception e) { - Timber.d("Exception", e); - return 2; - } - } - protected void onPostExecute(Integer result) { - - if (result == 0) - forumAdapter.notifyParentDataSetChanged(false); - else if (result == 1) - Toast.makeText(getActivity(), "Network error", Toast.LENGTH_SHORT).show(); - - progressBar.setVisibility(ProgressBar.INVISIBLE); - } - - private void parse(Document document) - { + @Override + public void parse(Document document) throws ParseException { Elements categoryBlocks = document.select(".tborder:not([style])>table[cellpadding=5]"); if (categoryBlocks.size() != 0) { - for(Element categoryBlock: categoryBlocks) - { + for (Element categoryBlock : categoryBlocks) { Element categoryElement = categoryBlock.select("td[colspan=2]>[name]").first(); String categoryUrl = categoryElement.attr("href"); Category category = new Category(categoryElement.text(), categoryUrl); - if(categoryUrl.contains("sa=collapse")|| !BaseActivity.getSessionManager().isLoggedIn()) - { + if (categoryUrl.contains("sa=collapse") || !BaseActivity.getSessionManager().isLoggedIn()) { category.setExpanded(true); Elements boardsElements = categoryBlock.select("b [name]"); - for(Element boardElement: boardsElements) { + for (Element boardElement : boardsElements) { Board board = new Board(boardElement.attr("href"), boardElement.text(), null, null, null, null); category.getBoards().add(board); } - } - else + } else category.setExpanded(false); fetchedCategories.add(category); } - } - else - Timber.e("Parsing failed!"); + categories.clear(); + categories.addAll(fetchedCategories); + fetchedCategories.clear(); + } else + throw new ParseException("Parsing failed"); + } + + @Override + protected void postParsing(ParseTask.ResultCode result) { + if (result == ResultCode.SUCCESS) + forumAdapter.notifyParentDataSetChanged(false); + + progressBar.setVisibility(ProgressBar.INVISIBLE); + swipeRefreshLayout.setRefreshing(false); } - public void setUrl(String string) + public void setUrl(String string) //TODO delete and simplify e.g. in prepareRequest possible? { forumUrl = HttpUrl.parse(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 47f4678d..86e3a2d2 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 @@ -10,13 +10,10 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.RelativeLayout; -import android.widget.Toast; -import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; @@ -27,14 +24,12 @@ import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.utils.CustomRecyclerView; +import gr.thmmy.mthmmy.utils.ParseTask; import gr.thmmy.mthmmy.utils.exceptions.ParseException; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; - -import okhttp3.HttpUrl; -import okhttp3.Request; -import okhttp3.Response; import timber.log.Timber; + /** * A {@link BaseFragment} subclass. * Activities that contain this fragment must implement the @@ -56,11 +51,13 @@ public class RecentFragment extends BaseFragment { private RecentTask recentTask; // Required empty public constructor - public RecentFragment() {} + public RecentFragment() { + } /** * Use ONLY this factory method to create a new instance of * this fragment using the provided parameters. + * * @return A new instance of fragment Recent. */ public static RecentFragment newInstance(int sectionNumber) { @@ -81,10 +78,9 @@ public class RecentFragment extends BaseFragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - if (topicSummaries.isEmpty()) - { - recentTask =new RecentTask(); - recentTask.execute(); + if (topicSummaries.isEmpty()) { + recentTask = new RecentTask(); + recentTask.execute(SessionManager.indexUrl.toString()); } Timber.d("onActivityCreated"); @@ -117,7 +113,7 @@ public class RecentFragment extends BaseFragment { public void onRefresh() { if (recentTask != null && recentTask.getStatus() != AsyncTask.Status.RUNNING) { recentTask = new RecentTask(); - recentTask.execute(); + recentTask.execute(SessionManager.indexUrl.toString()); } } @@ -131,7 +127,7 @@ public class RecentFragment extends BaseFragment { @Override public void onDestroy() { super.onDestroy(); - if(recentTask!=null&&recentTask.getStatus()!= AsyncTask.Status.RUNNING) + if (recentTask != null && recentTask.getStatus() != AsyncTask.Status.RUNNING) recentTask.cancel(true); } @@ -141,55 +137,16 @@ public class RecentFragment extends BaseFragment { } //---------------------------------------ASYNC TASK----------------------------------- - - private class RecentTask extends AsyncTask { - private final HttpUrl thmmyUrl = SessionManager.indexUrl; - private Document document; - + private class RecentTask extends ParseTask { protected void onPreExecute() { - progressBar.setVisibility(ProgressBar.VISIBLE); } - protected Integer doInBackground(Void... voids) { - Request request = new Request.Builder() - .url(thmmyUrl) - .build(); - try { - Response response = client.newCall(request).execute(); - document = Jsoup.parse(response.body().string()); - parse(document); - return 0; - } catch (ParseException e) { - Timber.e("ParseException", e); - return 1; - } catch (IOException e) { - Timber.i("Network Error", e); - return 2; - } catch (Exception e) { - Timber.e("Exception", e); - return 3; - } - - } - - - protected void onPostExecute(Integer result) { - - if (result == 0) - recentAdapter.notifyDataSetChanged(); - else if (result == 2) - Toast.makeText(getActivity(), "Network error", Toast.LENGTH_SHORT).show(); //Fixme, sometimes activity isn't ready - - progressBar.setVisibility(ProgressBar.INVISIBLE); - swipeRefreshLayout.setRefreshing(false); - } - - private void parse(Document document) throws ParseException { + @Override + public void parse(Document document) throws ParseException { Elements recent = document.select("#block8 :first-child div"); if (!recent.isEmpty()) { topicSummaries.clear(); - for (int i = 0; i < recent.size(); i += 3) { String link = recent.get(i).child(0).attr("href"); String title = recent.get(i).child(0).attr("title"); @@ -212,11 +169,18 @@ public class RecentFragment extends BaseFragment { topicSummaries.add(new TopicSummary(link, title, lastUser, dateTime)); } - return; } throw new ParseException("Parsing failed"); } - } + @Override + protected void postParsing(ParseTask.ResultCode result) { + if (result == ResultCode.SUCCESS) + recentAdapter.notifyDataSetChanged(); + + progressBar.setVisibility(ProgressBar.INVISIBLE); + swipeRefreshLayout.setRefreshing(false); + } + } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadAdapter.java new file mode 100644 index 00000000..73adc1e4 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadAdapter.java @@ -0,0 +1,147 @@ +package gr.thmmy.mthmmy.activities.main.unread; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import java.util.List; + +import gr.thmmy.mthmmy.R; +import gr.thmmy.mthmmy.base.BaseFragment; +import gr.thmmy.mthmmy.model.TopicSummary; + +class UnreadAdapter extends RecyclerView.Adapter { + private final Context context; + private final List unreadList; + private final UnreadFragment.UnreadFragmentInteractionListener mListener; + private final MarkReadInteractionListener markReadListener; + + private final int VIEW_TYPE_ITEM = 0; + private final int VIEW_TYPE_NADA = 1; + private final int VIEW_TYPE_MARK_READ = 2; + + UnreadAdapter(Context context, @NonNull List topicSummaryList, + BaseFragment.FragmentInteractionListener listener, + MarkReadInteractionListener markReadInteractionListener) { + this.context = context; + this.unreadList = topicSummaryList; + mListener = (UnreadFragment.UnreadFragmentInteractionListener) listener; + markReadListener = markReadInteractionListener; + } + + @Override + public int getItemViewType(int position) { + if (unreadList.get(position).getDateTimeModified() == null) return VIEW_TYPE_MARK_READ; + return unreadList.get(position).getTopicUrl() == null ? VIEW_TYPE_NADA : VIEW_TYPE_ITEM; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == VIEW_TYPE_ITEM) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.fragment_unread_row, parent, false); + return new ViewHolder(view); + } else if (viewType == VIEW_TYPE_NADA) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.fragment_unread_empty_row, parent, false); + return new EmptyViewHolder(view); + } else if (viewType == VIEW_TYPE_MARK_READ) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.fragment_unread_mark_read_row, parent, false); + return new MarkReadViewHolder(view); + } + return null; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { + if (holder instanceof UnreadAdapter.EmptyViewHolder) { + final UnreadAdapter.EmptyViewHolder emptyViewHolder = (UnreadAdapter.EmptyViewHolder) holder; + emptyViewHolder.text.setText(unreadList.get(position).getDateTimeModified()); + } else if (holder instanceof UnreadAdapter.ViewHolder) { + final UnreadAdapter.ViewHolder viewHolder = (UnreadAdapter.ViewHolder) holder; + + viewHolder.mTitleView.setText(unreadList.get(position).getSubject()); + viewHolder.mDateTimeView.setText(unreadList.get(position).getDateTimeModified()); + viewHolder.mUserView.setText(context.getString(R.string.byUser, unreadList.get(position).getLastUser())); + + viewHolder.topic = unreadList.get(position); + + viewHolder.mView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (null != mListener) { + // Notify the active callbacks interface (the activity, if the + // fragment is attached to one) that an item has been selected. + mListener.onUnreadFragmentInteraction(viewHolder.topic); //? + } + } + }); + } else if (holder instanceof UnreadAdapter.MarkReadViewHolder) { + final UnreadAdapter.MarkReadViewHolder markReadViewHolder = (UnreadAdapter.MarkReadViewHolder) holder; + markReadViewHolder.text.setText(unreadList.get(position).getSubject()); + markReadViewHolder.topic = unreadList.get(position); + + markReadViewHolder.mView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (null != mListener) { + // Notify the active callbacks interface (the activity, if the + // fragment is attached to one) that an item has been selected. + markReadListener.onMarkReadInteraction(unreadList.get(position).getTopicUrl()); + } + } + }); + } + } + + @Override + public int getItemCount() { + return unreadList.size(); + } + + private static class ViewHolder extends RecyclerView.ViewHolder { + final View mView; + final TextView mTitleView; + final TextView mUserView; + final TextView mDateTimeView; + public TopicSummary topic; + + ViewHolder(View view) { + super(view); + mView = view; + mTitleView = (TextView) view.findViewById(R.id.title); + mUserView = (TextView) view.findViewById(R.id.lastUser); + mDateTimeView = (TextView) view.findViewById(R.id.dateTime); + } + } + + private static class EmptyViewHolder extends RecyclerView.ViewHolder { + final TextView text; + + EmptyViewHolder(View view) { + super(view); + text = (TextView) view.findViewById(R.id.text); + } + } + + private static class MarkReadViewHolder extends RecyclerView.ViewHolder { + final View mView; + final TextView text; + public TopicSummary topic; + + MarkReadViewHolder(View view) { + super(view); + mView = view; + text = (TextView) view.findViewById(R.id.mark_read); + } + } + + public interface MarkReadInteractionListener { + void onMarkReadInteraction(String markReadLinkUrl); + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java new file mode 100644 index 00000000..d1febe97 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java @@ -0,0 +1,251 @@ +package gr.thmmy.mthmmy.activities.main.unread; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.DividerItemDecoration; +import android.support.v7.widget.LinearLayoutManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.Toast; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import gr.thmmy.mthmmy.R; +import gr.thmmy.mthmmy.base.BaseFragment; +import gr.thmmy.mthmmy.model.TopicSummary; +import gr.thmmy.mthmmy.session.SessionManager; +import gr.thmmy.mthmmy.utils.CustomRecyclerView; +import gr.thmmy.mthmmy.utils.ParseTask; +import gr.thmmy.mthmmy.utils.exceptions.ParseException; +import me.zhanghai.android.materialprogressbar.MaterialProgressBar; +import okhttp3.Request; +import timber.log.Timber; + +/** + * A {@link BaseFragment} subclass. + * Activities that contain this fragment must implement the + * {@link UnreadFragment.UnreadFragmentInteractionListener} interface + * to handle interaction events. + * Use the {@link UnreadFragment#newInstance} factory method to + * create an instance of this fragment. + */ + +public class UnreadFragment extends BaseFragment { + private static final String TAG = "UnreadFragment"; + // Fragment initialization parameters, e.g. ARG_SECTION_NUMBER + + private MaterialProgressBar progressBar; + private SwipeRefreshLayout swipeRefreshLayout; + private UnreadAdapter unreadAdapter; + + private List topicSummaries; + + private UnreadTask unreadTask; + private MarkReadTask markReadTask; + + // Required empty public constructor + public UnreadFragment() { + } + + /** + * Use ONLY this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @return A new instance of fragment Unread. + */ + public static UnreadFragment newInstance(int sectionNumber) { + UnreadFragment fragment = new UnreadFragment(); + Bundle args = new Bundle(); + args.putString(ARG_TAG, TAG); + args.putInt(ARG_SECTION_NUMBER, sectionNumber); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + topicSummaries = new ArrayList<>(); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (topicSummaries.isEmpty()) { + unreadTask = new UnreadTask(); + unreadTask.execute(SessionManager.unreadUrl.toString()); + } + markReadTask = new MarkReadTask(); + Timber.d("onActivityCreated"); + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + final View rootView = inflater.inflate(R.layout.fragment_unread, container, false); + + // Set the adapter + if (rootView instanceof RelativeLayout) { + progressBar = (MaterialProgressBar) rootView.findViewById(R.id.progressBar); + unreadAdapter = new UnreadAdapter(getActivity(), topicSummaries, + fragmentInteractionListener, new UnreadAdapter.MarkReadInteractionListener() { + @Override + public void onMarkReadInteraction(String markReadLinkUrl) { + if (markReadTask != null && markReadTask.getStatus() != AsyncTask.Status.RUNNING) { + markReadTask = new MarkReadTask(); + markReadTask.execute(markReadLinkUrl); + } + } + }); + + CustomRecyclerView recyclerView = (CustomRecyclerView) rootView.findViewById(R.id.list); + LinearLayoutManager linearLayoutManager = new LinearLayoutManager(rootView.findViewById(R.id.list).getContext()); + recyclerView.setLayoutManager(linearLayoutManager); + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), + linearLayoutManager.getOrientation()); + recyclerView.addItemDecoration(dividerItemDecoration); + recyclerView.setAdapter(unreadAdapter); + + swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swiperefresh); + swipeRefreshLayout.setOnRefreshListener( + new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + if (unreadTask != null && unreadTask.getStatus() != AsyncTask.Status.RUNNING) { + unreadTask = new UnreadTask(); + unreadTask.execute(SessionManager.unreadUrl.toString()); + } + } + + } + ); + } + + return rootView; + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (unreadTask != null && unreadTask.getStatus() != AsyncTask.Status.RUNNING) + unreadTask.cancel(true); + if (markReadTask != null && markReadTask.getStatus() != AsyncTask.Status.RUNNING) + markReadTask.cancel(true); + } + + public interface UnreadFragmentInteractionListener extends FragmentInteractionListener { + void onUnreadFragmentInteraction(TopicSummary topicSummary); + } + + //---------------------------------------ASYNC TASK----------------------------------- + private class UnreadTask extends ParseTask { + protected void onPreExecute() { + progressBar.setVisibility(ProgressBar.VISIBLE); + } + + @Override + public void parse(Document document) throws ParseException { + Elements unread = document.select("table.bordercolor[cellspacing=1] tr:not(.titlebg)"); + if (!unread.isEmpty()) { + topicSummaries.clear(); + for (Element row : unread) { + Elements information = row.select("td"); + String link = information.last().select("a").first().attr("href"); + String title = information.get(2).select("a").first().text(); + + Element lastUserAndDate = information.get(6); + String lastUser = lastUserAndDate.select("a").text(); + String dateTime = lastUserAndDate.select("span").html(); + //dateTime = dateTime.replace("
    ", ""); + dateTime = dateTime.substring(0, dateTime.indexOf("
    ")); + dateTime = dateTime.replace("", ""); + dateTime = dateTime.replace("", ""); + + topicSummaries.add(new TopicSummary(link, title, lastUser, dateTime)); + } + Element markRead = document.select("table:not(.bordercolor):not([width])").select("a") + .first(); + if (markRead != null) + topicSummaries.add(new TopicSummary(markRead.attr("href"), markRead.text(), null, + null)); + } else { + topicSummaries.clear(); + String message = document.select("table.bordercolor[cellspacing=1]").first().text(); + if (message.contains("No messages")) { //It's english + message = "No unread posts!"; + } else { //It's greek + message = "Δεν υπάρχουν μη διαβασμένα μηνύματα!"; + } + topicSummaries.add(new TopicSummary(null, null, null, message)); + } + } + + @Override + protected void postParsing(ParseTask.ResultCode result) { + if (result == ResultCode.SUCCESS) + unreadAdapter.notifyDataSetChanged(); + + progressBar.setVisibility(ProgressBar.INVISIBLE); + swipeRefreshLayout.setRefreshing(false); + } + } + + private class MarkReadTask extends AsyncTask { + private static final int SUCCESS = 0; + private static final int NETWORK_ERROR = 1; + private static final int OTHER_ERROR = 2; + + @Override + protected void onPreExecute() { + progressBar.setVisibility(ProgressBar.VISIBLE); + } + + @Override + protected Integer doInBackground(String... strings) { + Request request = new Request.Builder() + .url(strings[0]) + .build(); + try { + client.newCall(request).execute(); + return SUCCESS; + } catch (IOException e) { + Timber.i(e, "IO Exception"); + return NETWORK_ERROR; + } catch (Exception e) { + Timber.e(e, "Exception"); + return OTHER_ERROR; + } + } + + @Override + protected void onPostExecute(Integer result) { + progressBar.setVisibility(ProgressBar.GONE); + + if (result == NETWORK_ERROR) { + Toast.makeText(getContext() + , "Task was unsuccessful!\n Please check your internet conneciton.", + Toast.LENGTH_LONG).show(); + } else if (result == OTHER_ERROR) { + Toast.makeText(getContext() + , "Fatal error!\n Task aborted...", Toast.LENGTH_LONG).show(); + } else { + if (unreadTask != null && unreadTask.getStatus() != AsyncTask.Status.RUNNING) { + unreadTask = new UnreadTask(); + unreadTask.execute(SessionManager.unreadUrl.toString()); + } + } + } + } +} 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 08495dd9..f21a21eb 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,7 +19,6 @@ 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; @@ -50,7 +49,6 @@ 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 okhttp3.Request; import okhttp3.Response; import timber.log.Timber; @@ -276,11 +274,12 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment } catch (SSLHandshakeException e) { Timber.w("Certificate problem (please switch to unsafe connection)."); } catch (Exception e) { - Timber.e("ERROR", e); + Timber.e(e, "Exception"); } return false; } + //TODO: better parse error handling (ParseException etc.) protected void onPostExecute(Boolean result) { if (!result) { //Parse failed! //TODO report as ParseException? Timber.d("Parse failed!"); 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 5c3f8d40..8f39f6c9 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,7 +26,6 @@ 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 okhttp3.Request; import okhttp3.Response; import timber.log.Timber; @@ -167,7 +166,7 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap } catch (SSLHandshakeException e) { Timber.w("Certificate problem (please switch to unsafe connection)."); } catch (Exception e) { - Timber.e("ERROR", e); + Timber.e(e, "Exception"); } return false; } @@ -185,6 +184,7 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap isLoadingMore = false; } + //TODO: better parse error handling (ParseException etc.) private boolean parseLatestPosts(Document latestPostsPage) { Elements latestPostsRows = latestPostsPage. select("td:has(table:Contains(Show Posts)):not([style]) > table"); 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 f573f3db..50c8c521 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 @@ -41,7 +41,6 @@ import javax.net.ssl.SSLHandshakeException; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.base.BaseActivity; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; - import okhttp3.Request; import okhttp3.Response; import timber.log.Timber; @@ -141,11 +140,12 @@ public class StatsFragment extends Fragment { } catch (SSLHandshakeException e) { Timber.w("Certificate problem (please switch to unsafe connection)."); } catch (Exception e) { - Timber.e("ERROR", e); + Timber.e(e, "Exception"); } return false; } + //TODO: better parse error handling (ParseException etc.) @Override protected void onPostExecute(Boolean result) { if (!result) { //Parse failed! 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 70644bf4..fc3d4dd7 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 @@ -7,7 +7,6 @@ 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; @@ -25,7 +24,6 @@ import java.util.Objects; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.utils.ParseHelpers; - import timber.log.Timber; 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 index 7c42b8cd..3fe99911 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java @@ -11,12 +11,54 @@ import java.util.Map; import java.util.regex.Matcher; import okhttp3.Response; +import timber.log.Timber; +/** + * This is a utility class containing a collection of static methods to help with topic replying. + */ class Posting { + /** + * {@link REPLY_STATUS} enum defines the different possible outcomes of a topic reply request. + */ enum REPLY_STATUS { - SUCCESSFUL, NO_SUBJECT, EMPTY_BODY, NEW_REPLY_WHILE_POSTING, NOT_FOUND, SESSION_ENDED, OTHER_ERROR + /** + * The request was successful + */ + SUCCESSFUL, + /** + * Request was lacking a subject + */ + NO_SUBJECT, + /** + * Request had empty body + */ + EMPTY_BODY, + /** + * There were new topic replies while making the request + */ + NEW_REPLY_WHILE_POSTING, + /** + * Error 404, page was not found + */ + NOT_FOUND, + /** + * User session ended while posting the reply + */ + SESSION_ENDED, + /** + * Other undefined of unidentified error + */ + OTHER_ERROR } + /** + * This method can be used to check whether a topic post request was successful or not and if + * not maybe get the reason why. + * + * @param response {@link okhttp3.Response} of the request + * @return a {@link REPLY_STATUS} that describes the response status + * @throws IOException method relies to {@link org.jsoup.Jsoup#parse(String)} + */ 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; @@ -26,8 +68,8 @@ class Posting { 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]); + Timber.d(String.valueOf(i)); + Timber.d(errors[i]); } for (String error : errors) { if (error.contains("Your session timed out while posting") || @@ -45,10 +87,19 @@ class Posting { return REPLY_STATUS.SUCCESSFUL; } + /** + * This is a fucked up method.. Just don't waste your time here unless you have suicidal + * tendencies. + * + * @param html the html string to be transformed to BBcode + * @return the BBcode string + */ static String htmlToBBcode(String html) { + Log.d("Cancer", html); Map bbMap = new HashMap<>(); Map smileysMap1 = new HashMap<>(); Map smileysMap2 = new HashMap<>(); + smileysMap1.put("Smiley", ":)"); smileysMap1.put("Wink", ";)"); smileysMap1.put("Cheesy", ":D"); @@ -171,64 +222,66 @@ class Posting { //html stuff on the beginning bbMap.put("\n ", ""); //quotes and code headers - bbMap.put("\n\\s+?
    \n (.+?)\n
    ", ""); - bbMap.put("\n\\s+?
    \n (.+?)\n
    ", ""); - bbMap.put("\n\\s+?
    \n (.+?)\n
    ", ""); - bbMap.put("
    ", "\n"); + bbMap.put("\\s*?
    (.*?(\\n))*?.*?<\\/div>", ""); + bbMap.put("\\s*?
    (.*?(\\n))+?.*?<\\/div>", ""); + bbMap.put("\\s*?
    (.*?(\\n))+?.*?<\\/div>", ""); + bbMap.put("
    ", "\\\n"); + //Non-breaking space + bbMap.put(" ", " "); //bold - bbMap.put("\n\\s+?(.+?)", "\\[b\\]$1\\[/b\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/b>", "\\[b\\]$1\\[/b\\]"); //italics - bbMap.put("\n\\s+?(.+?)", "\\[i\\]$1\\[/i\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/i>", "\\[i\\]$1\\[/i\\]"); //underline - bbMap.put("\n\\s+?(.+?)", "\\[u\\]$1\\[/u\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/span>", "\\[u\\]$1\\[/u\\]"); //deleted - bbMap.put("\n\\s+?(.+?)", "\\[s\\]$1\\[/s\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/del>", "\\[s\\]$1\\[/s\\]"); //text color - bbMap.put("\n\\s+?(.+?)", "\\[color=$1\\]$2\\[/color\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/span>", "\\[color=$1\\]$2\\[/color\\]"); //glow - bbMap.put("\n\\s+?(.+?)", "\\[glow=$1,2,300\\]$2\\[/glow\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/span>", "\\[glow=$1,2,300\\]$2\\[/glow\\]"); //shadow - bbMap.put("\n\\s+?(.+?)", "\\[shadow=$1,$2\\]$3\\[/shadow\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/span>", "\\[shadow=$1,$2\\]$3\\[/shadow\\]"); //running text - bbMap.put("\\s+?\n (.+?)\n ", "\\[move\\]$1\\[/move\\]"); + bbMap.put("\\s*?\n ([\\S\\s]+?)\n <\\/marquee>", "\\[move\\]$1\\[/move\\]"); //alignment - bbMap.put("\n\\s+?
    \n (.+?)\n
    ", "\\[center\\]$1\\[/center\\]"); - bbMap.put("\n\\s+?
    \n (.+?)\n
    ", "\\[$1\\]$2\\[/$1\\]"); + bbMap.put("\\s*?
    \n ([\\S\\s]+?)\n <\\/div>", "\\[center\\]$1\\[/center\\]"); + bbMap.put("\\s*?
    \n ([\\S\\s]+?)\n <\\/div>", "\\[$1\\]$2\\[/$1\\]"); //preformated - bbMap.put("\n\\s+?
    (.+?)
    ", "\\[pre\\]$1\\[/pre\\]"); + bbMap.put("\\s*?
    ([\\S\\s]+?)<\\/pre>", "\\[pre\\]$1\\[/pre\\]");
             //horizontal rule
    -        bbMap.put("\n\\s+?
    ", "\\[hr\\]"); + bbMap.put("\\s*?
    ", "\\[hr\\]"); //resize - bbMap.put("\n\\s+?(.+?)", "\\[size=$1\\]$3\\[/size\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/span>", "\\[size=$1\\]$3\\[/size\\]"); //font - bbMap.put("\n\\s+?(.+?)", "\\[font=$1\\]$2\\[/font\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/span>", "\\[font=$1\\]$2\\[/font\\]"); //lists - bbMap.put("\\s+
  • (.+?)
  • ", "\\[li\\]$1\\[/li\\]"); - bbMap.put("\n\\s+
      ([\\S\\s]+?)\n\\s+
    ", + bbMap.put("\\s+
  • (.+?)<\\/li>", "\\[li\\]$1\\[/li\\]"); + bbMap.put("\n\\s+
      ([\\S\\s]+?)\n\\s+<\\/ul>", "\\[list\\]\n$1\n\\[/list\\]"); //latex code - bbMap.put("\n\\s+?", "\\[tex\\]$1\\[/tex\\]"); + bbMap.put("\\s*?", "\\[tex\\]$1\\[/tex\\]"); //code - bbMap.put("\n\\s+?
      \n (.+?)\n
      ", "\\[code\\]$1\\[/code\\]"); + bbMap.put("\\s*?
      ((.*?(\\n))+?.*?)<\\/div>", "\\[code\\]$1\\[/code\\]"); //teletype - bbMap.put("\n\\s+?(.+?)", "\\[tt\\]$1\\[/tt\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/tt>", "\\[tt\\]$1\\[/tt\\]"); //superscript/subscript - bbMap.put("\n\\s+?(.+?)", "\\[sub\\]$1\\[/sub\\]"); - bbMap.put("\n\\s+?(.+?)", "\\[sup\\]$1\\[/sup\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/sub>", "\\[sub\\]$1\\[/sub\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/sup>", "\\[sup\\]$1\\[/sup\\]"); //tables - bbMap.put("\\s+?([\\S\\s]+?)", "\\[td\\]$1\\[/td\\]"); - bbMap.put("([\\S\\s]+?)\n ", "\\[tr\\]$1\\[/tr\\]"); - bbMap.put("\n\\s+?\n \n ([\\S\\s]+?)\n \n
      " + bbMap.put("\\s*?([\\S\\s]+?)<\\/td>", "\\[td\\]$1\\[/td\\]"); + bbMap.put("([\\S\\s]+?)\n <\\/tr>", "\\[tr\\]$1\\[/tr\\]"); + bbMap.put("\\s*?\n \n ([\\S\\s]+?)\n <\\/tbody>\n <\\/table>" , "\\[table\\]$2\\[/table\\]"); //videos - bbMap.put("\n\\s+?
      \n", + bbMap.put("\\s*?
      .+?watch\\?v=(.+?)\"((.|\\n)*?)/div>\n", "[youtube]https://www.youtube.com/watch?v=$1[/youtube]"); //ftp - bbMap.put("([\\S\\s]+?)", "\\[fpt=ftp:$1\\]$2\\[/ftp\\]"); + bbMap.put("([\\S\\s]+?)<\\/a>", "\\[fpt=ftp:$1\\]$2\\[/ftp\\]"); //mailto - bbMap.put("\n\\s+?([\\S\\s]+?)", "\\[email\\]$2\\[/email\\]"); + bbMap.put("\\s*?([\\S\\s]+?)<\\/a>", "\\[email\\]$2\\[/email\\]"); //links - bbMap.put("\n\\s+?([\\S\\s]+?)", "\\[url=$1\\]$2\\[/url\\]"); + bbMap.put("\\s*?([\\S\\s]+?)", "\\[url=$1\\]$2\\[/url\\]"); //smileys for (Map.Entry entry : smileysMap1.entrySet()) { bbMap.put("\n \""", "\\[img height=$2\\]$1\\[/img\\]"); html = html.replaceAll("\\s+", "\\[img\\]$1\\[/img\\]"); + Log.d("Cancer", html); 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 050fea14..5c170204 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 @@ -5,15 +5,16 @@ import android.content.Intent; import android.graphics.Rect; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AlertDialog; -import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.text.Html; import android.text.SpannableStringBuilder; +import android.text.TextUtils; import android.text.method.LinkMovementMethod; import android.text.method.ScrollingMovementMethod; import android.text.style.ClickableSpan; @@ -47,9 +48,9 @@ import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.model.Bookmark; import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.ThmmyPage; +import gr.thmmy.mthmmy.utils.CustomLinearLayoutManager; import gr.thmmy.mthmmy.utils.ParseHelpers; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; - import okhttp3.MultipartBody; import okhttp3.Request; import okhttp3.RequestBody; @@ -114,12 +115,12 @@ public class TopicActivity extends BaseActivity { topicViewers = new SpannableStringBuilder("Loading..."); //Other variables private MaterialProgressBar progressBar; - TextView toolbarTitle; + private TextView toolbarTitle; private static String base_url = ""; private String topicTitle; private String parsedTitle; private RecyclerView recyclerView; - String loadedPageUrl = ""; + private String loadedPageUrl = ""; private boolean reloadingPage = false; @@ -134,7 +135,7 @@ public class TopicActivity extends BaseActivity { ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory( Uri.parse(topicPageUrl)); if (!target.is(ThmmyPage.PageCategory.TOPIC)) { - Timber.e("Bundle came with a non topic url!\nUrl:\n" + topicPageUrl); + Timber.e("Bundle came with a non topic url!\nUrl: %s", topicPageUrl); Toast.makeText(this, "An error has occurred\n Aborting.", Toast.LENGTH_SHORT).show(); finish(); } @@ -144,7 +145,11 @@ public class TopicActivity extends BaseActivity { //Initializes graphics toolbar = (Toolbar) findViewById(R.id.toolbar); toolbarTitle = (TextView) toolbar.findViewById(R.id.toolbar_title); + toolbarTitle.setSingleLine(true); + toolbarTitle.setEllipsize(TextUtils.TruncateAt.MARQUEE); + toolbarTitle.setMarqueeRepeatLimit(-1); toolbarTitle.setText(topicTitle); + toolbarTitle.setSelected(true); setSupportActionBar(toolbar); if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); @@ -163,7 +168,17 @@ public class TopicActivity extends BaseActivity { recyclerView = (RecyclerView) findViewById(R.id.topic_recycler_view); recyclerView.setHasFixedSize(true); - LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext()); + recyclerView.setOnTouchListener( + new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return topicTask != null && topicTask.getStatus() == AsyncTask.Status.RUNNING; + } + } + ); + //LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext()); + CustomLinearLayoutManager layoutManager = new CustomLinearLayoutManager( + getApplicationContext(), loadedPageUrl); recyclerView.setLayoutManager(layoutManager); topicAdapter = new TopicAdapter(this, postsList, topicTask); recyclerView.setAdapter(topicAdapter); @@ -182,6 +197,7 @@ public class TopicActivity extends BaseActivity { topicAdapter.prepareForReply(new ReplyTask(), topicTitle, loadedPageUrl); replyFAB.hide(); bottomNavBar.setVisibility(View.GONE); + recyclerView.scrollToPosition(postsList.size() - 1); } } }); @@ -210,6 +226,7 @@ public class TopicActivity extends BaseActivity { // 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)); + super.onCreateOptionsMenu(menu); return true; } @@ -253,8 +270,9 @@ public class TopicActivity extends BaseActivity { @Override protected void onResume() { - drawer.setSelection(-1); super.onResume(); + refreshTopicBookmark(); + drawer.setSelection(-1); } @Override @@ -517,10 +535,10 @@ public class TopicActivity extends BaseActivity { parse(document); return SUCCESS; } catch (IOException e) { - Timber.i("IO Exception", e); + Timber.i(e, "IO Exception"); return NETWORK_ERROR; } catch (Exception e) { - Timber.e("Exception", e); + Timber.e(e, "Exception"); return OTHER_ERROR; } } @@ -545,8 +563,12 @@ public class TopicActivity extends BaseActivity { } progressBar.setVisibility(ProgressBar.INVISIBLE); - topicAdapter.customNotifyDataSetChanged(new TopicTask()); - if (replyPageUrl == null) replyFAB.hide(); + recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug + if (replyPageUrl == null) { + replyFAB.hide(); + topicAdapter.customNotifyDataSetChanged(new TopicTask(), false); + } else topicAdapter.customNotifyDataSetChanged(new TopicTask(), true); + if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true); //Set current page @@ -560,7 +582,10 @@ public class TopicActivity extends BaseActivity { break; case SAME_PAGE: progressBar.setVisibility(ProgressBar.INVISIBLE); - topicAdapter.customNotifyDataSetChanged(new TopicTask()); + if (replyPageUrl == null) { + replyFAB.hide(); + topicAdapter.customNotifyDataSetChanged(new TopicTask(), false); + } else topicAdapter.customNotifyDataSetChanged(new TopicTask(), true); if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true); paginationEnabled(true); Toast.makeText(TopicActivity.this, "That's the same page.", Toast.LENGTH_SHORT).show(); @@ -568,7 +593,7 @@ public class TopicActivity extends BaseActivity { break; default: //Parse failed - should never happen - Timber.d("Parse failed!"); //TODO report ParseException? + Timber.d("Parse failed!"); //TODO report ParseException!!! Toast.makeText(getBaseContext(), "Fatal Error", Toast.LENGTH_SHORT).show(); finish(); break; @@ -624,6 +649,9 @@ public class TopicActivity extends BaseActivity { } postsList.clear(); + int oldSize = postsList.size(); + topicAdapter.notifyItemRangeRemoved(0, oldSize); + recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug postsList.addAll(TopicParser.parseTopic(topic, language)); } @@ -661,7 +689,13 @@ public class TopicActivity extends BaseActivity { } private SpannableStringBuilder getSpannableFromHtml(String html) { - CharSequence sequence = Html.fromHtml(html); + CharSequence sequence; + if (Build.VERSION.SDK_INT >= 24) { + sequence = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY); + } else { + //noinspection deprecation + sequence = Html.fromHtml(html); + } SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence); URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class); for (URLSpan span : urls) { @@ -683,7 +717,7 @@ public class TopicActivity extends BaseActivity { @Override protected Boolean doInBackground(String... message) { Document document; - String numReplies, seqnum, sc, subject, topic; + String numReplies, seqnum, sc, topic; Request request = new Request.Builder() .url(replyPageUrl + ";wap2") @@ -695,13 +729,12 @@ public class TopicActivity extends BaseActivity { 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); + Timber.e(e, "Post failed."); return false; } catch (Selector.SelectorParseException e) { - Timber.e("Post failed.", e); + Timber.e(e, "Post failed."); return false; } @@ -735,7 +768,7 @@ public class TopicActivity extends BaseActivity { return true; } } catch (IOException e) { - Timber.e("Post failed.", e); + Timber.e(e, "Post failed."); return false; } } 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 f1a0cca2..aa17b311 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 @@ -18,11 +18,10 @@ 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; import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; import android.webkit.WebResourceRequest; import android.webkit.WebView; import android.webkit.WebViewClient; @@ -54,7 +53,6 @@ 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 timber.log.Timber; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -76,36 +74,32 @@ class TopicAdapter extends RecyclerView.Adapter { private static int THUMBNAIL_SIZE; private final Context context; private String topicTitle; - private ArrayList toQuoteList = new ArrayList<>(); + private final ArrayList toQuoteList = new ArrayList<>(); private final List postsList; /** * Used to hold the state of visibility and other attributes for views that are animated or - * otherwise changed. Used in combination with {@link #isPostDateAndNumberVisibile}, - * {@link #isUserExtraInfoVisibile} and {@link #isQuoteButtonChecked}. + * otherwise changed. Used in combination with {@link #isUserExtraInfoVisibile} and + * {@link #isQuoteButtonChecked}. */ private final ArrayList viewProperties = new ArrayList<>(); - /** - * Index of state indicator in the boolean array. If true post is expanded and post's date and - * number are visible. - */ - private static final int isPostDateAndNumberVisibile = 0; /** * Index of state indicator in the boolean array. If true user's extra info are expanded and * visible. */ - private static final int isUserExtraInfoVisibile = 1; + private static final int isUserExtraInfoVisibile = 0; /** * Index of state indicator in the boolean array. If true quote button for this post is checked. */ - private static final int isQuoteButtonChecked = 2; + private static final int isQuoteButtonChecked = 1; private TopicActivity.TopicTask topicTask; private TopicActivity.ReplyTask replyTask; private final int VIEW_TYPE_POST = 0; private final int VIEW_TYPE_QUICK_REPLY = 1; - private String[] replyDataHolder = new String[2]; - private int replySubject = 0, replyText = 1; + private final String[] replyDataHolder = new String[2]; + private final int replySubject = 0, replyText = 1; private String loadedPageUrl = ""; + private boolean canReply = false; /** * @param context the context of the {@link RecyclerView} @@ -144,6 +138,23 @@ class TopicAdapter extends RecyclerView.Adapter { 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); + + final EditText quickReplyText = (EditText) view.findViewById(R.id.quick_reply_text); + quickReplyText.setFocusableInTouchMode(true); + quickReplyText.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + quickReplyText.post(new Runnable() { + @Override + public void run() { + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(quickReplyText, InputMethodManager.SHOW_IMPLICIT); + } + }); + } + }); + quickReplyText.requestFocus(); + //Default post subject replyDataHolder[replySubject] = "Re: " + topicTitle; //Build quotes @@ -161,7 +172,8 @@ class TopicAdapter extends RecyclerView.Adapter { @SuppressLint({"SetJavaScriptEnabled", "SetTextI18n"}) @Override - public void onBindViewHolder(final RecyclerView.ViewHolder currentHolder, final int position) { + 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; @@ -169,6 +181,7 @@ class TopicAdapter extends RecyclerView.Adapter { //Post's WebView parameters holder.post.setClickable(true); holder.post.setWebViewClient(new LinkLauncher()); + holder.post.setLayerType(View.LAYER_TYPE_SOFTWARE, null); //Avoids errors about layout having 0 width/height holder.thumbnail.setMinimumWidth(1); @@ -325,9 +338,23 @@ class TopicAdapter extends RecyclerView.Adapter { if (!currentPost.isDeleted() && viewProperties.get(position)[isUserExtraInfoVisibile]) { holder.userExtraInfo.setVisibility(View.VISIBLE); holder.userExtraInfo.setAlpha(1.0f); + + 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 { holder.userExtraInfo.setVisibility(View.GONE); holder.userExtraInfo.setAlpha(0.0f); + + 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); } if (!currentPost.isDeleted()) { //Sets graphics behavior @@ -355,7 +382,9 @@ class TopicAdapter extends RecyclerView.Adapter { boolean[] tmp = viewProperties.get(holder.getAdapterPosition()); tmp[isUserExtraInfoVisibile] = !tmp[isUserExtraInfoVisibile]; viewProperties.set(holder.getAdapterPosition(), tmp); - TopicAnimations.animateUserExtraInfoVisibility(holder.userExtraInfo); + TopicAnimations.animateUserExtraInfoVisibility(holder.username, + holder.subject, Color.parseColor("#FFFFFF"), + Color.parseColor("#757575"), holder.userExtraInfo); } }); //Clicking the expanded part of a header (the extra info) makes it collapse @@ -366,7 +395,9 @@ class TopicAdapter extends RecyclerView.Adapter { tmp[isUserExtraInfoVisibile] = false; viewProperties.set(holder.getAdapterPosition(), tmp); - TopicAnimations.animateUserExtraInfoVisibility(v); + TopicAnimations.animateUserExtraInfoVisibility(holder.username, + holder.subject, Color.parseColor("#FFFFFF"), + Color.parseColor("#757575"), v); } }); } else { @@ -374,34 +405,10 @@ class TopicAdapter extends RecyclerView.Adapter { 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()) + if (!BaseActivity.getSessionManager().isLoggedIn() || !canReply) { holder.quoteToggle.setVisibility(View.GONE); - else { + } else { if (viewProperties.get(position)[isQuoteButtonChecked]) holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked); else @@ -427,23 +434,6 @@ class TopicAdapter extends RecyclerView.Adapter { } }); } - //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)); } else if (currentHolder instanceof QuickReplyViewHolder) { final QuickReplyViewHolder holder = (QuickReplyViewHolder) currentHolder; @@ -482,14 +472,16 @@ class TopicAdapter extends RecyclerView.Adapter { } } - void customNotifyDataSetChanged(TopicActivity.TopicTask topicTask) { + void customNotifyDataSetChanged(TopicActivity.TopicTask topicTask, boolean canReply) { this.topicTask = topicTask; + this.canReply = canReply; viewProperties.clear(); for (int i = 0; i < postsList.size(); ++i) { //Initializes properties, array's values will be false by default viewProperties.add(new boolean[3]); } - notifyDataSetChanged(); + notifyItemRangeInserted(0, postsList.size()); + //notifyDataSetChanged(); } @Override @@ -503,7 +495,7 @@ class TopicAdapter extends RecyclerView.Adapter { private class PostViewHolder extends RecyclerView.ViewHolder { final CardView cardView; final LinearLayout cardChildLinear; - final FrameLayout postDateAndNumberExp; + final FrameLayout postDateAndNumber; final TextView postDate, postNum, username, subject; final ImageView thumbnail; final public WebView post; @@ -521,7 +513,7 @@ class TopicAdapter extends RecyclerView.Adapter { //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); + postDateAndNumber = (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); @@ -570,71 +562,6 @@ class TopicAdapter extends RecyclerView.Adapter { } } - /** - * This class is a gesture detector for WebViews. It handles post's clicks, long clicks and - * touch and drag. - */ - private class CustomTouchListener implements View.OnTouchListener { - //Long press handling - private float downCoordinateX; - private float downCoordinateY; - private final float SCROLL_THRESHOLD = 7; - final private WebView post; - final private CardView cardView; - - //Other variables - final static int FINGER_RELEASED = 0; - final static int FINGER_TOUCHED = 1; - final static int FINGER_DRAGGING = 2; - final static int FINGER_UNDEFINED = 3; - - private int fingerState = FINGER_RELEASED; - - CustomTouchListener(WebView pPost, CardView pCard) { - post = pPost; - cardView = pCard; - } - - @Override - public boolean onTouch(View view, MotionEvent motionEvent) { - switch (motionEvent.getAction()) { - case MotionEvent.ACTION_DOWN: - //Logs XY - downCoordinateX = motionEvent.getX(); - downCoordinateY = motionEvent.getY(); - - if (fingerState == FINGER_RELEASED) - fingerState = FINGER_TOUCHED; - else - fingerState = FINGER_UNDEFINED; - break; - case MotionEvent.ACTION_UP: - if (fingerState != FINGER_DRAGGING) { - //Doesn't expand the card if this was a link - WebView.HitTestResult htResult = post.getHitTestResult(); - if (htResult.getExtra() != null - && htResult.getExtra() != null) { - fingerState = FINGER_RELEASED; - return false; - } - cardView.performClick(); - } - fingerState = FINGER_RELEASED; - break; - case MotionEvent.ACTION_MOVE: - //Cancels long click if finger moved too much - if (((Math.abs(downCoordinateX - motionEvent.getX()) > SCROLL_THRESHOLD || - Math.abs(downCoordinateY - motionEvent.getY()) > SCROLL_THRESHOLD))) { - fingerState = FINGER_DRAGGING; - } else fingerState = FINGER_UNDEFINED; - break; - default: - fingerState = FINGER_UNDEFINED; - } - return false; - } - } - /** * This class is used to handle link clicks in WebViews. When link url is one that the app can * handle internally, it does. Otherwise user is prompt to open the link in a browser. diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAnimations.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAnimations.java index c0206a1d..ae4516ef 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAnimations.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAnimations.java @@ -7,80 +7,16 @@ import android.view.View; import android.widget.TextView; class TopicAnimations { - //--------------------------POST'S INFO VISIBILITY CHANGE ANIMATION METHOD-------------------------- - - /** - * Method that animates view's visibility changes for post's extra info - */ - static void animatePostExtraInfoVisibility(final View dateAndPostNum, TextView username, - TextView subject, int expandedColor, int collapsedColor) { - //If the view is gone fade it in - if (dateAndPostNum.getVisibility() == View.GONE) { - //Show full username - username.setMaxLines(Integer.MAX_VALUE); //As in the android sourcecode - username.setEllipsize(null); - - //Show full subject - subject.setTextColor(expandedColor); - subject.setMaxLines(Integer.MAX_VALUE); //As in the android sourcecode - subject.setEllipsize(null); - - - dateAndPostNum.clearAnimation(); - // Prepare the View for the animation - dateAndPostNum.setVisibility(View.VISIBLE); - dateAndPostNum.setAlpha(0.0f); - - // Start the animation - dateAndPostNum.animate() - .translationY(0) - .alpha(1.0f) - .setDuration(300) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - dateAndPostNum.setVisibility(View.VISIBLE); - } - }); - } - //If the view is visible fade it out - else { - username.setMaxLines(1); //As in the android sourcecode - username.setEllipsize(TextUtils.TruncateAt.END); - - subject.setTextColor(collapsedColor); - subject.setMaxLines(1); //As in the android sourcecode - subject.setEllipsize(TextUtils.TruncateAt.END); - - dateAndPostNum.clearAnimation(); - - // Start the animation - dateAndPostNum.animate() - .translationY(dateAndPostNum.getHeight()) - .alpha(0.0f) - .setDuration(300) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - dateAndPostNum.setVisibility(View.GONE); - } - }); - } - } -//------------------------POST'S INFO VISIBILITY CHANGE ANIMATION METHOD END------------------------ - //--------------------------USER'S INFO VISIBILITY CHANGE ANIMATION METHOD-------------------------- /** * Method that animates view's visibility changes for user's extra info */ - static void animateUserExtraInfoVisibility(final View userExtra) { - + static void animateUserExtraInfoVisibility(TextView username, TextView subject, + int expandedColor, int collapsedColor, + final View userExtra) { //If the view is gone fade it in if (userExtra.getVisibility() == View.GONE) { - userExtra.clearAnimation(); userExtra.setVisibility(View.VISIBLE); userExtra.setAlpha(0.0f); @@ -96,6 +32,15 @@ class TopicAnimations { userExtra.setVisibility(View.VISIBLE); } }); + + //Show full username + username.setMaxLines(Integer.MAX_VALUE); //As in the android sourcecode + username.setEllipsize(null); + + //Show full subject + subject.setTextColor(expandedColor); + subject.setMaxLines(Integer.MAX_VALUE); //As in the android sourcecode + subject.setEllipsize(null); } //If the view is visible fade it out else { @@ -112,6 +57,13 @@ class TopicAnimations { userExtra.setVisibility(View.GONE); } }); + + username.setMaxLines(1); //As in the android sourcecode + username.setEllipsize(TextUtils.TruncateAt.END); + + subject.setTextColor(collapsedColor); + subject.setMaxLines(1); //As in the android sourcecode + subject.setEllipsize(TextUtils.TruncateAt.END); } } //------------------------POST'S INFO VISIBILITY CHANGE ANIMATION METHOD END------------------------ 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 3ce9c24d..81b1e47f 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,7 +1,6 @@ package gr.thmmy.mthmmy.activities.topic; import android.graphics.Color; -import android.util.Log; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -259,7 +258,7 @@ class TopicParser { try { attachedUrl = new URL(tmpAttachedFileUrlAndName.attr("href")); } catch (MalformedURLException e) { - Timber.e("Attached file malformed url", e); + Timber.e(e, "Attached file malformed url"); break; } String attachedFileName = tmpAttachedFileUrlAndName.text().substring(1); 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 3be339d3..f844573d 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java @@ -65,7 +65,6 @@ public abstract class BaseActivity extends AppCompatActivity { private static final String BOOKMARKED_BOARDS_KEY = "bookmarkedBoardsKey"; protected Bookmark thisPageBookmark; private MenuItem thisPageBookmarkMenuButton; - private ImageButton thisPageBookmarkImageButton; private SharedPreferences bookmarksFile; private ArrayList topicsBookmarked; private ArrayList boardsBookmarked; @@ -76,6 +75,8 @@ public abstract class BaseActivity extends AppCompatActivity { protected Toolbar toolbar; protected Drawer drawer; + private MainActivity mainActivity; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -86,25 +87,23 @@ public abstract class BaseActivity extends AppCompatActivity { if (sessionManager == null) sessionManager = BaseApplication.getInstance().getSessionManager(); - - if (sessionManager.isLoggedIn()) { - if (bookmarked == null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - bookmarked = getResources().getDrawable(R.drawable.ic_bookmark_true, null); - } else //noinspection deprecation - bookmarked = getResources().getDrawable(R.drawable.ic_bookmark_true); - } - if (notBookmarked == null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - notBookmarked = getResources().getDrawable(R.drawable.ic_bookmark_false, null); - } else //noinspection deprecation - notBookmarked = getResources().getDrawable(R.drawable.ic_bookmark_false); - } - if (topicsBookmarked == null || boardsBookmarked == null) { - bookmarksFile = getSharedPreferences(BOOKMARKS_SHARED_PREFS, Context.MODE_PRIVATE); - loadSavedBookmarks(); - } + if (bookmarked == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + bookmarked = getResources().getDrawable(R.drawable.ic_bookmark_true, null); + } else //noinspection deprecation + bookmarked = getResources().getDrawable(R.drawable.ic_bookmark_true); } + if (notBookmarked == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + notBookmarked = getResources().getDrawable(R.drawable.ic_bookmark_false, null); + } else //noinspection deprecation + notBookmarked = getResources().getDrawable(R.drawable.ic_bookmark_false); + } + if (topicsBookmarked == null || boardsBookmarked == null) { + bookmarksFile = getSharedPreferences(BOOKMARKS_SHARED_PREFS, Context.MODE_PRIVATE); + loadSavedBookmarks(); + } + } @Override @@ -221,14 +220,6 @@ public abstract class BaseActivity extends AppCompatActivity { .withName(R.string.downloads) .withIcon(downloadsIcon) .withSelectedIcon(downloadsIconSelected); - bookmarksItem = new PrimaryDrawerItem() - .withTextColor(primaryColor) - .withSelectedColor(selectedPrimaryColor) - .withSelectedTextColor(selectedSecondaryColor) - .withIdentifier(BOOKMARKS_ID) - .withName(R.string.bookmark) - .withIcon(bookmarksIcon) - .withSelectedIcon(bookmarksIconSelected); } else loginLogoutItem = new PrimaryDrawerItem() .withTextColor(primaryColor) @@ -237,6 +228,15 @@ public abstract class BaseActivity extends AppCompatActivity { .withIcon(loginIcon) .withSelectable(false); + bookmarksItem = new PrimaryDrawerItem() + .withTextColor(primaryColor) + .withSelectedColor(selectedPrimaryColor) + .withSelectedTextColor(selectedSecondaryColor) + .withIdentifier(BOOKMARKS_ID) + .withName(R.string.bookmark) + .withIcon(bookmarksIcon) + .withSelectedIcon(bookmarksIconSelected); + aboutItem = new PrimaryDrawerItem() .withTextColor(primaryColor) .withSelectedColor(selectedPrimaryColor) @@ -333,11 +333,13 @@ public abstract class BaseActivity extends AppCompatActivity { if (sessionManager.isLoggedIn()) drawerBuilder.addDrawerItems(homeItem, bookmarksItem, downloadsItem, loginLogoutItem, aboutItem); else - drawerBuilder.addDrawerItems(homeItem, loginLogoutItem, aboutItem); + drawerBuilder.addDrawerItems(homeItem, bookmarksItem, loginLogoutItem, aboutItem); drawer = drawerBuilder.build(); - drawer.getActionBarDrawerToggle().setDrawerIndicatorEnabled(false); + if (!(BaseActivity.this instanceof MainActivity)) + drawer.getActionBarDrawerToggle().setDrawerIndicatorEnabled(false); + drawer.setOnDrawerNavigationListener(new Drawer.OnDrawerNavigationListener() { @Override public boolean onNavigationClickListener(View clickedView) { @@ -352,7 +354,6 @@ public abstract class BaseActivity extends AppCompatActivity { if (!sessionManager.isLoggedIn()) //When logged out or if user is guest { drawer.removeItem(DOWNLOADS_ID); - drawer.removeItem(BOOKMARKS_ID); loginLogoutItem.withName(R.string.login).withIcon(loginIcon); //Swap logout with login profileDrawerItem.withName(sessionManager.getUsername()).withIcon(new IconicsDrawable(this) .icon(FontAwesome.Icon.faw_user) @@ -395,6 +396,8 @@ public abstract class BaseActivity extends AppCompatActivity { protected void onPostExecute(Integer result) { Toast.makeText(getBaseContext(), "Logged out successfully!", Toast.LENGTH_LONG).show(); updateDrawer(); + if (mainActivity != null) + mainActivity.updateTabs(); progressDialog.dismiss(); } } @@ -419,6 +422,18 @@ public abstract class BaseActivity extends AppCompatActivity { } } + protected void refreshTopicBookmark() { + if (thisPageBookmarkMenuButton == null) { + return; + } + loadSavedBookmarks(); + if (thisPageBookmark.matchExists(topicsBookmarked)) { + thisPageBookmarkMenuButton.setIcon(bookmarked); + } else { + thisPageBookmarkMenuButton.setIcon(notBookmarked); + } + } + protected void topicMenuBookmarkClick() { if (thisPageBookmark.matchExists(topicsBookmarked)) { thisPageBookmarkMenuButton.setIcon(notBookmarked); @@ -432,7 +447,6 @@ public abstract class BaseActivity extends AppCompatActivity { } protected void setBoardBookmark(final ImageButton thisPageBookmarkImageButton) { - this.thisPageBookmarkImageButton = thisPageBookmarkImageButton; if (thisPageBookmark.matchExists(boardsBookmarked)) { thisPageBookmarkImageButton.setImageDrawable(bookmarked); } else { @@ -453,6 +467,17 @@ public abstract class BaseActivity extends AppCompatActivity { }); } + protected void refreshBoardBookmark(final ImageButton thisPageBookmarkImageButton) { + if (thisPageBookmarkImageButton == null) + return; + loadSavedBookmarks(); + if (thisPageBookmark.matchExists(boardsBookmarked)) { + thisPageBookmarkImageButton.setImageDrawable(bookmarked); + } else { + thisPageBookmarkImageButton.setImageDrawable(notBookmarked); + } + } + private void loadSavedBookmarks() { String tmpString = bookmarksFile.getString(BOOKMARKED_TOPICS_KEY, null); if (tmpString != null) @@ -565,4 +590,9 @@ public abstract class BaseActivity extends AppCompatActivity { } + //----------------------------------MISC---------------------- + protected void setMainActivity(MainActivity mainActivity) { + this.mainActivity = mainActivity; + } + } 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 3030cce0..24c84558 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java @@ -35,8 +35,6 @@ import okhttp3.Response; import timber.log.Timber; public class BaseApplication extends Application { - - private static BaseApplication baseApplication; //BaseApplication singleton // Client & SessionManager 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 20bd8da2..0f3487d9 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java @@ -6,7 +6,6 @@ import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import okhttp3.OkHttpClient; -import timber.log.Timber; public abstract class BaseFragment extends Fragment { protected static final String ARG_SECTION_NUMBER = "SectionNumber"; @@ -26,31 +25,6 @@ public abstract class BaseFragment extends Fragment { if (client == null) client = BaseApplication.getInstance().getClient(); //must check every time - e.g. // becomes null when app restarts after crash - Timber.d("onCreate"); - } - - @Override - public void onStart() { - super.onStart(); - Timber.d("onStart"); - } - - @Override - public void onResume() { - super.onResume(); - Timber.d("onResume"); - } - - @Override - public void onPause() { - super.onPause(); - Timber.d("onPause"); - } - - @Override - public void onStop() { - super.onStop(); - Timber.d("onStop"); } @Override @@ -76,6 +50,5 @@ 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/ThmmyPage.java b/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java index 9e935f69..3ca324ed 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java +++ b/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java @@ -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; - Timber.v("Unknown thmmy link found, link: " + uriString); + Timber.v("Unknown thmmy link found, link: %s" , uriString); return PageCategory.UNKNOWN_THMMY; } return PageCategory.NOT_THMMY; 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 d93f4272..2ef4fdd4 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,6 @@ import android.webkit.MimeTypeMap; import java.io.File; import gr.thmmy.mthmmy.R; - import timber.log.Timber; import static gr.thmmy.mthmmy.services.DownloadService.ACTION_DOWNLOAD; @@ -50,7 +49,7 @@ public class Receiver extends BroadcastReceiver { .setContentText(text) .setTicker(ticker) .setAutoCancel(true) - .setSmallIcon(R.mipmap.ic_launcher); + .setSmallIcon(R.drawable.ic_file_download); if (state.equals(STARTED)) builder.setOngoing(true); 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 52883ef4..4e22c7b1 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/DownloadService.java +++ b/app/src/main/java/gr/thmmy/mthmmy/services/DownloadService.java @@ -13,7 +13,6 @@ import java.io.IOException; import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.receiver.Receiver; - import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; @@ -142,7 +141,7 @@ public class DownloadService extends IntentService { fileName = file.getName(); - Timber.v("Started saving file " + fileName); + Timber.v("Started saving file %s" , fileName); sendNotification(downloadId, STARTED, fileName); sink = Okio.buffer(Okio.sink(file)); @@ -154,11 +153,11 @@ public class DownloadService extends IntentService { Timber.e("Response not a binary file!"); } catch (FileNotFoundException e) { Timber.i("Download failed..."); - Timber.e("FileNotFound", e); + Timber.e(e, "FileNotFound"); sendNotification(downloadId, FAILED, fileName); } catch (IOException e) { Timber.i("Download failed..."); - Timber.e("IOException", e); + Timber.e(e, "IOException"); sendNotification(downloadId, FAILED, fileName); } finally { if (sink != null) { 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 1455fb27..4c2c60cc 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java +++ b/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java @@ -36,6 +36,7 @@ public class SessionManager { 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"); private static final HttpUrl loginUrl = HttpUrl.parse("https://www.thmmy.gr/smf/index.php?action=login2"); + public static final HttpUrl unreadUrl = HttpUrl.parse("https://www.thmmy.gr/smf/index.php?action=unread;all;start=0;theme=4"); private static final String guestName = "Guest"; //Response Codes @@ -154,10 +155,10 @@ public class SessionManager { Timber.i("Login InterruptedIOException"); //users cancels LoginTask return CANCELLED; } catch (IOException e) { - Timber.w("Login IOException", e); + Timber.w(e ,"Login IOException"); return CONNECTION_ERROR; } catch (Exception e) { - Timber.w("Login Exception (other)", e); + Timber.e(e, "Login Exception (other)"); return EXCEPTION; } } @@ -223,7 +224,7 @@ public class SessionManager { Timber.w("Logout IOException", e); return CONNECTION_ERROR; } catch (Exception e) { - Timber.w("Logout Exception", e); + Timber.e(e, "Logout Exception"); return EXCEPTION; } finally { //All data should always be cleared from device regardless the result of logout @@ -317,7 +318,7 @@ public class SessionManager { if (userName != null && !userName.isEmpty()) return userName; - Timber.e("ParseException", new ParseException("Parsing failed(username extraction)")); + Timber.e(new ParseException("Parsing failed(username extraction)"),"ParseException"); return "User"; //return a default username } @@ -341,7 +342,7 @@ public class SessionManager { if (link != null && !link.isEmpty()) return link; } - Timber.e("ParseException", new ParseException("Parsing failed(logoutLink extraction)")); + Timber.e(new ParseException("Parsing failed(logoutLink extraction)"),"ParseException"); 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/CrashReportingTree.java b/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java index 2aeca9a2..a3407254 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java @@ -4,31 +4,36 @@ import android.util.Log; import com.google.firebase.crash.FirebaseCrash; -import gr.thmmy.mthmmy.utils.exceptions.UnknownException; -import timber.log.Timber; +import timber.log.Timber.DebugTree; -public class CrashReportingTree extends Timber.Tree { +public class CrashReportingTree extends DebugTree { + @Override protected void log(int priority, String tag, String message, Throwable t) { if (priority == Log.VERBOSE || priority == Log.DEBUG) { return; } - String level="A"; + char level; if (priority == Log.INFO) - level = "I"; + level = 'I'; else if (priority == Log.WARN) - level = "W"; + level = 'W'; else if(priority == Log.ERROR) - level = "E"; + level = 'E'; + else + level = 'A'; FirebaseCrash.log(level + "/" + tag + ": " + message); - if(t==null) - t = new UnknownException("UnknownException"); + if(priority == Log.ERROR) + { + if (t!=null) + FirebaseCrash.report(t); + else + FirebaseCrash.report(new Exception(message)); + } - if ((priority == Log.ERROR)) - FirebaseCrash.report(t); } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java b/app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java new file mode 100644 index 00000000..cc03225c --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java @@ -0,0 +1,38 @@ +package gr.thmmy.mthmmy.utils; + +import android.content.Context; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.Log; + +import timber.log.Timber; + +public class CustomLinearLayoutManager extends LinearLayoutManager { + private String pageUrl; + + public CustomLinearLayoutManager(Context context, String pageUrl) { + super(context); + this.pageUrl = pageUrl; + } + + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + try { + super.onLayoutChildren(recycler, state); + } catch (IndexOutOfBoundsException e) { + Timber.wtf(e, "Inconsistency detected: topic_requested = \"" + pageUrl + "\""); + Log.d("CustomLinearLayoutMan", "Inconsistency detected: topic_requested = \"" + + pageUrl + "\"", e); + } + } + + /** + * Disable predictive animations. There is a bug in RecyclerView which causes views that + * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the + * adapter size has decreased since the ViewHolder was recycled. + */ + @Override + public boolean supportsPredictiveItemAnimations() { + return false; + } +} \ No newline at end of file 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 75a9fa72..a2e80cca 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/ParseHelpers.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ParseHelpers.java @@ -156,7 +156,7 @@ public class ParseHelpers { + "" + "" + + "src=\"YouTube_light_color_icon.png\">" + "" + " { + protected enum ResultCode { + SUCCESS, PARSING_ERROR, NETWORK_ERROR, OTHER_ERROR + } + + protected abstract void parse (Document document) throws ParseException; + protected abstract void postParsing (ParseTask.ResultCode result); //ResultCode.NETWORK_ERROR is handled automatically + + protected Request prepareRequest(String... params) { + return new Request.Builder() + .url(params[0]) + .build(); + } + + @Override + protected ResultCode doInBackground(String... params) { + Request request = prepareRequest(params); + try { + Response response = BaseApplication.getInstance().getClient().newCall(request).execute(); + Document document = Jsoup.parse(response.body().string()); + parse(document); + return ResultCode.SUCCESS; + } catch (ParseException e) { + Timber.tag(this.getClass().getSimpleName()); + Timber.e(e, "Parsing Error"); + return ResultCode.PARSING_ERROR; + } catch (IOException e) { + Timber.tag(this.getClass().getSimpleName()); + Timber.i(e, "Network Error"); + return ResultCode.NETWORK_ERROR; + } catch (Exception e) { + Timber.tag(this.getClass().getSimpleName()); + Timber.e(e, "Other Error"); + return ResultCode.OTHER_ERROR; + } + } + + @Override + protected void onPostExecute(ParseTask.ResultCode result) { + if (result == ResultCode.NETWORK_ERROR) + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show(); + postParsing(result); + } +} + diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java index 58ba1a62..f49fe28d 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java @@ -41,8 +41,7 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior 40)) && child.getVisibility() != View.VISIBLE) { + } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) { child.show(); } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java index 0f3564c6..834cbe34 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java @@ -35,8 +35,7 @@ public class ScrollAwareLinearBehavior extends CoordinatorLayout.Behavior if ((dyConsumed > 0 || (!target.canScrollVertically(-1) && dyConsumed == 0 && dyUnconsumed < 40)) && bottomNavBar.getVisibility() == View.VISIBLE) { hide(bottomNavBar); - } else if ((dyConsumed < 0 || (!target.canScrollVertically(1) && dyConsumed == 0 - && dyUnconsumed > 40)) && bottomNavBar.getVisibility() != View.VISIBLE) { + } else if (dyConsumed < 0 && bottomNavBar.getVisibility() != View.VISIBLE) { show(bottomNavBar); } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/exceptions/UnknownException.java b/app/src/main/java/gr/thmmy/mthmmy/utils/exceptions/UnknownException.java deleted file mode 100644 index 81c890ad..00000000 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/exceptions/UnknownException.java +++ /dev/null @@ -1,14 +0,0 @@ -package gr.thmmy.mthmmy.utils.exceptions; - -/** - * UnknownException is thrown upon an error (see Report.java in release), when no other specific - * exception is set, to report to FireBase. - */ -public class UnknownException extends Exception { - public UnknownException() { - } - - public UnknownException(String message) { - super(message); - } -} diff --git a/app/src/main/res/drawable/ic_file_download.xml b/app/src/main/res/drawable/ic_file_download.xml new file mode 100644 index 00000000..80734766 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_download.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_reply.xml b/app/src/main/res/drawable/ic_reply.xml new file mode 100644 index 00000000..1fc7f4f1 --- /dev/null +++ b/app/src/main/res/drawable/ic_reply.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout-sw600dp/activity_main.xml b/app/src/main/res/layout-sw600dp/activity_main.xml new file mode 100644 index 00000000..62b3d010 --- /dev/null +++ b/app/src/main/res/layout-sw600dp/activity_main.xml @@ -0,0 +1,34 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout-v21/activity_topic_post_row.xml b/app/src/main/res/layout-v21/activity_topic_post_row.xml index 8b745727..2362427d 100644 --- a/app/src/main/res/layout-v21/activity_topic_post_row.xml +++ b/app/src/main/res/layout-v21/activity_topic_post_row.xml @@ -1,48 +1,14 @@ - - - - - - - - - + + + android:transitionName="user_thumbnail" /> + android:textStyle="bold" /> + android:text="@string/post_subject" /> + android:src="@drawable/ic_format_quote_unchecked" /> + android:visibility="gone" /> + android:visibility="gone" /> - + android:visibility="gone" /> + android:visibility="gone" /> + android:visibility="gone" /> + android:visibility="gone" /> + + + + + + + + + android:layout_marginTop="5dp" + android:background="@color/divider" /> + android:text="@string/post" /> + android:visibility="gone" /> - + android:paddingRight="16dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 2e724601..797665a2 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -30,7 +30,7 @@ android:id="@+id/scrollview" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginTop="64dp" + app:layout_behavior="@string/appbar_scrolling_view_behavior" android:background="@color/background"> + android:scrollbars="none" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index b032b9b9..d3c0fe27 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -13,17 +13,8 @@ android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="@dimen/appbar_padding_top" android:theme="@style/ToolbarTheme"> - - - @@ -44,12 +37,12 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="top|start" - android:layout_marginTop="64dp" android:background="@color/background" - android:paddingBottom="54dp" + android:paddingTop="4dp" android:clipToPadding="false" android:scrollbars="none" - tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity"> + tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + app:srcCompat="@drawable/ic_reply"/> diff --git a/app/src/main/res/layout/activity_topic_post_row.xml b/app/src/main/res/layout/activity_topic_post_row.xml index 677a58db..99eeefd6 100644 --- a/app/src/main/res/layout/activity_topic_post_row.xml +++ b/app/src/main/res/layout/activity_topic_post_row.xml @@ -1,6 +1,5 @@ - - - - - - - - - + android:src="@drawable/ic_default_user_thumbnail" /> + android:textStyle="bold" /> + android:text="@string/post_subject" /> + android:src="@drawable/ic_format_quote_unchecked" /> + android:visibility="gone" /> + android:visibility="gone" /> - + android:visibility="gone" /> + android:visibility="gone" /> + android:visibility="gone" /> + android:visibility="gone" /> + + + + + + + + + android:layout_marginTop="5dp" + android:background="@color/divider" /> + android:text="@string/post" /> + android:visibility="gone" /> - + android:paddingRight="16dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_forum.xml b/app/src/main/res/layout/fragment_forum.xml index 49bf6061..04c4ec4e 100644 --- a/app/src/main/res/layout/fragment_forum.xml +++ b/app/src/main/res/layout/fragment_forum.xml @@ -6,17 +6,22 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + android:layout_height="match_parent"> + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_unread_empty_row.xml b/app/src/main/res/layout/fragment_unread_empty_row.xml new file mode 100644 index 00000000..d5394258 --- /dev/null +++ b/app/src/main/res/layout/fragment_unread_empty_row.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_unread_mark_read_row.xml b/app/src/main/res/layout/fragment_unread_mark_read_row.xml new file mode 100644 index 00000000..ffc6e8e8 --- /dev/null +++ b/app/src/main/res/layout/fragment_unread_mark_read_row.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_unread_row.xml b/app/src/main/res/layout/fragment_unread_row.xml new file mode 100644 index 00000000..8f256f2e --- /dev/null +++ b/app/src/main/res/layout/fragment_unread_row.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/topic_menu.xml b/app/src/main/res/menu/topic_menu.xml index 16c60e0a..516a4a48 100644 --- a/app/src/main/res/menu/topic_menu.xml +++ b/app/src/main/res/menu/topic_menu.xml @@ -5,12 +5,12 @@ android:id="@+id/menu_bookmark" android:icon="@drawable/ic_bookmark_false" app:showAsAction="ifRoom" - android:title="Bookmark"> + android:title="@string/bookmark"> + app:showAsAction="ifRoom" + android:title="@string/info"> \ No newline at end of file diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml index d70052bd..2efd3b5a 100644 --- a/app/src/main/res/values-w820dp/dimens.xml +++ b/app/src/main/res/values-w820dp/dimens.xml @@ -9,4 +9,5 @@ 144dp 64dp 40dp + 24sp diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index a779507a..914c6e3d 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -7,4 +7,5 @@ 36dp 16dp 10dp + 24sp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d95e5e63..5c6dc980 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -9,6 +9,7 @@ About Home Bookmarks + Info thmmy.gr @@ -63,8 +64,8 @@ Remove - Your bookmarked boards: - Your bookmarked topics: + Boards + Topics Libraries diff --git a/build.gradle b/build.gradle index a40a0e30..d786c62a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ buildscript { maven { url "https://jitpack.io" } } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' - classpath 'com.google.gms:google-services:3.0.0' + classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.google.gms:google-services:3.1.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/doc/forum_post.txt b/doc/forum_post.txt deleted file mode 100644 index 1957af12..00000000 --- a/doc/forum_post.txt +++ /dev/null @@ -1,35 +0,0 @@ -[center][size=25pt][b]Introduction[/b][/size][/center] - -Από τη συζήτηση [url=https://www.thmmy.gr/smf/index.php?topic=67629.0]εδώ[/url], ξεκίνησε ένα project με στόχο τη δημιουργία εφαρμογής για Android κινητά που θα συγκεντρώνει και θα κάνει πιο εύκολη τη πρόσβαση σε μερικές από τις βασικές σελίδες και υπηρεσίες που αφορούν τη σχολή και χρησιμοποιούμε καθημερινά. - -Μετά από 2+ μήνες δουλειάς και πάνω από 9000 γραμμές κώδικα (.java, .xml και άλλα), σήμερα (16/1/2017) ανεβάσαμε για πρώτη φορά την εφαρμογή (σε closed alpha phase) στο Google Play Store! - -Προς το παρόν η εφαρμογή υποστηρίζει κάποιες από τις βασικές λειτουργίες του forum. Σταδιακά θα ενσωματώνονται όλο και περισσότερες λειτουργίες, για παράδειγμα ενός συστήματος ειδοποιήσεων για νέες ανακοινώσεις του ethmmy και της σελίδας της γραμματείας, instant chat κ.ά., ανάλογα πάντα με τις ιδέες, τη διάθεση και την ενέργεια όσων θα συμμετέχουν. - -Αυτή τη στιγμή με το project ασχολούμαστε εγώ και ο L, ενώ έχουν βοηθήσει ο iason1907 και ο [url=https://www.thmmy.gr/smf/index.php?topic=67565.msg1163192#msg1163192]nohponex[/url]. - -[hr] -[center][size=25pt][b]Κάλεσμα για contributors[/b][/size] - -[img height=200]https://tctechcrunch2011.files.wordpress.com/2015/04/uncle-sam-we-want-you1-kopie_1.png[/img][/center] - -Αν ενδιαφέρεσαι κι [b]ΕΣΥ[/b] να ασχοληθείς με το project μπορείς να το κάνεις με πολλούς τρόπους: -[list] - -[li] -Να αναφέρεις bugs, να προτείνεις βελτιώσεις και να συμμετέχεις σε συζητήσεις στον [url=https://discord.gg/PVRVjth]Discord server[/url] μας και στον Issue Tracker στο [url=trello.com]Trello[/url] (το link του είναι pinned στο #feedback στο Discord). -[/li] -[li] -Να κατεβάσεις και να δοκιμάσεις την alpha έκδοση της εφαρμογής από [url=https://play.google.com/apps/testing/gr.thmmy.mthmmy]εδώ[/url], [b]αφού [/b]πρώτα μας στείλεις το Gmail που έχεις στο Google Play για να αποκτήσεις πρόσβαση. -[/li] -[li] -Να έρθεις σε άμεση επικοινωνία με την ομάδα μέσω του [url=https://discord.gg/PVRVjth]Discord[/url] ή στέλνοντας email στο thmmynolife@gmail.com. -[/li] -[li]Αν ξέρεις προγραμματισμό μπορείς αρχικά να ζητήσεις πρόσβαση στο repository και να συνεισφέρεις κώδικα με fork και merge requests στους ρυθμούς σου. Χρειάζονται [i]άμεσα[/i] νέοι developers για υλοποιήση καινούργιων χαρακτηριστικών, διόρθωση εντόμων, σύνταξη των javadocs και του documentation γενικότερα, white-box testing, υλοποίηση του backend στο server που στήθηκε πρόσφατα και πολλών άλλων. -[/li][/list] -[hr] -[center][size=25pt][b]Η εφαρμογή[/b][/size][/center] - -[center][url=https://s6.postimg.org/v9mseb7n5/image.png][img width=200]https://s6.postimg.org/v9mseb7n5/image.png[/img][/url] [url=https://s6.postimg.org/3nk0tmoa9/image2.png][img width=200]https://s6.postimg.org/3nk0tmoa9/image2.png[/img][/url] [url=https://s6.postimg.org/i813ogj8x/image3.png][img width=200]https://s6.postimg.org/i813ogj8x/image3.png[/img][/url] [url=https://s6.postimg.org/4to0sfckx/image4.png][img width=200]https://s6.postimg.org/4to0sfckx/image4.png[/img][/url] [url=https://s6.postimg.org/69zjakfht/image5.png][img width=200]https://s6.postimg.org/69zjakfht/image5.png[/img][/url] [url=https://s6.postimg.org/rkx3etxm9/image6.png][img width=200]https://s6.postimg.org/rkx3etxm9/image6.png[/img][/url][/center] - -Αυτή τη στιγμή στην εφαρμογή μπορείς να κάνεις login και logout, να δεις τα "Πρόσφατα", να περιηγηθείς στα boards, topics και user profiles, να κατεβάσεις αρχεία από τα downloads και από συνημμένα σε post. \ No newline at end of file